126
167
/**************************************************************************
168
Called when a key is pressed.
169
**************************************************************************/
170
static gboolean inputline_handler(GtkWidget *w, GdkEventKey *ev)
172
if ((ev->state & GDK_CONTROL_MASK)) {
173
/* Chatline featured text support. */
174
switch (ev->keyval) {
176
inputline_make_tag(GTK_ENTRY(w), TTT_BOLD);
180
inputline_make_tag(GTK_ENTRY(w), TTT_COLOR);
184
inputline_make_tag(GTK_ENTRY(w), TTT_ITALIC);
188
inputline_make_tag(GTK_ENTRY(w), TTT_STRIKE);
192
inputline_make_tag(GTK_ENTRY(w), TTT_UNDERLINE);
200
/* Chatline history controls. */
201
switch (ev->keyval) {
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);
211
if (history_pos >= 0) {
215
if (history_pos >= 0) {
216
gtk_entry_set_text(GTK_ENTRY(w),
217
genlist_get(history_list, history_pos));
219
gtk_entry_set_text(GTK_ENTRY(w), "");
221
gtk_editable_set_position(GTK_EDITABLE(w), -1);
232
/**************************************************************************
233
Make a text tag for the selected text.
234
**************************************************************************/
235
void inputline_make_tag(GtkEntry *entry, enum text_tag_type type)
237
char buf[MAX_LEN_MSG];
238
GtkEditable *editable = GTK_EDITABLE(entry);
239
gint start_pos, end_pos;
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);
247
selection = gtk_editable_get_chars(editable, start_pos, end_pos);
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");
255
if (!fg_color && !bg_color) {
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));
262
if (0 == featured_text_apply_tag(selection, buf, sizeof(buf),
263
TTT_COLOR, 0, OFFSET_UNSET,
264
ft_color(fg_color_text,
268
} else if (0 == featured_text_apply_tag(selection, buf, sizeof(buf),
269
type, 0, OFFSET_UNSET)) {
273
/* Replace the selection. */
274
gtk_editable_delete_text(editable, start_pos, end_pos);
276
gtk_editable_insert_text(editable, buf, -1, &end_pos);
277
gtk_editable_select_region(editable, start_pos, end_pos);
283
/**************************************************************************
284
Make a chat link at the current position or make the current selection
286
**************************************************************************/
287
void inputline_make_chat_link(struct tile *ptile, bool unit)
289
char buf[MAX_LEN_MSG];
290
GtkWidget *entry = toolkit.entry;
291
GtkEditable *editable = GTK_EDITABLE(entry);
292
gint start_pos, end_pos;
296
/* Get the target. */
298
punit = find_visible_unit(ptile);
300
output_window_append(ftc_client, _("No visible unit on this tile."));
307
if (gtk_editable_get_selection_bounds(editable, &start_pos, &end_pos)) {
308
/* There is a selection, make it clickable. */
310
enum text_link_type type;
312
chars = gtk_editable_get_chars(editable, start_pos, end_pos);
316
} else if (tile_city(ptile)) {
318
target = tile_city(ptile);
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);
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);
334
/* Just insert the link at the current position. */
335
start_pos = gtk_editable_get_position(editable);
337
chars = gtk_editable_get_chars(editable, MAX(start_pos - 1, 0),
340
sz_strlcpy(buf, unit_link(punit));
341
} else if (tile_city(ptile)) {
342
sz_strlcpy(buf, city_link(tile_city(ptile)));
344
sz_strlcpy(buf, tile_link(ptile));
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);
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);
357
gtk_widget_grab_focus(entry);
358
gtk_editable_set_position(editable, end_pos);
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
157
395
/**************************************************************************
397
**************************************************************************/
398
static gboolean event_after(GtkWidget *text_view, GdkEventButton *event)
400
GtkTextIter start, end, iter;
401
GtkTextBuffer *buffer;
404
struct tile *ptile = NULL;
406
if (event->type != GDK_BUTTON_RELEASE || event->button != 1) {
410
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view));
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)) {
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);
422
gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(text_view), &iter, x, y);
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"));
431
/* This is a link. */
432
int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "id"));
435
/* Real type is type - 1.
436
* See comment in apply_text_tag() for g_object_set_data(). */
442
struct city *pcity = game_find_city_by_number(id);
445
ptile = client_city_tile(pcity);
447
output_window_append(ftc_client, _("This city isn't known!"));
452
ptile = index_to_tile(id);
455
output_window_append(ftc_client,
456
_("This tile doesn't exist in this game!"));
461
struct unit *punit = game_find_unit_by_number(id);
464
ptile = unit_tile(punit);
466
output_window_append(ftc_client, _("This unit isn't known!"));
473
center_tile_mapcanvas(ptile);
474
link_mark_restore(type, id);
475
gtk_widget_grab_focus(GTK_WIDGET(map_canvas));
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)
490
static gboolean hovering_over_link = FALSE;
491
static GdkCursor *hand_cursor = NULL;
492
static GdkCursor *regular_cursor = NULL;
495
gboolean hovering = FALSE;
497
/* Initialize the cursors. */
499
hand_cursor = gdk_cursor_new_for_display(
500
gdk_screen_get_display(gdk_screen_get_default()), GDK_HAND2);
502
if (!regular_cursor) {
503
regular_cursor = gdk_cursor_new_for_display(
504
gdk_screen_get_display(gdk_screen_get_default()), GDK_XTERM);
507
gtk_text_view_get_iter_at_location(text_view, &iter, x, y);
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"));
520
if (hovering != hovering_over_link) {
521
hovering_over_link = hovering;
523
if (hovering_over_link) {
524
gdk_window_set_cursor(gtk_text_view_get_window(text_view,
525
GTK_TEXT_WINDOW_TEXT),
528
gdk_window_set_cursor(gtk_text_view_get_window(text_view,
529
GTK_TEXT_WINDOW_TEXT),
539
/**************************************************************************
540
Maybe are the mouse is moving over a link.
541
**************************************************************************/
542
static gboolean motion_notify_event(GtkWidget *text_view,
543
GdkEventMotion *event)
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);
556
/**************************************************************************
557
Maybe are the mouse is moving over a link.
558
**************************************************************************/
559
static gboolean visibility_notify_event(GtkWidget *text_view,
560
GdkEventVisibility *event)
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,
568
set_cursor_if_appropriate(GTK_TEXT_VIEW(text_view), bx, by);
573
/**************************************************************************
574
Set the appropriate callbacks for the message buffer.
575
**************************************************************************/
576
void set_message_buffer_view_link_handlers(GtkWidget *view)
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);
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)
593
static bool initalized = FALSE;
594
GtkTextIter start, stop;
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);
608
/* Get the position. */
610
* N.B.: text_tag_*_offset() value is in bytes, so we need to convert it
611
* to utf8 character offset.
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);
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)));
624
switch (text_tag_type(ptag)) {
626
gtk_text_buffer_apply_tag_by_name(buf, "bold", &start, &stop);
629
gtk_text_buffer_apply_tag_by_name(buf, "italic", &start, &stop);
632
gtk_text_buffer_apply_tag_by_name(buf, "strike", &start, &stop);
635
gtk_text_buffer_apply_tag_by_name(buf, "underline", &start, &stop);
639
/* We have to make a new tag every time. */
640
GtkTextTag *tag = NULL;
644
if (gdk_color_parse(text_tag_color_foreground(ptag), &foreground)) {
645
if (gdk_color_parse(text_tag_color_background(ptag),
647
tag = gtk_text_buffer_create_tag(buf, NULL,
648
"foreground-gdk", &foreground,
649
"background-gdk", &background,
652
tag = gtk_text_buffer_create_tag(buf, NULL,
653
"foreground-gdk", &foreground,
656
} else if (gdk_color_parse(text_tag_color_background(ptag),
658
tag = gtk_text_buffer_create_tag(buf, NULL,
659
"background-gdk", &background,
664
break; /* No color. */
666
gtk_text_buffer_apply_tag(buf, tag, &start, &stop);
667
g_object_unref(G_OBJECT(tag));
672
struct color *pcolor = NULL;
675
switch (text_tag_link_type(ptag)) {
677
pcolor = get_color(tileset, COLOR_MAPVIEW_CITY_LINK);
680
pcolor = get_color(tileset, COLOR_MAPVIEW_TILE_LINK);
683
pcolor = get_color(tileset, COLOR_MAPVIEW_UNIT_LINK);
688
break; /* Not a valid link type case. */
691
tag = gtk_text_buffer_create_tag(buf, NULL,
692
"foreground-gdk", &pcolor->color,
693
"underline", PANGO_UNDERLINE_SINGLE,
696
/* Type 0 is reserved for non-link tags. So, add 1 to the
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));
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,
163
717
GtkTextBuffer *buf;
164
718
GtkTextIter iter;
165
719
GtkTextMark *mark;
720
offset_t text_start_offset;
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);
172
if (show_chat_message_time) {
727
if (gui_gtk2_show_chat_message_time) {
173
728
char timebuf[64];
175
730
struct tm *now_tm;
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)
236
if (!message_buffer) {
239
gtk_text_buffer_get_end_iter(message_buffer, &end);
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);
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);
789
Returns whether the chatline is scrolled to the bottom.
790
**************************************************************************/
791
bool chatline_is_scrolled_to_bottom(void)
795
gdouble val, max, upper, page_size;
797
if (get_client_page() == PAGE_GAME) {
798
w = GTK_WIDGET(main_message_area);
800
w = GTK_WIDGET(start_message_area);
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;
815
return max - val < 0.00000001;
818
/**************************************************************************
819
Scrolls the pregame and in-game chat windows all the way to the bottom.
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
825
**************************************************************************/
826
static gboolean chatline_scroll_callback(gpointer data)
828
chatline_scroll_to_bottom(FALSE); /* Not delayed this time! */
830
*((guint *) data) = 0;
831
return FALSE; /* Remove this idle function. */
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)
840
static guint callback_id = 0;
843
if (callback_id == 0) {
844
callback_id = g_idle_add(chatline_scroll_callback, &callback_id);
846
} else if (message_buffer) {
849
gtk_text_buffer_get_end_iter(message_buffer, &end);
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);
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);
862
/**************************************************************************
864
**************************************************************************/
865
static void make_tag_callback(GtkToolButton *button, gpointer data)
867
inputline_make_tag(GTK_ENTRY(data),
868
GPOINTER_TO_INT(g_object_get_data(G_OBJECT(button),
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)
878
GdkColor *current_color = g_object_get_data(object, color_target);
879
GdkColormap *colormap = gdk_colormap_get_system();
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);
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;
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);
903
gdk_colormap_alloc_color(colormap, current_color, TRUE, TRUE);
904
if (NULL != button) {
905
/* Update the button. */
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));
920
/**************************************************************************
921
Color selection dialog response.
922
**************************************************************************/
923
static void color_selected(GtkDialog *dialog, gint res, gpointer data)
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");
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"));
939
gtk_color_selection_get_current_color(selection, &new_color);
940
color_set(G_OBJECT(data), color_target, &new_color, button);
943
gtk_widget_destroy(GTK_WIDGET(dialog));
946
/**************************************************************************
947
Color selection tool button clicked.
948
**************************************************************************/
949
static void select_color_callback(GtkToolButton *button, gpointer data)
952
GtkWidget *dialog, *selection;
953
/* "fg_color" or "bg_color". */
954
const gchar *color_target = g_object_get_data(G_OBJECT(button),
956
GdkColor *current_color = g_object_get_data(G_OBJECT(data), color_target);
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),
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);
970
selection = gtk_color_selection_new();
971
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), selection,
973
g_object_set_data(G_OBJECT(dialog), "selection", selection);
975
gtk_color_selection_set_current_color(GTK_COLOR_SELECTION(selection),
979
gtk_widget_show_all(dialog);
982
/**************************************************************************
983
Moves the tool kit to the toolkit view.
984
**************************************************************************/
985
static gboolean move_toolkit(GtkWidget *toolkit_view, GdkEventExpose *event,
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),
995
if (parent == toolkit_view) {
996
return FALSE; /* Already owned. */
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);
1004
gtk_widget_reparent(ptoolkit->main_widget, toolkit_view);
1005
if (ptoolkit->toolbar_displayed) {
1006
gtk_widget_show(ptoolkit->toolbar);
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,
1014
gtk_widget_show_all(ptoolkit->main_widget);
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);
1021
if (widget != button_box) {
1022
gtk_widget_hide(widget);
1028
/* First time attached to a parent. */
1029
gtk_box_pack_start(GTK_BOX(toolkit_view), ptoolkit->main_widget,
1031
gtk_box_pack_end(GTK_BOX(ptoolkit->button_box), button_box,
1033
gtk_widget_show_all(ptoolkit->main_widget);
1039
/**************************************************************************
1040
Show/Hide the toolbar.
1041
**************************************************************************/
1042
static gboolean set_toolbar_visibility(GtkWidget *w,
1043
GdkEventExpose *event,
1046
struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
1047
GtkToggleButton *button = GTK_TOGGLE_BUTTON(toolkit.toggle_button);
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);
1054
/* Unsure the widget is visible. */
1055
gtk_widget_show(ptoolkit->toolbar);
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);
1062
/* Unsure the widget is visible. */
1063
gtk_widget_hide(ptoolkit->toolbar);
1070
/**************************************************************************
1071
Show/Hide the toolbar.
1072
**************************************************************************/
1073
static void button_toggled(GtkToggleButton *button, gpointer data)
1075
struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
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);
1085
gtk_widget_hide(ptoolkit->toolbar);
1086
ptoolkit->toolbar_displayed = FALSE;
1090
/**************************************************************************
1091
Returns a new inputline toolkit view widget that can contain the
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)
1099
GtkWidget *toolkit_view, *bbox;
1102
toolkit_view = gtk_vbox_new(FALSE, 0);
1103
g_signal_connect(toolkit_view, "expose-event",
1104
G_CALLBACK(move_toolkit), &toolkit);
1107
bbox = gtk_hbox_new(FALSE, 12);
1108
g_object_set_data(G_OBJECT(toolkit_view), "button_box", bbox);
1110
return toolkit_view;
1113
/**************************************************************************
1114
Appends a button to the inputline toolkit view widget.
1115
**************************************************************************/
1116
void inputline_toolkit_view_append_button(GtkWidget *toolkit_view,
1119
gtk_box_pack_start(GTK_BOX(g_object_get_data(G_OBJECT(toolkit_view),
1120
"button_box")), button, FALSE, FALSE, 0);
1123
/**************************************************************************
1124
Initializes the chatline stuff.
1125
**************************************************************************/
1126
void chatline_init(void)
1128
GtkWidget *vbox, *toolbar, *hbox, *button, *entry, *bbox;
1130
GtkTooltips *tooltips;
1133
/* Chatline history. */
1134
if (!history_list) {
1135
history_list = genlist_new();
1139
/* Inputline toolkit. */
1140
memset(&toolkit, 0, sizeof(toolkit));
1142
tooltips = gtk_tooltips_new();
1143
gtk_tooltips_enable(tooltips);
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);
1150
entry = gtk_entry_new();
1151
toolkit.entry = entry;
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;
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);
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);
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);
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);
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);
1208
gtk_toolbar_insert(GTK_TOOLBAR(toolbar),
1209
gtk_separator_tool_item_new(), -1);
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));
1224
freelog(LOG_ERROR, "Failed to set the default foreground color.");
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));
1240
freelog(LOG_ERROR, "Failed to set the default background color.");
1243
gtk_toolbar_insert(GTK_TOOLBAR(toolbar),
1244
gtk_separator_tool_item_new(), -1);
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);
1256
hbox = gtk_hbox_new(FALSE, 4);
1257
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
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;
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);
1276
bbox = gtk_hbox_new(FALSE, 0);
1277
gtk_box_pack_end(GTK_BOX(hbox), bbox, FALSE, FALSE, 0);
1278
toolkit.button_box = bbox;