~ubuntu-branches/debian/squeeze/freeciv/squeeze

« back to all changes in this revision

Viewing changes to client/gui-gtk-2.0/chatline.c

  • Committer: Bazaar Package Importer
  • Author(s): Clint Adams, Karl Goetz, Clint Adams
  • Date: 2010-02-23 22:09:02 UTC
  • mfrom: (1.2.13 upstream)
  • Revision ID: james.westby@ubuntu.com-20100223220902-kiyrmr9i4152cka5
Tags: 2.2.0-1
[ Karl Goetz ]
* Remove civserver files in /etc/ggzd/ (Closes: 523772, 517787)
* Adding ${misc:Depends} to all binary packages (lintian warnings)

[ Clint Adams ]
* New upstream version.
  - Drop data_dsc_use_bindir.diff (binary pathnames have changed).

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
 
23
23
#include <gdk/gdkkeysyms.h>
24
24
 
 
25
/* utility */
25
26
#include "fcintl.h"
 
27
#include "genlist.h"
 
28
#include "log.h"
26
29
#include "mem.h"
 
30
#include "support.h"
 
31
 
 
32
/* common */
 
33
#include "featured_text.h"
 
34
#include "game.h"
27
35
#include "packets.h"
28
 
#include "support.h"
29
36
 
30
 
#include "civclient.h"
 
37
/* client */
 
38
#include "client_main.h"
31
39
#include "climisc.h"
32
 
#include "clinet.h"
 
40
#include "control.h"
 
41
#include "mapview_common.h"
 
42
 
 
43
/* gui-gtk-2.0 */
 
44
#include "colors.h"
33
45
#include "gui_main.h"
34
46
#include "gui_stuff.h"
 
47
#include "pages.h"
35
48
 
36
49
#include "chatline.h"
37
 
#include "pages.h"
38
 
 
39
 
struct genlist *history_list;
40
 
int history_pos;
 
50
 
 
51
#define MAX_CHATLINE_HISTORY 20
 
52
 
 
53
static struct genlist *history_list = NULL;
 
54
static int history_pos = -1;
 
55
 
 
56
static struct inputline_toolkit {
 
57
  GtkWidget *main_widget;
 
58
  GtkWidget *entry;
 
59
  GtkWidget *button_box;
 
60
  GtkWidget *toolbar;
 
61
  GtkWidget *toggle_button;
 
62
  bool toolbar_displayed;
 
63
} toolkit;      /* Singleton. */
 
64
 
 
65
static void inputline_make_tag(GtkEntry *entry, enum text_tag_type type);
 
66
 
 
67
/**************************************************************************
 
68
  Returns TRUE iff the input line has focus.
 
69
**************************************************************************/
 
70
bool inputline_has_focus(void)
 
71
{
 
72
  return GTK_WIDGET_HAS_FOCUS(toolkit.entry);
 
73
}
 
74
 
 
75
/**************************************************************************
 
76
  Gives the focus to the intput line.
 
77
**************************************************************************/
 
78
void inputline_grab_focus(void)
 
79
{
 
80
  gtk_widget_grab_focus(toolkit.entry);
 
81
}
41
82
 
42
83
/**************************************************************************
43
84
  Helper function to determine if a given client input line is intended as
90
131
 
91
132
 
92
133
/**************************************************************************
93
 
...
 
134
  Called when the return key is pressed.
94
135
**************************************************************************/
95
 
void inputline_return(GtkEntry *w, gpointer data)
 
136
static void inputline_return(GtkEntry *w, gpointer data)
96
137
{
97
138
  const char *theinput;
98
139
 
99
140
  theinput = gtk_entry_get_text(w);
100
141
  
101
142
  if (*theinput) {
102
 
    if (client_state() == C_S_RUNNING && allied_chat_only
 
143
    if (client_state() == C_S_RUNNING && gui_gtk2_allied_chat_only
103
144
        && is_plain_public_message(theinput)) {
104
145
      char buf[MAX_LEN_MSG];
105
146
      my_snprintf(buf, sizeof(buf), ". %s", theinput);
124
165
}
125
166
 
126
167
/**************************************************************************
 
168
  Called when a key is pressed.
 
169
**************************************************************************/
 
170
static gboolean inputline_handler(GtkWidget *w, GdkEventKey *ev)
 
171
{
 
172
  if ((ev->state & GDK_CONTROL_MASK)) {
 
173
    /* Chatline featured text support. */
 
174
    switch (ev->keyval) {
 
175
    case GDK_b:
 
176
      inputline_make_tag(GTK_ENTRY(w), TTT_BOLD);
 
177
      return TRUE;
 
178
 
 
179
    case GDK_c:
 
180
      inputline_make_tag(GTK_ENTRY(w), TTT_COLOR);
 
181
      return TRUE;
 
182
 
 
183
    case GDK_i:
 
184
      inputline_make_tag(GTK_ENTRY(w), TTT_ITALIC);
 
185
      return TRUE;
 
186
 
 
187
    case GDK_s:
 
188
      inputline_make_tag(GTK_ENTRY(w), TTT_STRIKE);
 
189
      return TRUE;
 
190
 
 
191
    case GDK_u:
 
192
      inputline_make_tag(GTK_ENTRY(w), TTT_UNDERLINE);
 
193
      return TRUE;
 
194
 
 
195
    default:
 
196
      break;
 
197
    }
 
198
 
 
199
  } else {
 
200
    /* Chatline history controls. */
 
201
    switch (ev->keyval) {
 
202
    case GDK_Up:
 
203
      if (history_pos < genlist_size(history_list) - 1) {
 
204
        gtk_entry_set_text(GTK_ENTRY(w),
 
205
                           genlist_get(history_list, ++history_pos));
 
206
        gtk_editable_set_position(GTK_EDITABLE(w), -1);
 
207
      }
 
208
      return TRUE;
 
209
 
 
210
    case GDK_Down:
 
211
      if (history_pos >= 0) {
 
212
        history_pos--;
 
213
      }
 
214
 
 
215
      if (history_pos >= 0) {
 
216
        gtk_entry_set_text(GTK_ENTRY(w),
 
217
                           genlist_get(history_list, history_pos));
 
218
      } else {
 
219
        gtk_entry_set_text(GTK_ENTRY(w), "");
 
220
      }
 
221
      gtk_editable_set_position(GTK_EDITABLE(w), -1);
 
222
      return TRUE;
 
223
 
 
224
    default:
 
225
      break;
 
226
    }
 
227
  }
 
228
 
 
229
  return FALSE;
 
230
}
 
231
 
 
232
/**************************************************************************
 
233
  Make a text tag for the selected text.
 
234
**************************************************************************/
 
235
void inputline_make_tag(GtkEntry *entry, enum text_tag_type type)
 
236
{
 
237
  char buf[MAX_LEN_MSG];
 
238
  GtkEditable *editable = GTK_EDITABLE(entry);
 
239
  gint start_pos, end_pos;
 
240
  gchar *selection;
 
241
 
 
242
  if (!gtk_editable_get_selection_bounds(editable, &start_pos, &end_pos)) {
 
243
    /* Let's say the selection starts and ends at the current position. */
 
244
    start_pos = end_pos = gtk_editable_get_position(editable);
 
245
  }
 
246
 
 
247
  selection = gtk_editable_get_chars(editable, start_pos, end_pos);
 
248
 
 
249
  if (type == TTT_COLOR) {
 
250
    /* Get the color arguments. */
 
251
    char fg_color_text[32], bg_color_text[32];
 
252
    GdkColor *fg_color = g_object_get_data(G_OBJECT(entry), "fg_color");
 
253
    GdkColor *bg_color = g_object_get_data(G_OBJECT(entry), "bg_color");
 
254
 
 
255
    if (!fg_color && !bg_color) {
 
256
      goto CLEAN_UP;
 
257
    }
 
258
 
 
259
    color_to_string(fg_color, fg_color_text, sizeof(fg_color_text));
 
260
    color_to_string(bg_color, bg_color_text, sizeof(bg_color_text));
 
261
 
 
262
    if (0 == featured_text_apply_tag(selection, buf, sizeof(buf),
 
263
                                     TTT_COLOR, 0, OFFSET_UNSET,
 
264
                                     ft_color(fg_color_text,
 
265
                                              bg_color_text))) {
 
266
      goto CLEAN_UP;
 
267
    }
 
268
  } else if (0 == featured_text_apply_tag(selection, buf, sizeof(buf),
 
269
                                          type, 0, OFFSET_UNSET)) {
 
270
    goto CLEAN_UP;
 
271
  }
 
272
 
 
273
  /* Replace the selection. */
 
274
  gtk_editable_delete_text(editable, start_pos, end_pos);
 
275
  end_pos = start_pos;
 
276
  gtk_editable_insert_text(editable, buf, -1, &end_pos);
 
277
  gtk_editable_select_region(editable, start_pos, end_pos);
 
278
 
 
279
CLEAN_UP:
 
280
  g_free(selection);
 
281
}
 
282
 
 
283
/**************************************************************************
 
284
  Make a chat link at the current position or make the current selection
 
285
  clickable.
 
286
**************************************************************************/
 
287
void inputline_make_chat_link(struct tile *ptile, bool unit)
 
288
{
 
289
  char buf[MAX_LEN_MSG];
 
290
  GtkWidget *entry = toolkit.entry;
 
291
  GtkEditable *editable = GTK_EDITABLE(entry);
 
292
  gint start_pos, end_pos;
 
293
  gchar *chars;
 
294
  struct unit *punit;
 
295
 
 
296
  /* Get the target. */
 
297
  if (unit) {
 
298
    punit = find_visible_unit(ptile);
 
299
    if (!punit) {
 
300
      output_window_append(ftc_client, _("No visible unit on this tile."));
 
301
      return;
 
302
    }
 
303
  } else {
 
304
    punit = NULL;
 
305
  }
 
306
 
 
307
  if (gtk_editable_get_selection_bounds(editable, &start_pos, &end_pos)) {
 
308
    /* There is a selection, make it clickable. */
 
309
    gpointer target;
 
310
    enum text_link_type type;
 
311
 
 
312
    chars = gtk_editable_get_chars(editable, start_pos, end_pos);
 
313
    if (punit) {
 
314
      type = TLT_UNIT;
 
315
      target = punit;
 
316
    } else if (tile_city(ptile)) {
 
317
      type = TLT_CITY;
 
318
      target = tile_city(ptile);
 
319
    } else {
 
320
      type = TLT_TILE;
 
321
      target = ptile;
 
322
    }
 
323
 
 
324
    if (0 != featured_text_apply_tag(chars, buf, sizeof(buf), TTT_LINK,
 
325
                                     0, OFFSET_UNSET, type, target)) {
 
326
      /* Replace the selection. */
 
327
      gtk_editable_delete_text(editable, start_pos, end_pos);
 
328
      end_pos = start_pos;
 
329
      gtk_editable_insert_text(editable, buf, -1, &end_pos);
 
330
      gtk_widget_grab_focus(entry);
 
331
      gtk_editable_select_region(editable, start_pos, end_pos);
 
332
    }
 
333
  } else {
 
334
    /* Just insert the link at the current position. */
 
335
    start_pos = gtk_editable_get_position(editable);
 
336
    end_pos = start_pos;
 
337
    chars = gtk_editable_get_chars(editable, MAX(start_pos - 1, 0),
 
338
                                   start_pos + 1);
 
339
    if (punit) {
 
340
      sz_strlcpy(buf, unit_link(punit));
 
341
    } else if (tile_city(ptile)) {
 
342
      sz_strlcpy(buf, city_link(tile_city(ptile)));
 
343
    } else {
 
344
      sz_strlcpy(buf, tile_link(ptile));
 
345
    }
 
346
 
 
347
    if (start_pos > 0 && strlen(chars) > 0 && chars[0] != ' ') {
 
348
      /* Maybe insert an extra space. */
 
349
      gtk_editable_insert_text(editable, " ", 1, &end_pos);
 
350
    }
 
351
    gtk_editable_insert_text(editable, buf, -1, &end_pos);
 
352
    if (chars[start_pos > 0 ? 1 : 0] != '\0'
 
353
        && chars[start_pos > 0 ? 1 : 0] != ' ') {
 
354
      /* Maybe insert an extra space. */
 
355
      gtk_editable_insert_text(editable, " ", 1, &end_pos);
 
356
    }
 
357
    gtk_widget_grab_focus(entry);
 
358
    gtk_editable_set_position(editable, end_pos);
 
359
  }
 
360
 
 
361
  g_free(chars);
 
362
}
 
363
 
 
364
/**************************************************************************
127
365
  Scroll a textview so that the given mark is visible, but only if the
128
366
  scroll window containing the textview is very close to the bottom. The
129
367
  text mark 'scroll_target' should probably be the first character of the
155
393
}
156
394
 
157
395
/**************************************************************************
 
396
  Click a link.
 
397
**************************************************************************/
 
398
static gboolean event_after(GtkWidget *text_view, GdkEventButton *event)
 
399
{
 
400
  GtkTextIter start, end, iter;
 
401
  GtkTextBuffer *buffer;
 
402
  GSList *tags, *tagp;
 
403
  gint x, y;
 
404
  struct tile *ptile = NULL;
 
405
 
 
406
  if (event->type != GDK_BUTTON_RELEASE || event->button != 1) {
 
407
    return FALSE;
 
408
  }
 
409
 
 
410
  buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
 
411
 
 
412
  /* We shouldn't follow a link if the user has selected something. */
 
413
  gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
 
414
  if (gtk_text_iter_get_offset(&start) != gtk_text_iter_get_offset(&end)) {
 
415
    return FALSE;
 
416
  }
 
417
 
 
418
  gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW (text_view), 
 
419
                                        GTK_TEXT_WINDOW_WIDGET,
 
420
                                        event->x, event->y, &x, &y);
 
421
 
 
422
  gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(text_view), &iter, x, y);
 
423
 
 
424
  if ((tags = gtk_text_iter_get_tags(&iter))) {
 
425
    for (tagp = tags; tagp; tagp = tagp->next) {
 
426
      GtkTextTag *tag = tagp->data;
 
427
      enum text_link_type type =
 
428
        GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "type"));
 
429
 
 
430
      if (type != 0) {
 
431
        /* This is a link. */
 
432
        int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "id"));
 
433
        ptile = NULL;
 
434
 
 
435
        /* Real type is type - 1.
 
436
         * See comment in apply_text_tag() for g_object_set_data(). */
 
437
        type--;
 
438
 
 
439
        switch (type) {
 
440
        case TLT_CITY:
 
441
          {
 
442
            struct city *pcity = game_find_city_by_number(id);
 
443
 
 
444
            if (pcity) {
 
445
              ptile = client_city_tile(pcity);
 
446
            } else {
 
447
              output_window_append(ftc_client, _("This city isn't known!"));
 
448
            }
 
449
          }
 
450
          break;
 
451
        case TLT_TILE:
 
452
          ptile = index_to_tile(id);
 
453
 
 
454
          if (!ptile) {
 
455
            output_window_append(ftc_client,
 
456
                                 _("This tile doesn't exist in this game!"));
 
457
          }
 
458
          break;
 
459
        case TLT_UNIT:
 
460
          {
 
461
            struct unit *punit = game_find_unit_by_number(id);
 
462
 
 
463
            if (punit) {
 
464
              ptile = unit_tile(punit);
 
465
            } else {
 
466
              output_window_append(ftc_client, _("This unit isn't known!"));
 
467
            }
 
468
          }
 
469
          break;
 
470
        }
 
471
 
 
472
        if (ptile) {
 
473
          center_tile_mapcanvas(ptile);
 
474
          link_mark_restore(type, id);
 
475
          gtk_widget_grab_focus(GTK_WIDGET(map_canvas));
 
476
        }
 
477
      }
 
478
    }
 
479
    g_slist_free(tags);
 
480
  }
 
481
 
 
482
  return FALSE;
 
483
}
 
484
 
 
485
/**************************************************************************
 
486
  Set the "hand" cursor when moving over a link.
 
487
**************************************************************************/
 
488
static void set_cursor_if_appropriate(GtkTextView *text_view, gint x, gint y)
 
489
{
 
490
  static gboolean hovering_over_link = FALSE;
 
491
  static GdkCursor *hand_cursor = NULL;
 
492
  static GdkCursor *regular_cursor = NULL;
 
493
  GSList *tags, *tagp;
 
494
  GtkTextIter iter;
 
495
  gboolean hovering = FALSE;
 
496
 
 
497
  /* Initialize the cursors. */
 
498
  if (!hand_cursor) {
 
499
    hand_cursor = gdk_cursor_new_for_display(
 
500
        gdk_screen_get_display(gdk_screen_get_default()), GDK_HAND2);
 
501
  }
 
502
  if (!regular_cursor) {
 
503
    regular_cursor = gdk_cursor_new_for_display(
 
504
        gdk_screen_get_display(gdk_screen_get_default()), GDK_XTERM);
 
505
  }
 
506
 
 
507
  gtk_text_view_get_iter_at_location(text_view, &iter, x, y);
 
508
 
 
509
  tags = gtk_text_iter_get_tags(&iter);
 
510
  for (tagp = tags; tagp; tagp = tagp->next) {
 
511
    enum text_link_type type =
 
512
      GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tagp->data), "type"));
 
513
 
 
514
    if (type != 0) {
 
515
      hovering = TRUE;
 
516
      break;
 
517
    }
 
518
  }
 
519
 
 
520
  if (hovering != hovering_over_link) {
 
521
    hovering_over_link = hovering;
 
522
 
 
523
    if (hovering_over_link) {
 
524
      gdk_window_set_cursor(gtk_text_view_get_window(text_view,
 
525
                                                     GTK_TEXT_WINDOW_TEXT),
 
526
                            hand_cursor);
 
527
    } else {
 
528
      gdk_window_set_cursor(gtk_text_view_get_window(text_view,
 
529
                                                     GTK_TEXT_WINDOW_TEXT),
 
530
                            regular_cursor);
 
531
    }
 
532
  }
 
533
 
 
534
  if (tags) {
 
535
    g_slist_free(tags);
 
536
  }
 
537
}
 
538
 
 
539
/**************************************************************************
 
540
  Maybe are the mouse is moving over a link.
 
541
**************************************************************************/
 
542
static gboolean motion_notify_event(GtkWidget *text_view,
 
543
                                    GdkEventMotion *event)
 
544
{
 
545
  gint x, y;
 
546
 
 
547
  gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text_view), 
 
548
                                        GTK_TEXT_WINDOW_WIDGET,
 
549
                                        event->x, event->y, &x, &y);
 
550
  set_cursor_if_appropriate(GTK_TEXT_VIEW(text_view), x, y);
 
551
  gdk_window_get_pointer(text_view->window, NULL, NULL, NULL);
 
552
 
 
553
  return FALSE;
 
554
}
 
555
 
 
556
/**************************************************************************
 
557
  Maybe are the mouse is moving over a link.
 
558
**************************************************************************/
 
559
static gboolean visibility_notify_event(GtkWidget *text_view,
 
560
                                        GdkEventVisibility *event)
 
561
{
 
562
  gint wx, wy, bx, by;
 
563
 
 
564
  gdk_window_get_pointer(text_view->window, &wx, &wy, NULL);
 
565
  gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW (text_view), 
 
566
                                        GTK_TEXT_WINDOW_WIDGET,
 
567
                                        wx, wy, &bx, &by);
 
568
  set_cursor_if_appropriate(GTK_TEXT_VIEW(text_view), bx, by);
 
569
 
 
570
  return FALSE;
 
571
}
 
572
 
 
573
/**************************************************************************
 
574
  Set the appropriate callbacks for the message buffer.
 
575
**************************************************************************/
 
576
void set_message_buffer_view_link_handlers(GtkWidget *view)
 
577
{
 
578
  g_signal_connect(view, "event-after",
 
579
                   G_CALLBACK(event_after), NULL);
 
580
  g_signal_connect(view, "motion-notify-event",
 
581
                   G_CALLBACK(motion_notify_event), NULL);
 
582
  g_signal_connect(view, "visibility-notify-event",
 
583
                   G_CALLBACK(visibility_notify_event), NULL);
 
584
 
 
585
}
 
586
 
 
587
/**************************************************************************
 
588
  Convert a struct text_tag to a GtkTextTag.
 
589
**************************************************************************/
 
590
static void apply_text_tag(const struct text_tag *ptag, GtkTextBuffer *buf,
 
591
                           offset_t text_start_offset, const char *text)
 
592
{
 
593
  static bool initalized = FALSE;
 
594
  GtkTextIter start, stop;
 
595
 
 
596
  if (!initalized) {
 
597
    gtk_text_buffer_create_tag(buf, "bold",
 
598
                               "weight", PANGO_WEIGHT_BOLD, NULL);
 
599
    gtk_text_buffer_create_tag(buf, "italic",
 
600
                               "style", PANGO_STYLE_ITALIC, NULL);
 
601
    gtk_text_buffer_create_tag(buf, "strike",
 
602
                               "strikethrough", TRUE, NULL);
 
603
    gtk_text_buffer_create_tag(buf, "underline",
 
604
                               "underline", PANGO_UNDERLINE_SINGLE, NULL);
 
605
    initalized = TRUE;
 
606
  }
 
607
 
 
608
  /* Get the position. */
 
609
  /*
 
610
   * N.B.: text_tag_*_offset() value is in bytes, so we need to convert it
 
611
   * to utf8 character offset.
 
612
   */
 
613
  gtk_text_buffer_get_iter_at_offset(buf, &start, text_start_offset
 
614
                                     + g_utf8_pointer_to_offset(text,
 
615
                                     text + text_tag_start_offset(ptag)));
 
616
  if (text_tag_stop_offset(ptag) == OFFSET_UNSET) {
 
617
    gtk_text_buffer_get_end_iter(buf, &stop);
 
618
  } else {
 
619
    gtk_text_buffer_get_iter_at_offset(buf, &stop, text_start_offset
 
620
                                       + g_utf8_pointer_to_offset(text,
 
621
                                       text + text_tag_stop_offset(ptag)));
 
622
  }
 
623
 
 
624
  switch (text_tag_type(ptag)) {
 
625
  case TTT_BOLD:
 
626
    gtk_text_buffer_apply_tag_by_name(buf, "bold", &start, &stop);
 
627
    break;
 
628
  case TTT_ITALIC:
 
629
    gtk_text_buffer_apply_tag_by_name(buf, "italic", &start, &stop);
 
630
    break;
 
631
  case TTT_STRIKE:
 
632
    gtk_text_buffer_apply_tag_by_name(buf, "strike", &start, &stop);
 
633
    break;
 
634
  case TTT_UNDERLINE:
 
635
    gtk_text_buffer_apply_tag_by_name(buf, "underline", &start, &stop);
 
636
    break;
 
637
  case TTT_COLOR:
 
638
    {
 
639
      /* We have to make a new tag every time. */
 
640
      GtkTextTag *tag = NULL;
 
641
      GdkColor foreground;
 
642
      GdkColor background;
 
643
 
 
644
      if (gdk_color_parse(text_tag_color_foreground(ptag), &foreground)) {
 
645
        if (gdk_color_parse(text_tag_color_background(ptag),
 
646
                            &background)) {
 
647
          tag = gtk_text_buffer_create_tag(buf, NULL,
 
648
                                           "foreground-gdk", &foreground,
 
649
                                           "background-gdk", &background,
 
650
                                           NULL);
 
651
        } else {
 
652
          tag = gtk_text_buffer_create_tag(buf, NULL,
 
653
                                           "foreground-gdk", &foreground,
 
654
                                           NULL);
 
655
        }
 
656
      } else if (gdk_color_parse(text_tag_color_background(ptag),
 
657
                                 &background)) {
 
658
        tag = gtk_text_buffer_create_tag(buf, NULL,
 
659
                                         "background-gdk", &background,
 
660
                                         NULL);
 
661
      }
 
662
 
 
663
      if (!tag) {
 
664
        break; /* No color. */
 
665
      }
 
666
      gtk_text_buffer_apply_tag(buf, tag, &start, &stop);
 
667
      g_object_unref(G_OBJECT(tag));
 
668
    }
 
669
    break;
 
670
  case TTT_LINK:
 
671
    {
 
672
      struct color *pcolor = NULL;
 
673
      GtkTextTag *tag;
 
674
 
 
675
      switch (text_tag_link_type(ptag)) {
 
676
      case TLT_CITY:
 
677
        pcolor = get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
 
678
        break;
 
679
      case TLT_TILE:
 
680
        pcolor = get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
 
681
        break;
 
682
      case TLT_UNIT:
 
683
        pcolor = get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
 
684
        break;
 
685
      }
 
686
 
 
687
      if (!pcolor) {
 
688
        break; /* Not a valid link type case. */
 
689
      }
 
690
 
 
691
      tag = gtk_text_buffer_create_tag(buf, NULL, 
 
692
                                       "foreground-gdk", &pcolor->color, 
 
693
                                       "underline", PANGO_UNDERLINE_SINGLE,
 
694
                                       NULL);
 
695
 
 
696
      /* Type 0 is reserved for non-link tags.  So, add 1 to the
 
697
       * type value. */
 
698
      g_object_set_data(G_OBJECT(tag), "type",
 
699
                        GINT_TO_POINTER(text_tag_link_type(ptag) + 1));
 
700
      g_object_set_data(G_OBJECT(tag), "id",
 
701
                        GINT_TO_POINTER(text_tag_link_id(ptag)));
 
702
      gtk_text_buffer_apply_tag(buf, tag, &start, &stop);
 
703
      g_object_unref(G_OBJECT(tag));
 
704
      break;
 
705
    }
 
706
  }
 
707
}
 
708
 
 
709
/**************************************************************************
158
710
  Appends the string to the chat output window.  The string should be
159
711
  inserted on its own line, although it will have no newline.
160
712
**************************************************************************/
161
 
void real_append_output_window(const char *astring, int conn_id)
 
713
void real_output_window_append(const char *astring,
 
714
                               const struct text_tag_list *tags,
 
715
                               int conn_id)
162
716
{
163
717
  GtkTextBuffer *buf;
164
718
  GtkTextIter iter;
165
719
  GtkTextMark *mark;
 
720
  offset_t text_start_offset;
166
721
 
167
722
  buf = message_buffer;
168
723
  gtk_text_buffer_get_end_iter(buf, &iter);
169
724
  gtk_text_buffer_insert(buf, &iter, "\n", -1);
170
725
  mark = gtk_text_buffer_create_mark(buf, NULL, &iter, TRUE);
171
726
 
172
 
  if (show_chat_message_time) {
 
727
  if (gui_gtk2_show_chat_message_time) {
173
728
    char timebuf[64];
174
729
    time_t now;
175
730
    struct tm *now_tm;
180
735
    gtk_text_buffer_insert(buf, &iter, timebuf, -1);
181
736
  }
182
737
 
 
738
  text_start_offset = gtk_text_iter_get_offset(&iter);
183
739
  gtk_text_buffer_insert(buf, &iter, astring, -1);
 
740
  text_tag_list_iterate(tags, ptag) {
 
741
    apply_text_tag(ptag, buf, text_start_offset, astring);
 
742
  } text_tag_list_iterate_end;
184
743
 
185
744
  if (main_message_area) {
186
745
    scroll_if_necessary(GTK_TEXT_VIEW(main_message_area), mark);
196
755
/**************************************************************************
197
756
 I have no idea what module this belongs in -- Syela
198
757
 I've decided to put output_window routines in chatline.c, because
199
 
 the are somewhat related and append_output_window is already here.  --dwp
 
758
 the are somewhat related and output_window_* is already here.  --dwp
200
759
**************************************************************************/
201
760
void log_output_window(void)
202
761
{
227
786
}
228
787
 
229
788
/**************************************************************************
230
 
  Scrolls the pregame and in-game chat windows all the way to the bottom.
231
 
**************************************************************************/
232
 
void chatline_scroll_to_bottom(void)
233
 
{
234
 
  GtkTextIter end;
235
 
 
236
 
  if (!message_buffer) {
237
 
    return;
238
 
  }
239
 
  gtk_text_buffer_get_end_iter(message_buffer, &end);
240
 
 
241
 
  if (main_message_area) {
242
 
    gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(main_message_area),
243
 
                                 &end, 0.0, TRUE, 1.0, 0.0);
244
 
  }
245
 
  if (start_message_area) {
246
 
    gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(start_message_area),
247
 
                                 &end, 0.0, TRUE, 1.0, 0.0);
248
 
  }
 
789
  Returns whether the chatline is scrolled to the bottom.
 
790
**************************************************************************/
 
791
bool chatline_is_scrolled_to_bottom(void)
 
792
{
 
793
  GtkWidget *sw, *w;
 
794
  GtkAdjustment *vadj;
 
795
  gdouble val, max, upper, page_size;
 
796
 
 
797
  if (get_client_page() == PAGE_GAME) {
 
798
    w = GTK_WIDGET(main_message_area);
 
799
  } else {
 
800
    w = GTK_WIDGET(start_message_area);
 
801
  }
 
802
 
 
803
  if (w == NULL) {
 
804
    return TRUE;
 
805
  }
 
806
 
 
807
  sw = gtk_widget_get_parent(w);
 
808
  vadj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(sw));
 
809
  val = gtk_adjustment_get_value(GTK_ADJUSTMENT(vadj));
 
810
  g_object_get(G_OBJECT(vadj), "upper", &upper,
 
811
               "page-size", &page_size, NULL);
 
812
  max = upper - page_size;
 
813
 
 
814
  /* Approximation. */
 
815
  return max - val < 0.00000001;
 
816
}
 
817
 
 
818
/**************************************************************************
 
819
  Scrolls the pregame and in-game chat windows all the way to the bottom.
 
820
 
 
821
  Why do we do it in such a convuluted fasion rather than calling
 
822
  chatline_scroll_to_bottom directly from toplevel_configure?
 
823
  Because the widget is not at its final size yet when the configure
 
824
  event occurs.
 
825
**************************************************************************/
 
826
static gboolean chatline_scroll_callback(gpointer data)
 
827
{
 
828
  chatline_scroll_to_bottom(FALSE);     /* Not delayed this time! */
 
829
 
 
830
  *((guint *) data) = 0;
 
831
  return FALSE;         /* Remove this idle function. */
 
832
}
 
833
 
 
834
/**************************************************************************
 
835
  Scrolls the pregame and in-game chat windows all the way to the bottom.
 
836
  If delayed is TRUE, it will be done in a idle_callback.
 
837
**************************************************************************/
 
838
void chatline_scroll_to_bottom(bool delayed)
 
839
{
 
840
  static guint callback_id = 0;
 
841
 
 
842
  if (delayed) {
 
843
    if (callback_id == 0) {
 
844
      callback_id = g_idle_add(chatline_scroll_callback, &callback_id);
 
845
    }
 
846
  } else if (message_buffer) {
 
847
    GtkTextIter end;
 
848
 
 
849
    gtk_text_buffer_get_end_iter(message_buffer, &end);
 
850
 
 
851
    if (main_message_area) {
 
852
      gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(main_message_area),
 
853
                                   &end, 0.0, TRUE, 1.0, 0.0);
 
854
    }
 
855
    if (start_message_area) {
 
856
      gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(start_message_area),
 
857
                                   &end, 0.0, TRUE, 1.0, 0.0);
 
858
    }
 
859
  }
 
860
}
 
861
 
 
862
/**************************************************************************
 
863
  Tool button clicked.
 
864
**************************************************************************/
 
865
static void make_tag_callback(GtkToolButton *button, gpointer data)
 
866
{
 
867
  inputline_make_tag(GTK_ENTRY(data),
 
868
                     GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),
 
869
                                                       "text_tag_type")));
 
870
}
 
871
 
 
872
/**************************************************************************
 
873
  Set the color for an object.  Update the button if not NULL.
 
874
**************************************************************************/
 
875
static void color_set(GObject *object, const gchar *color_target,
 
876
                      GdkColor *color, GtkToolButton *button)
 
877
{
 
878
  GdkColor *current_color = g_object_get_data(object, color_target);
 
879
  GdkColormap *colormap = gdk_colormap_get_system();
 
880
 
 
881
  if (NULL == color) {
 
882
    /* Clears the current color. */
 
883
    if (NULL != current_color) {
 
884
      gdk_colormap_free_colors(colormap, current_color, 1);
 
885
      gdk_color_free(current_color);
 
886
      g_object_set_data(object, color_target, NULL);
 
887
      if (NULL != button) {
 
888
        gtk_tool_button_set_icon_widget(button, NULL);
 
889
      }
 
890
    }
 
891
  } else {
 
892
    /* Apply the new color. */
 
893
    if (NULL != current_color) {
 
894
      /* We already have a GdkColor pointer. */
 
895
      gdk_colormap_free_colors(colormap, current_color, 1);
 
896
      *current_color = *color;
 
897
    } else {
 
898
      /* We need to make a GdkColor pointer. */
 
899
      current_color = gdk_color_copy(color);
 
900
      g_object_set_data(object, color_target, current_color);
 
901
    }
 
902
 
 
903
    gdk_colormap_alloc_color(colormap, current_color, TRUE, TRUE);
 
904
    if (NULL != button) {
 
905
      /* Update the button. */
 
906
      GdkPixmap *pixmap;
 
907
      GtkWidget *image;
 
908
 
 
909
      pixmap = gdk_pixmap_new(root_window, 16, 16, -1);
 
910
      gdk_gc_set_foreground(fill_bg_gc, current_color);
 
911
      gdk_draw_rectangle(pixmap, fill_bg_gc, TRUE, 0, 0, 16, 16);
 
912
      image = gtk_pixmap_new(pixmap, NULL);
 
913
      gtk_tool_button_set_icon_widget(button, image);
 
914
      gtk_widget_show(image);
 
915
      g_object_unref(G_OBJECT(pixmap));
 
916
    }
 
917
  }
 
918
}
 
919
 
 
920
/**************************************************************************
 
921
  Color selection dialog response.
 
922
**************************************************************************/
 
923
static void color_selected(GtkDialog *dialog, gint res, gpointer data)
 
924
{
 
925
  GtkToolButton *button =
 
926
    GTK_TOOL_BUTTON(g_object_get_data(G_OBJECT(dialog), "button"));
 
927
  const gchar *color_target =
 
928
    g_object_get_data(G_OBJECT(button), "color_target");
 
929
 
 
930
  if (res == GTK_RESPONSE_REJECT) {
 
931
    /* Clears the current color. */
 
932
    color_set(G_OBJECT(data), color_target, NULL, button);
 
933
  } else if (res == GTK_RESPONSE_OK) {
 
934
    /* Apply the new color. */
 
935
    GtkColorSelection *selection =
 
936
      GTK_COLOR_SELECTION(g_object_get_data(G_OBJECT(dialog), "selection"));
 
937
    GdkColor new_color;
 
938
 
 
939
    gtk_color_selection_get_current_color(selection, &new_color);
 
940
    color_set(G_OBJECT(data), color_target, &new_color, button);
 
941
  }
 
942
 
 
943
  gtk_widget_destroy(GTK_WIDGET(dialog));
 
944
}
 
945
 
 
946
/**************************************************************************
 
947
  Color selection tool button clicked.
 
948
**************************************************************************/
 
949
static void select_color_callback(GtkToolButton *button, gpointer data)
 
950
{
 
951
  char buf[64];
 
952
  GtkWidget *dialog, *selection;
 
953
  /* "fg_color" or "bg_color". */
 
954
  const gchar *color_target = g_object_get_data(G_OBJECT(button),
 
955
                                                "color_target");
 
956
  GdkColor *current_color = g_object_get_data(G_OBJECT(data), color_target);
 
957
 
 
958
  /* TRANS: "text" or "background". */
 
959
  my_snprintf(buf, sizeof(buf), _("Select the %s color"),
 
960
              (const char *) g_object_get_data(G_OBJECT(button),
 
961
                                               "color_info"));
 
962
  dialog = gtk_dialog_new_with_buttons(buf, NULL, GTK_DIALOG_MODAL,
 
963
                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
964
                                       GTK_STOCK_CLEAR, GTK_RESPONSE_REJECT,
 
965
                                       GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
 
966
  setup_dialog(dialog, toplevel);
 
967
  g_object_set_data(G_OBJECT(dialog), "button", button);
 
968
  g_signal_connect(dialog, "response", G_CALLBACK(color_selected), data);
 
969
 
 
970
  selection = gtk_color_selection_new();
 
971
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), selection,
 
972
                     FALSE, FALSE, 0);
 
973
  g_object_set_data(G_OBJECT(dialog), "selection", selection);
 
974
  if (current_color) {
 
975
    gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(selection),
 
976
                                          current_color);
 
977
  }
 
978
 
 
979
  gtk_widget_show_all(dialog);
 
980
}
 
981
 
 
982
/**************************************************************************
 
983
  Moves the tool kit to the toolkit view.
 
984
**************************************************************************/
 
985
static gboolean move_toolkit(GtkWidget *toolkit_view, GdkEventExpose *event,
 
986
                             gpointer data)
 
987
{
 
988
  struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
 
989
  GtkWidget *parent = gtk_widget_get_parent(ptoolkit->main_widget);
 
990
  GtkWidget *button_box = GTK_WIDGET(g_object_get_data(G_OBJECT(toolkit_view),
 
991
                                                       "button_box"));
 
992
  GList *list, *iter;
 
993
 
 
994
  if (parent) {
 
995
    if (parent == toolkit_view) {
 
996
      return FALSE;     /* Already owned. */
 
997
    }
 
998
 
 
999
    /* N.B.: We need to hide/show the toolbar to reset the sensitivity
 
1000
     * of the tool buttons. */
 
1001
    if (ptoolkit->toolbar_displayed) {
 
1002
      gtk_widget_hide(ptoolkit->toolbar);
 
1003
    }
 
1004
    gtk_widget_reparent(ptoolkit->main_widget, toolkit_view);
 
1005
    if (ptoolkit->toolbar_displayed) {
 
1006
      gtk_widget_show(ptoolkit->toolbar);
 
1007
    }
 
1008
 
 
1009
    if (!gtk_widget_get_parent(button_box)) {
 
1010
      /* Attach to the toolkit button_box. */
 
1011
      gtk_box_pack_end(GTK_BOX(ptoolkit->button_box), button_box,
 
1012
                       FALSE, FALSE, 0);
 
1013
    }
 
1014
    gtk_widget_show_all(ptoolkit->main_widget);
 
1015
 
 
1016
    /* Hide all other buttons boxes. */
 
1017
    list = gtk_container_get_children(GTK_CONTAINER(ptoolkit->button_box));
 
1018
    for (iter = list; iter != NULL; iter = g_list_next(iter)) {
 
1019
      GtkWidget *widget = GTK_WIDGET(iter->data);
 
1020
 
 
1021
      if (widget != button_box) {
 
1022
        gtk_widget_hide(widget);
 
1023
      }
 
1024
    }
 
1025
    g_list_free(list);
 
1026
 
 
1027
  } else {
 
1028
    /* First time attached to a parent. */
 
1029
    gtk_box_pack_start(GTK_BOX(toolkit_view), ptoolkit->main_widget,
 
1030
                       TRUE, TRUE, 0);
 
1031
    gtk_box_pack_end(GTK_BOX(ptoolkit->button_box), button_box,
 
1032
                     FALSE, FALSE, 0);
 
1033
    gtk_widget_show_all(ptoolkit->main_widget);
 
1034
  }
 
1035
 
 
1036
  return FALSE;
 
1037
}
 
1038
 
 
1039
/**************************************************************************
 
1040
  Show/Hide the toolbar.
 
1041
**************************************************************************/
 
1042
static gboolean set_toolbar_visibility(GtkWidget *w,
 
1043
                                       GdkEventExpose *event,
 
1044
                                       gpointer data)
 
1045
{
 
1046
  struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
 
1047
  GtkToggleButton *button = GTK_TOGGLE_BUTTON(toolkit.toggle_button);
 
1048
 
 
1049
  if (ptoolkit->toolbar_displayed) {
 
1050
    if (!gtk_toggle_button_get_active(button)) {
 
1051
      /* button_toggled() will be called and the toolbar shown. */
 
1052
      gtk_toggle_button_set_active(button, TRUE);
 
1053
    } else {
 
1054
      /* Unsure the widget is visible. */
 
1055
      gtk_widget_show(ptoolkit->toolbar);
 
1056
    }
 
1057
  } else {
 
1058
    if (gtk_toggle_button_get_active(button)) {
 
1059
      /* button_toggled() will be called and the toolbar hiden. */
 
1060
      gtk_toggle_button_set_active(button, FALSE);
 
1061
    } else {
 
1062
      /* Unsure the widget is visible. */
 
1063
      gtk_widget_hide(ptoolkit->toolbar);
 
1064
    }
 
1065
  }
 
1066
 
 
1067
  return FALSE;
 
1068
}
 
1069
 
 
1070
/**************************************************************************
 
1071
  Show/Hide the toolbar.
 
1072
**************************************************************************/
 
1073
static void button_toggled(GtkToggleButton *button, gpointer data)
 
1074
{
 
1075
  struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
 
1076
 
 
1077
  if (gtk_toggle_button_get_active(button)) {
 
1078
    gtk_widget_show(ptoolkit->toolbar);
 
1079
    ptoolkit->toolbar_displayed = TRUE;
 
1080
    if (chatline_is_scrolled_to_bottom()) {
 
1081
      /* Make sure to be still at the end. */
 
1082
      chatline_scroll_to_bottom(TRUE);
 
1083
    }
 
1084
  } else {
 
1085
    gtk_widget_hide(ptoolkit->toolbar);
 
1086
    ptoolkit->toolbar_displayed = FALSE;
 
1087
  }
 
1088
}
 
1089
 
 
1090
/**************************************************************************
 
1091
  Returns a new inputline toolkit view widget that can contain the
 
1092
  inputline.
 
1093
 
 
1094
  This widget has the following datas:
 
1095
  "button_box": pointer to the GtkHBox where to append buttons.
 
1096
**************************************************************************/
 
1097
GtkWidget *inputline_toolkit_view_new(void)
 
1098
{
 
1099
  GtkWidget *toolkit_view, *bbox;
 
1100
 
 
1101
  /* Main widget. */
 
1102
  toolkit_view = gtk_vbox_new(FALSE, 0);
 
1103
  g_signal_connect(toolkit_view, "expose-event",
 
1104
                   G_CALLBACK(move_toolkit), &toolkit);
 
1105
 
 
1106
  /* Button box. */
 
1107
  bbox = gtk_hbox_new(FALSE, 12);
 
1108
  g_object_set_data(G_OBJECT(toolkit_view), "button_box", bbox);
 
1109
 
 
1110
  return toolkit_view;
 
1111
}
 
1112
 
 
1113
/**************************************************************************
 
1114
  Appends a button to the inputline toolkit view widget.
 
1115
**************************************************************************/
 
1116
void inputline_toolkit_view_append_button(GtkWidget *toolkit_view,
 
1117
                                          GtkWidget *button)
 
1118
{
 
1119
  gtk_box_pack_start(GTK_BOX(g_object_get_data(G_OBJECT(toolkit_view),
 
1120
                     "button_box")), button, FALSE, FALSE, 0);
 
1121
}
 
1122
 
 
1123
/**************************************************************************
 
1124
  Initializes the chatline stuff.
 
1125
**************************************************************************/
 
1126
void chatline_init(void)
 
1127
{
 
1128
  GtkWidget *vbox, *toolbar, *hbox, *button, *entry, *bbox;
 
1129
  GtkToolItem *item;
 
1130
  GtkTooltips *tooltips;
 
1131
  GdkColor color;
 
1132
 
 
1133
  /* Chatline history. */
 
1134
  if (!history_list) {
 
1135
    history_list = genlist_new();
 
1136
    history_pos = -1;
 
1137
  }
 
1138
 
 
1139
  /* Inputline toolkit. */
 
1140
  memset(&toolkit, 0, sizeof(toolkit));
 
1141
 
 
1142
  tooltips = gtk_tooltips_new();
 
1143
  gtk_tooltips_enable(tooltips);
 
1144
 
 
1145
  vbox = gtk_vbox_new(FALSE, 2);
 
1146
  toolkit.main_widget = vbox;
 
1147
  g_signal_connect(vbox, "expose-event",
 
1148
                   G_CALLBACK(set_toolbar_visibility), &toolkit);
 
1149
 
 
1150
  entry = gtk_entry_new();
 
1151
  toolkit.entry = entry;
 
1152
 
 
1153
  /* First line: toolbar */
 
1154
  toolbar = gtk_toolbar_new();
 
1155
  gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
 
1156
  gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_MENU);
 
1157
  gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), FALSE);
 
1158
  gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
 
1159
  gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar),
 
1160
                              GTK_ORIENTATION_HORIZONTAL);
 
1161
  toolkit.toolbar = toolbar;
 
1162
 
 
1163
  /* Bold button. */
 
1164
  item = gtk_tool_button_new_from_stock(GTK_STOCK_BOLD);
 
1165
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1166
  g_object_set_data(G_OBJECT(item), "text_tag_type",
 
1167
                    GINT_TO_POINTER(TTT_BOLD));
 
1168
  g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry);
 
1169
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1170
                       _("Bold (Ctrl-B)"), NULL);
 
1171
 
 
1172
  /* Italic button. */
 
1173
  item = gtk_tool_button_new_from_stock(GTK_STOCK_ITALIC);
 
1174
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1175
  g_object_set_data(G_OBJECT(item), "text_tag_type",
 
1176
                    GINT_TO_POINTER(TTT_ITALIC));
 
1177
  g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry);
 
1178
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1179
                       _("Italic (Ctrl-I)"), NULL);
 
1180
 
 
1181
  /* Strike button. */
 
1182
  item = gtk_tool_button_new_from_stock(GTK_STOCK_STRIKETHROUGH);
 
1183
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1184
  g_object_set_data(G_OBJECT(item), "text_tag_type",
 
1185
                    GINT_TO_POINTER(TTT_STRIKE));
 
1186
  g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry);
 
1187
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1188
                       _("Strikethrough (Ctrl-S)"), NULL);
 
1189
 
 
1190
  /* Underline button. */
 
1191
  item = gtk_tool_button_new_from_stock(GTK_STOCK_UNDERLINE);
 
1192
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1193
  g_object_set_data(G_OBJECT(item), "text_tag_type",
 
1194
                    GINT_TO_POINTER(TTT_UNDERLINE));
 
1195
  g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry);
 
1196
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1197
                       _("Underline (Ctrl-U)"), NULL);
 
1198
 
 
1199
  /* Color button. */
 
1200
  item = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_COLOR);
 
1201
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1202
  g_object_set_data(G_OBJECT(item), "text_tag_type",
 
1203
                    GINT_TO_POINTER(TTT_COLOR));
 
1204
  g_signal_connect(item, "clicked", G_CALLBACK(make_tag_callback), entry);
 
1205
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1206
                       _("Color (Ctrl-C)"), NULL);
 
1207
 
 
1208
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar),
 
1209
                     gtk_separator_tool_item_new(), -1);
 
1210
 
 
1211
  /* Foreground selector. */
 
1212
  item = gtk_tool_button_new(NULL, "");
 
1213
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1214
  g_object_set_data(G_OBJECT(item), "color_target", mystrdup("fg_color"));
 
1215
  g_object_set_data(G_OBJECT(item), "color_info", mystrdup(_("foreground")));
 
1216
  g_signal_connect(item, "clicked",
 
1217
                   G_CALLBACK(select_color_callback), entry);
 
1218
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1219
                       _("Select the text color"), NULL);
 
1220
  if (gdk_color_parse("#000000", &color)) {
 
1221
    /* Set default foreground color. */
 
1222
    color_set(G_OBJECT(entry), "fg_color", &color, GTK_TOOL_BUTTON(item));
 
1223
  } else {
 
1224
    freelog(LOG_ERROR, "Failed to set the default foreground color.");
 
1225
  }
 
1226
 
 
1227
  /* Background selector. */
 
1228
  item = gtk_tool_button_new(NULL, "");
 
1229
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1230
  g_object_set_data(G_OBJECT(item), "color_target", mystrdup("bg_color"));
 
1231
  g_object_set_data(G_OBJECT(item), "color_info", mystrdup(_("background")));
 
1232
  g_signal_connect(item, "clicked",
 
1233
                   G_CALLBACK(select_color_callback), entry);
 
1234
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1235
                       _("Select the background color"), NULL);
 
1236
  if (gdk_color_parse("#ffffff", &color)) {
 
1237
    /* Set default background color. */
 
1238
    color_set(G_OBJECT(entry), "bg_color", &color, GTK_TOOL_BUTTON(item));
 
1239
  } else {
 
1240
    freelog(LOG_ERROR, "Failed to set the default background color.");
 
1241
  }
 
1242
 
 
1243
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar),
 
1244
                     gtk_separator_tool_item_new(), -1);
 
1245
 
 
1246
  /* Return button. */
 
1247
  item = gtk_tool_button_new_from_stock(GTK_STOCK_OK);
 
1248
  gtk_toolbar_insert(GTK_TOOLBAR(toolbar), item, -1);
 
1249
  g_signal_connect_swapped(item, "clicked",
 
1250
                           G_CALLBACK(inputline_return), entry);
 
1251
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(item),
 
1252
                       /* TRANS: "Return" means the return key. */
 
1253
                       _("Send the chat (Return)"), NULL);
 
1254
 
 
1255
  /* Second line */
 
1256
  hbox = gtk_hbox_new(FALSE, 4);
 
1257
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
 
1258
 
 
1259
  /* Toggle button. */
 
1260
  button = gtk_toggle_button_new();
 
1261
  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 2);
 
1262
  gtk_container_add(GTK_CONTAINER(button),
 
1263
                    gtk_image_new_from_stock(GTK_STOCK_EDIT,
 
1264
                                             GTK_ICON_SIZE_MENU));
 
1265
  g_signal_connect(button, "toggled", G_CALLBACK(button_toggled), &toolkit);
 
1266
  gtk_tooltips_set_tip(tooltips, GTK_WIDGET(button), _("Chat tools"), NULL);
 
1267
  toolkit.toggle_button = button;
 
1268
 
 
1269
  /* Entry. */
 
1270
  gtk_box_pack_start(GTK_BOX(hbox), entry, TRUE, TRUE, 2);
 
1271
  g_signal_connect(entry, "activate", G_CALLBACK(inputline_return), NULL);
 
1272
  g_signal_connect(entry, "key_press_event",
 
1273
                   G_CALLBACK(inputline_handler), NULL);
 
1274
 
 
1275
  /* Button box. */
 
1276
  bbox = gtk_hbox_new(FALSE, 0);
 
1277
  gtk_box_pack_end(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
 
1278
  toolkit.button_box = bbox;
249
1279
}