Freeciv-3.3
Loading...
Searching...
No Matches
chatline.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <time.h>
22
23#include <gdk/gdkkeysyms.h>
24
25/* utility */
26#include "fcintl.h"
27#include "genlist.h"
28#include "log.h"
29#include "mem.h"
30#include "support.h"
31
32/* common */
33#include "chat.h"
34#include "featured_text.h"
35#include "game.h"
36#include "packets.h"
37
38/* client */
39#include "client_main.h"
40#include "climap.h"
41#include "control.h"
42#include "mapview_common.h"
43#include "update_queue.h"
44
45/* client/gui-gtk-3.22 */
46#include "colors.h"
47#include "gui_main.h"
48#include "gui_stuff.h"
49#include "pages.h"
50
51#include "chatline.h"
52
53#define MAX_CHATLINE_HISTORY 20
54
55static struct genlist *history_list = NULL;
56static int history_pos = -1;
57
66
68
69/**********************************************************************/
73{
74 return gtk_widget_has_focus(toolkit.entry);
75}
76
77/**********************************************************************/
84
85/**********************************************************************/
89{
90 return gtk_widget_get_mapped(toolkit.entry);
91}
92
93/**********************************************************************/
98static bool is_plain_public_message(const char *s)
99{
100 const char *p;
101
102 /* If it is a server command or an explicit ally
103 * message, then it is not a public message. */
104 if (s[0] == SERVER_COMMAND_PREFIX || s[0] == CHAT_ALLIES_PREFIX) {
105 return FALSE;
106 }
107
108 /* It might be a private message of the form
109 * 'player name with spaces':the message
110 * or with ". So skip past the player name part. */
111 if (s[0] == '\'' || s[0] == '"') {
112 p = strchr(s + 1, s[0]);
113 } else {
114 p = s;
115 }
116
117 /* Now we just need to check that it is not a private
118 * message. If we encounter a space then the preceding
119 * text could not have been a user/player name (the
120 * quote check above eliminated names with spaces) so
121 * it must be a public message. Otherwise if we encounter
122 * the message prefix : then the text parsed up until now
123 * was a player/user name and the line is intended as
124 * a private message (or explicit public message if the
125 * first character is :). */
126 while (p != NULL && *p != '\0') {
127 if (fc_isspace(*p)) {
128 return TRUE;
129 } else if (*p == CHAT_DIRECT_PREFIX) {
130 return FALSE;
131 }
132 p++;
133 }
134 return TRUE;
135}
136
137/**********************************************************************/
140static void inputline_return(GtkEntry *w, gpointer data)
141{
142 const char *theinput;
143
145
146 if (*theinput) {
150 char buf[MAX_LEN_MSG];
151
152 fc_snprintf(buf, sizeof(buf), ". %s", theinput);
153 send_chat(buf);
154 } else {
156 }
157
159 void *history_data;
160
164 }
165
167 history_pos = -1;
168 }
169
170 gtk_entry_set_text(w, "");
171}
172
173/**********************************************************************/
176static const char *get_player_or_user_name(int id)
177{
179
180 if (id < size) {
181 return conn_list_get(game.all_connections, id)->username;
182 } else {
183 struct player *pplayer = player_by_number(id - size);
184 if (pplayer) {
185 return pplayer->name;
186 } else {
187 /* Empty slot. Relies on being used with comparison function
188 * which can cope with NULL. */
189 return NULL;
190 }
191 }
192}
193
194/**********************************************************************/
203static int check_player_or_user_name(const char *prefix,
204 const char **matches,
205 const int max_matches)
206{
207 int matches_id[max_matches * 2], ind, num;
208
213 prefix, &ind, matches_id,
214 max_matches * 2, &num)) {
215 case M_PRE_EXACT:
216 case M_PRE_ONLY:
218 return 1;
219 case M_PRE_AMBIGUOUS:
220 {
221 /* Remove duplications playername/username. */
222 const char *name;
223 int i, j, c = 0;
224
225 for (i = 0; i < num && c < max_matches; i++) {
227 for (j = 0; j < c; j++) {
228 if (0 == fc_strncasecmp(name, matches[j], MAX_LEN_NAME)) {
229 break;
230 }
231 }
232 if (j >= c) {
233 matches[c++] = name;
234 }
235 }
236 return c;
237 }
238 case M_PRE_EMPTY:
239 case M_PRE_LONG:
240 case M_PRE_FAIL:
241 case M_PRE_LAST:
242 break;
243 }
244
245 return 0;
246}
247
248/**********************************************************************/
258static size_t get_common_prefix(const char *const *prefixes,
259 size_t num_prefixes,
260 char *buf, size_t buf_len)
261{
262 const char *p;
263 char *q;
264 size_t i;
265
267 for (i = 1; i < num_prefixes; i++) {
268 for (p = prefixes[i], q = buf; *p != '\0' && *q != '\0';
272 *q = '\0';
273 break;
274 }
275 }
276 }
277
278 return g_utf8_strlen(buf, -1);
279}
280
281/**********************************************************************/
286{
287#define MAX_MATCHES 10
288 const char *name[MAX_MATCHES];
290 gint pos;
291 gchar *chars, *p, *prev;
292 int num, i;
293 size_t prefix_len;
294
295 /* Part 1: get the string to complete. */
298
299 p = chars + strlen(chars);
300 while ((prev = g_utf8_find_prev_char(chars, p))) {
302 break;
303 }
304 p = prev;
305 }
306 /* p points to the start of the last word, or the start of the string. */
307
308 prefix_len = g_utf8_strlen(p, -1);
309 if (0 == prefix_len) {
310 /* Empty: nothing to complete, propagate the event. */
311 g_free(chars);
312 return FALSE;
313 }
314
315 /* Part 2: compare with player and user names. */
317 if (1 == num) {
319 pos -= prefix_len;
322 g_free(chars);
323 return TRUE;
324 } else if (num > 1) {
325 if (get_common_prefix(name, num, buf, sizeof(buf)) > prefix_len) {
327 pos -= prefix_len;
330 }
331 sz_strlcpy(buf, name[0]);
332 for (i = 1; i < num; i++) {
333 cat_snprintf(buf, sizeof(buf), ", %s", name[i]);
334 }
335 /* TRANS: comma-separated list of player/user names for completion */
336 output_window_printf(ftc_client, _("Suggestions: %s."), buf);
337 }
338
339 g_free(chars);
340 return TRUE;
341}
342
343/**********************************************************************/
347{
348 if ((ev->state & GDK_CONTROL_MASK)) {
349 /* Chatline featured text support. */
350 switch (ev->keyval) {
351 case GDK_KEY_b:
353 return TRUE;
354
355 case GDK_KEY_c:
357 return TRUE;
358
359 case GDK_KEY_i:
361 return TRUE;
362
363 case GDK_KEY_s:
365 return TRUE;
366
367 case GDK_KEY_u:
369 return TRUE;
370
371 default:
372 break;
373 }
374
375 } else {
376 /* Chatline history controls. */
377 switch (ev->keyval) {
378 case GDK_KEY_Up:
383 }
384 return TRUE;
385
386 case GDK_KEY_Down:
387 if (history_pos >= 0) {
388 history_pos--;
389 }
390
391 if (history_pos >= 0) {
394 } else {
396 }
398 return TRUE;
399
400 case GDK_KEY_Tab:
403 }
404 return FALSE;
405
406 default:
407 break;
408 }
409 }
410
411 return FALSE;
412}
413
414/**********************************************************************/
418{
419 char buf[MAX_LEN_MSG];
422 gchar *selection;
424
426 /* Let's say the selection starts and ends at the current position. */
428 }
429
431
432 if (type == TTT_COLOR) {
433 /* Get the color arguments. */
436
437 if (!fg_color && !bg_color) {
438 goto CLEAN_UP;
439 }
440
441 if (fg_color) {
443 }
444 if (bg_color) {
446 }
447
448 if (0 == featured_text_apply_tag(selection, buf, sizeof(buf),
451 bg_color_text))) {
452 goto CLEAN_UP;
453 }
454 } else if (0 == featured_text_apply_tag(selection, buf, sizeof(buf),
455 type, 0, FT_OFFSET_UNSET)) {
456 goto CLEAN_UP;
457 }
458
459 /* Replace the selection. */
464
466 g_free(selection);
469}
470
471/**********************************************************************/
475void inputline_make_chat_link(struct tile *ptile, bool unit)
476{
477 char buf[MAX_LEN_MSG];
478 GtkWidget *entry = toolkit.entry;
481 gchar *chars;
482 struct unit *punit;
483
484 /* Get the target. */
485 if (unit) {
486 punit = find_visible_unit(ptile);
487 if (!punit) {
488 output_window_append(ftc_client, _("No visible unit on this tile."));
489 return;
490 }
491 } else {
492 punit = NULL;
493 }
494
496 /* There is a selection, make it clickable. */
497 gpointer target;
498 enum text_link_type type;
499
501 if (punit) {
502 type = TLT_UNIT;
503 target = punit;
504 } else if (tile_city(ptile)) {
505 type = TLT_CITY;
506 target = tile_city(ptile);
507 } else {
508 type = TLT_TILE;
509 target = ptile;
510 }
511
512 if (0 != featured_text_apply_tag(chars, buf, sizeof(buf), TTT_LINK,
513 0, FT_OFFSET_UNSET, type, target)) {
514 /* Replace the selection. */
520 }
521 } else {
522 /* Just insert the link at the current position. */
526 start_pos + 1);
527 if (punit) {
529 } else if (tile_city(ptile)) {
531 } else {
532 sz_strlcpy(buf, tile_link(ptile));
533 }
534
535 if (start_pos > 0 && strlen(chars) > 0 && chars[0] != ' ') {
536 /* Maybe insert an extra space. */
538 }
540 if (chars[start_pos > 0 ? 1 : 0] != '\0'
541 && chars[start_pos > 0 ? 1 : 0] != ' ') {
542 /* Maybe insert an extra space. */
544 }
547 }
548
549 g_free(chars);
550}
551
552/**********************************************************************/
581
582/**********************************************************************/
586{
587 GtkTextIter start, end, iter;
588 GtkTextBuffer *buffer;
589 GSList *tags, *tagp;
590 gint x, y;
591 struct tile *ptile = NULL;
592
593 if (event->type != GDK_BUTTON_RELEASE || event->button != 1) {
594 return FALSE;
595 }
596
598
599 /* We shouldn't follow a link if the user has selected something. */
600 gtk_text_buffer_get_selection_bounds(buffer, &start, &end);
602 return FALSE;
603 }
604
607 event->x, event->y, &x, &y);
608
610
611 if ((tags = gtk_text_iter_get_tags(&iter))) {
612 for (tagp = tags; tagp; tagp = tagp->next) {
613 GtkTextTag *tag = tagp->data;
614 enum text_link_type type =
616
617 if (type != 0) {
618 /* This is a link. */
619 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tag), "id"));
620 ptile = NULL;
621
622 /* Real type is type - 1.
623 * See comment in apply_text_tag() for g_object_set_data(). */
624 type--;
625
626 switch (type) {
627 case TLT_CITY:
628 {
629 struct city *pcity = game_city_by_number(id);
630
631 if (pcity) {
632 ptile = client_city_tile(pcity);
633 } else {
634 output_window_append(ftc_client, _("This city isn't known!"));
635 }
636 }
637 break;
638 case TLT_TILE:
639 ptile = index_to_tile(&(wld.map), id);
640
641 if (!ptile) {
643 _("This tile doesn't exist in this game!"));
644 }
645 break;
646 case TLT_UNIT:
647 {
648 struct unit *punit = game_unit_by_number(id);
649
650 if (punit) {
651 ptile = unit_tile(punit);
652 } else {
653 output_window_append(ftc_client, _("This unit isn't known!"));
654 }
655 }
656 break;
657 }
658
659 if (ptile) {
663 }
664 }
665 }
666 g_slist_free(tags);
667 }
668
669 return FALSE;
670}
671
672/**********************************************************************/
676{
678 static GdkCursor *hand_cursor = NULL;
679 static GdkCursor *regular_cursor = NULL;
680 GSList *tags, *tagp;
683
684 /* Initialize the cursors. */
685 if (!hand_cursor) {
688 }
689 if (!regular_cursor) {
692 }
693
695
697 for (tagp = tags; tagp; tagp = tagp->next) {
698 enum text_link_type type =
700
701 if (type != 0) {
702 hovering = TRUE;
703 break;
704 }
705 }
706
709
710 if (hovering_over_link) {
714 } else {
718 }
719 }
720
721 if (tags) {
722 g_slist_free(tags);
723 }
724}
725
726/**********************************************************************/
741
742/**********************************************************************/
752
753/**********************************************************************/
757 ft_offset_t text_start_offset, const char *text)
758{
759 static bool initialized = FALSE;
760 GtkTextIter start, stop;
761
762 if (!initialized) {
764 "weight", PANGO_WEIGHT_BOLD, NULL);
766 "style", PANGO_STYLE_ITALIC, NULL);
768 "strikethrough", TRUE, NULL);
769 gtk_text_buffer_create_tag(buf, "underline",
770 "underline", PANGO_UNDERLINE_SINGLE, NULL);
772 }
773
774 /* Get the position. */
775 /*
776 * N.B.: text_tag_*_offset() value is in bytes, so we need to convert it
777 * to utf8 character offset.
778 */
781 text + text_tag_start_offset(ptag)));
784 } else {
787 text + text_tag_stop_offset(ptag)));
788 }
789
790 switch (text_tag_type(ptag)) {
791 case TTT_BOLD:
792 gtk_text_buffer_apply_tag_by_name(buf, "bold", &start, &stop);
793 break;
794 case TTT_ITALIC:
795 gtk_text_buffer_apply_tag_by_name(buf, "italic", &start, &stop);
796 break;
797 case TTT_STRIKE:
798 gtk_text_buffer_apply_tag_by_name(buf, "strike", &start, &stop);
799 break;
800 case TTT_UNDERLINE:
801 gtk_text_buffer_apply_tag_by_name(buf, "underline", &start, &stop);
802 break;
803 case TTT_COLOR:
804 {
805 /* We have to make a new tag every time. */
806 GtkTextTag *tag = NULL;
807 const char *foreground = text_tag_color_foreground(ptag);
808 const char *background = text_tag_color_background(ptag);
809
810 if (foreground && foreground[0]) {
811 if (background && background[0]) {
813 "foreground", foreground,
814 "background", background,
815 NULL);
816 } else {
818 "foreground", foreground,
819 NULL);
820 }
821 } else if (background && background[0]) {
823 "background", background,
824 NULL);
825 }
826
827 if (!tag) {
828 break; /* No color. */
829 }
830 gtk_text_buffer_apply_tag(buf, tag, &start, &stop);
831 }
832 break;
833 case TTT_LINK:
834 {
835 struct color *pcolor = NULL;
836 GtkTextTag *tag;
837
838 switch (text_tag_link_type(ptag)) {
839 case TLT_CITY:
841 break;
842 case TLT_TILE:
844 break;
845 case TLT_UNIT:
847 break;
848 }
849
850 if (!pcolor) {
851 break; /* Not a valid link type case. */
852 }
853
855 "foreground-rgba", &pcolor->color,
856 "underline", PANGO_UNDERLINE_SINGLE,
857 NULL);
858
859 /* Type 0 is reserved for non-link tags. So, add 1 to the
860 * type value. */
861 g_object_set_data(G_OBJECT(tag), "type",
863 g_object_set_data(G_OBJECT(tag), "id",
865 gtk_text_buffer_apply_tag(buf, tag, &start, &stop);
866 break;
867 }
868 }
869}
870
871/**********************************************************************/
876 const struct text_tag_list *tags,
877 int conn_id)
878{
883
885
886 if (buf == NULL) {
887 log_error("Output when no message buffer: %s", astring);
888
889 return;
890 }
891
893 gtk_text_buffer_insert(buf, &iter, "\n", -1);
895
897 char timebuf[64];
898 time_t now;
899 struct tm now_tm;
900
901 now = time(NULL);
903 strftime(timebuf, sizeof(timebuf), "[%H:%M:%S] ", &now_tm);
905 }
906
912
913 if (main_message_area) {
915 }
916 if (start_message_area) {
918 }
920
922}
923
924/**********************************************************************/
930{
931 GtkTextIter start, end;
932 gchar *txt;
933
936
938 g_free(txt);
939}
940
941/**********************************************************************/
945{
946 set_output_window_text(_("Cleared output window."));
947}
948
949/**********************************************************************/
952void set_output_window_text(const char *text)
953{
955}
956
957/**********************************************************************/
961{
962 GtkWidget *sw, *w;
964 gdouble val, max, upper, page_size;
965
966 if (get_client_page() == PAGE_GAME) {
968 } else {
970 }
971
972 if (w == NULL) {
973 return TRUE;
974 }
975
976 sw = gtk_widget_get_parent(w);
979 g_object_get(G_OBJECT(vadj), "upper", &upper,
980 "page-size", &page_size, NULL);
981 max = upper - page_size;
982
983 /* Approximation. */
984 return max - val < 0.00000001;
985}
986
987/**********************************************************************/
996{
997 chatline_scroll_to_bottom(FALSE); /* Not delayed this time! */
998
999 *((guint *) data) = 0;
1000 return FALSE; /* Remove this idle function. */
1001}
1002
1003/**********************************************************************/
1008{
1009 static guint callback_id = 0;
1010
1011 if (delayed) {
1012 if (callback_id == 0) {
1014 }
1015 } else if (message_buffer) {
1016 GtkTextIter end;
1017
1019
1020 if (main_message_area) {
1022 &end, 0.0, TRUE, 1.0, 0.0);
1023 }
1024 if (start_message_area) {
1026 &end, 0.0, TRUE, 1.0, 0.0);
1027 }
1028 }
1029}
1030
1031/**********************************************************************/
1034static void make_tag_callback(GtkToolButton *button, gpointer data)
1035{
1038 "text_tag_type")));
1039}
1040
1041/**********************************************************************/
1044static void color_set(GObject *object, const gchar *color_target,
1045 GdkRGBA *color, GtkToolButton *button)
1046{
1048
1049 if (NULL == color) {
1050 /* Clears the current color. */
1051 if (NULL != current_color) {
1054 if (NULL != button) {
1056 }
1057 }
1058 } else {
1059 /* Apply the new color. */
1060 if (NULL != current_color) {
1061 /* We already have a GdkRGBA pointer. */
1062 *current_color = *color;
1063 } else {
1064 /* We need to make a GdkRGBA pointer. */
1067 }
1068
1069 if (NULL != button) {
1070 /* Update the button. */
1073
1074 {
1076 CAIRO_FORMAT_RGB24, 16, 16);
1077 cairo_t *cr = cairo_create(surface);
1079 cairo_paint(cr);
1080 cairo_destroy(cr);
1081 pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, 16, 16);
1082 cairo_surface_destroy(surface);
1083 }
1088 }
1089 }
1090}
1091
1092/**********************************************************************/
1095static void color_selected(GtkDialog *dialog, gint res, gpointer data)
1096{
1097 GtkToolButton *button =
1098 GTK_TOOL_BUTTON(g_object_get_data(G_OBJECT(dialog), "button"));
1099 const gchar *color_target =
1100 g_object_get_data(G_OBJECT(button), "color_target");
1101
1102 if (res == GTK_RESPONSE_REJECT) {
1103 /* Clears the current color. */
1104 color_set(G_OBJECT(data), color_target, NULL, button);
1105 } else if (res == GTK_RESPONSE_OK) {
1106 /* Apply the new color. */
1108 GTK_COLOR_CHOOSER(g_object_get_data(G_OBJECT(dialog), "chooser"));
1110
1112 color_set(G_OBJECT(data), color_target, &new_color, button);
1113 }
1114
1116}
1117
1118/**********************************************************************/
1122{
1123 GtkWidget *dialog, *chooser;
1124 /* "fg_color" or "bg_color". */
1126 "color_target");
1128
1129 /* TRANS: "text" or "background". */
1130 gchar *buf = g_strdup_printf(_("Select the %s color"),
1131 (const char *) g_object_get_data(G_OBJECT(button),
1132 "color_info"));
1134 _("_Cancel"), GTK_RESPONSE_CANCEL,
1135 _("C_lear"), GTK_RESPONSE_REJECT,
1136 _("_OK"), GTK_RESPONSE_OK, NULL);
1137 setup_dialog(dialog, toplevel);
1138 g_object_set_data(G_OBJECT(dialog), "button", button);
1139 g_signal_connect(dialog, "response", G_CALLBACK(color_selected), data);
1140
1143 chooser, FALSE, FALSE, 0);
1144 g_object_set_data(G_OBJECT(dialog), "chooser", chooser);
1145 if (current_color) {
1147 }
1148
1149 gtk_widget_show_all(dialog);
1150 g_free(buf);
1151}
1152
1153/**********************************************************************/
1157 gpointer data)
1158{
1159 struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
1162 "button_box"));
1163 GList *list, *iter;
1164
1165 if (parent) {
1166 if (parent == toolkit_view) {
1167 return FALSE; /* Already owned. */
1168 }
1169
1170 /* N.B.: We need to hide/show the toolbar to reset the sensitivity
1171 * of the tool buttons. */
1172 if (ptoolkit->toolbar_displayed) {
1173 gtk_widget_hide(ptoolkit->toolbar);
1174 }
1175 g_object_ref(ptoolkit->main_widget); /* Make sure reference count stays above 0
1176 * during the transition to new parent. */
1179 g_object_unref(ptoolkit->main_widget);
1180 if (ptoolkit->toolbar_displayed) {
1181 gtk_widget_show(ptoolkit->toolbar);
1182 }
1183
1185 /* Attach to the toolkit button_box. */
1187 }
1188 gtk_widget_show_all(ptoolkit->main_widget);
1189 if (!ptoolkit->toolbar_displayed) {
1190 gtk_widget_hide(ptoolkit->toolbar);
1191 }
1192
1193 /* Hide all other buttons boxes. */
1195 for (iter = list; iter != NULL; iter = g_list_next(iter)) {
1196 GtkWidget *widget = GTK_WIDGET(iter->data);
1197
1198 if (widget != button_box) {
1200 }
1201 }
1203
1204 } else {
1205 /* First time attached to a parent. */
1208 gtk_widget_show_all(ptoolkit->main_widget);
1209 }
1210
1211 return FALSE;
1212}
1213
1214/**********************************************************************/
1218 gpointer data)
1219{
1220 struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
1221 GtkToggleButton *button = GTK_TOGGLE_BUTTON(toolkit.toggle_button);
1222
1223 if (ptoolkit->toolbar_displayed) {
1224 if (!gtk_toggle_button_get_active(button)) {
1225 /* button_toggled() will be called and the toolbar shown. */
1227 } else {
1228 /* Ensure the widget is visible. */
1229 gtk_widget_show(ptoolkit->toolbar);
1230 }
1231 } else {
1232 if (gtk_toggle_button_get_active(button)) {
1233 /* button_toggled() will be called and the toolbar hidden. */
1235 } else {
1236 /* Ensure the widget is not visible. */
1237 gtk_widget_hide(ptoolkit->toolbar);
1238 }
1239 }
1240
1241 return FALSE;
1242}
1243
1244/**********************************************************************/
1247static void button_toggled(GtkToggleButton *button, gpointer data)
1248{
1249 struct inputline_toolkit *ptoolkit = (struct inputline_toolkit *) data;
1250
1251 if (gtk_toggle_button_get_active(button)) {
1252 gtk_widget_show(ptoolkit->toolbar);
1253 ptoolkit->toolbar_displayed = TRUE;
1255 /* Make sure to be still at the end. */
1257 }
1258 } else {
1259 gtk_widget_hide(ptoolkit->toolbar);
1260 ptoolkit->toolbar_displayed = FALSE;
1261 }
1262}
1263
1264/**********************************************************************/
1289
1290/**********************************************************************/
1299
1300/**********************************************************************/
1304{
1305 GtkWidget *vbox, *toolbar, *hbox, *button, *entry, *bbox;
1307 GdkRGBA color;
1308
1309 /* Chatline history. */
1310 if (!history_list) {
1312 history_pos = -1;
1313 }
1314
1315 /* Inputline toolkit. */
1316 memset(&toolkit, 0, sizeof(toolkit));
1317
1318 vbox = gtk_grid_new();
1322 toolkit.main_widget = vbox;
1323 g_signal_connect_after(vbox, "map",
1325
1326 entry = gtk_entry_new();
1327 g_object_set(entry, "margin", 2, NULL);
1329 toolkit.entry = entry;
1330
1331 /* First line: toolbar */
1339 toolkit.toolbar = toolbar;
1340
1341 /* Bold button. */
1342 item = gtk_tool_button_new(gtk_image_new_from_icon_name("format-text-bold", 0),
1343 _("Bold"));
1344
1346 g_object_set_data(G_OBJECT(item), "text_tag_type",
1349 gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Bold (Ctrl-B)"));
1350
1351 /* Italic button. */
1352 item = gtk_tool_button_new(gtk_image_new_from_icon_name("format-text-italic", 0),
1353 _("Italic"));
1355 g_object_set_data(G_OBJECT(item), "text_tag_type",
1358 gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Italic (Ctrl-I)"));
1359
1360 /* Strike button. */
1361 item = gtk_tool_button_new(gtk_image_new_from_icon_name("format-text-strikethrough", 0),
1362 _("Strikethrough"));
1364 g_object_set_data(G_OBJECT(item), "text_tag_type",
1367 gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Strikethrough (Ctrl-S)"));
1368
1369 /* Underline button. */
1370 item = gtk_tool_button_new(gtk_image_new_from_icon_name("format-text-underline", 0),
1371 _("Underline"));
1373 g_object_set_data(G_OBJECT(item), "text_tag_type",
1376 gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Underline (Ctrl-U)"));
1377
1378 /* Color button. */
1379 item = gtk_tool_button_new(NULL, _("Color"));
1381 g_object_set_data(G_OBJECT(item), "text_tag_type",
1384 gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Color (Ctrl-C)"));
1385
1388
1389 /* Foreground selector. */
1392 g_object_set_data(G_OBJECT(item), "color_target", fc_strdup("fg_color"));
1393 g_object_set_data(G_OBJECT(item), "color_info",
1394 fc_strdup(_("foreground")));
1395 g_signal_connect(item, "clicked",
1397 gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Select the text color"));
1398 if (gdk_rgba_parse(&color, "#000000")) {
1399 /* Set default foreground color. */
1401 } else {
1402 log_error("Failed to set the default foreground color.");
1403 }
1404
1405 /* Background selector. */
1408 g_object_set_data(G_OBJECT(item), "color_target", fc_strdup("bg_color"));
1409 g_object_set_data(G_OBJECT(item), "color_info",
1410 fc_strdup(_("background")));
1411 g_signal_connect(item, "clicked",
1414 _("Select the background color"));
1415 if (gdk_rgba_parse(&color, "#ffffff")) {
1416 /* Set default background color. */
1418 } else {
1419 log_error("Failed to set the default background color.");
1420 }
1421
1424
1425 /* Return button. */
1426 item = gtk_tool_button_new(NULL, _("OK"));
1428 g_signal_connect_swapped(item, "clicked",
1431 /* TRANS: "Return" means the return key. */
1432 _("Send the chat (Return)"));
1433
1434 /* Second line */
1435 hbox = gtk_grid_new();
1438
1439 /* Toggle button. */
1440 button = gtk_toggle_button_new();
1441 g_object_set(button, "margin", 2, NULL);
1444 gtk_image_new_from_icon_name("gtk-edit", 0));
1445 g_signal_connect(button, "toggled", G_CALLBACK(button_toggled), &toolkit);
1446 gtk_widget_set_tooltip_text(GTK_WIDGET(button), _("Chat tools"));
1447 toolkit.toggle_button = button;
1448
1449 /* Entry. */
1452 g_signal_connect(entry, "key_press_event",
1454
1455 /* Button box. */
1456 bbox = gtk_grid_new();
1458 toolkit.button_box = bbox;
1459}
1460
1461/**********************************************************************/
1465{
1466 char *vertext = (char *)user_data;
1467
1469
1471
1472 return G_SOURCE_REMOVE;
1473}
1474
1475/**********************************************************************/
1478void version_message(const char *vertext)
1479{
1480 int len = strlen(vertext) + 1;
1481 char *persistent = fc_malloc(len);
1482
1484
1486}
struct canvas int int int int struct sprite *sprite struct canvas struct color * pcolor
Definition canvas_g.h:56
#define CHAT_DIRECT_PREFIX
Definition chat.h:31
#define CHAT_ALLIES_PREFIX
Definition chat.h:30
#define SERVER_COMMAND_PREFIX
Definition chat.h:28
int send_chat(const char *message)
void output_window_append(const struct ft_color color, const char *featured_text)
void write_chatline_content(const char *txt)
void output_window_printf(const struct ft_color color, const char *format,...)
enum client_states client_state(void)
@ C_S_RUNNING
Definition client_main.h:47
struct tile * client_city_tile(const struct city *pcity)
Definition climap.c:87
static struct fc_sockaddr_list * list
Definition clinet.c:102
struct color * get_color(const struct tileset *t, enum color_std stdcolor)
char * incite_cost
Definition comments.c:76
#define MAX_LEN_MSG
Definition conn_types.h:37
struct unit * find_visible_unit(struct tile *ptile)
Definition control.c:822
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit * punit
Definition dialogs_g.h:74
struct unit struct city struct unit struct tile struct extra_type const struct act_prob *act_probs int actor_unit_id struct unit struct unit int const struct action *paction struct unit struct city * pcity
Definition dialogs_g.h:78
static bool initialized
Definition effects.c:42
enum event_type event
Definition events.c:81
#define MAX_LEN_NAME
Definition fc_types.h:66
#define _(String)
Definition fcintl.h:67
size_t featured_text_apply_tag(const char *text_source, char *featured_text, size_t featured_text_len, enum text_tag_type tag_type, ft_offset_t start_offset, ft_offset_t stop_offset,...)
enum text_link_type text_tag_link_type(const struct text_tag *ptag)
const char * tile_link(const struct tile *ptile)
ft_offset_t text_tag_stop_offset(const struct text_tag *ptag)
const char * text_tag_color_foreground(const struct text_tag *ptag)
const struct ft_color ftc_client
const char * city_link(const struct city *pcity)
int text_tag_link_id(const struct text_tag *ptag)
ft_offset_t text_tag_start_offset(const struct text_tag *ptag)
const char * unit_link(const struct unit *punit)
const char * text_tag_color_background(const struct text_tag *ptag)
#define text_tag_list_iterate_end
#define text_tag_list_iterate(tags, ptag)
#define FT_OFFSET_UNSET
int ft_offset_t
text_link_type
@ TLT_TILE
@ TLT_UNIT
@ TLT_CITY
text_tag_type
@ TTT_LINK
@ TTT_BOLD
@ TTT_ITALIC
@ TTT_STRIKE
@ TTT_COLOR
@ TTT_UNDERLINE
static struct ft_color ft_color_construct(const char *foreground, const char *background)
struct civ_game game
Definition game.c:61
struct world wld
Definition game.c:62
struct unit * game_unit_by_number(int id)
Definition game.c:115
struct city * game_city_by_number(int id)
Definition game.c:106
bool genlist_remove(struct genlist *pgenlist, const void *punlink)
Definition genlist.c:322
struct genlist * genlist_new(void)
Definition genlist.c:31
void * genlist_get(const struct genlist *pgenlist, int idx)
Definition genlist.c:221
void genlist_prepend(struct genlist *pgenlist, void *data)
Definition genlist.c:500
int genlist_size(const struct genlist *pgenlist)
Definition genlist.c:192
bool inputline_has_focus(void)
Definition chatline.c:72
static void set_cursor_if_appropriate(GtkTextView *text_view, gint x, gint y)
Definition chatline.c:675
static size_t get_common_prefix(const char *const *prefixes, size_t num_prefixes, char *buf, size_t buf_len)
Definition chatline.c:258
static int history_pos
Definition chatline.c:56
#define MAX_CHATLINE_HISTORY
Definition chatline.c:53
static gboolean version_message_main_thread(gpointer user_data)
Definition chatline.c:1464
void inputline_toolkit_view_append_button(GtkWidget *toolkit_view, GtkWidget *button)
Definition chatline.c:1293
void version_message(const char *vertext)
Definition chatline.c:1478
static const char * get_player_or_user_name(int id)
Definition chatline.c:176
GtkWidget * inputline_toolkit_view_new(void)
Definition chatline.c:1271
void log_output_window(void)
Definition chatline.c:929
static gboolean chatline_scroll_callback(gpointer data)
Definition chatline.c:995
void set_message_buffer_view_link_handlers(GtkWidget *view)
Definition chatline.c:745
static gboolean motion_notify_event(GtkWidget *text_view, GdkEventMotion *event)
Definition chatline.c:729
static void button_toggled(GtkToggleButton *button, gpointer data)
Definition chatline.c:1247
static void select_color_callback(GtkToolButton *button, gpointer data)
Definition chatline.c:1121
void scroll_if_necessary(GtkTextView *textview, GtkTextMark *scroll_target)
Definition chatline.c:558
bool inputline_is_visible(void)
Definition chatline.c:88
static void color_selected(GtkDialog *dialog, gint res, gpointer data)
Definition chatline.c:1095
static bool is_plain_public_message(const char *s)
Definition chatline.c:98
static bool chatline_autocomplete(GtkEditable *editable)
Definition chatline.c:285
static struct genlist * history_list
Definition chatline.c:55
void clear_output_window(void)
Definition chatline.c:944
void apply_text_tag(const struct text_tag *ptag, GtkTextBuffer *buf, ft_offset_t text_start_offset, const char *text)
Definition chatline.c:756
void chatline_init(void)
Definition chatline.c:1303
static void make_tag_callback(GtkToolButton *button, gpointer data)
Definition chatline.c:1034
static int check_player_or_user_name(const char *prefix, const char **matches, const int max_matches)
Definition chatline.c:203
static struct inputline_toolkit toolkit
void inputline_make_chat_link(struct tile *ptile, bool unit)
Definition chatline.c:475
bool chatline_is_scrolled_to_bottom(void)
Definition chatline.c:960
static gboolean event_after(GtkWidget *text_view, GdkEventButton *event)
Definition chatline.c:585
void real_output_window_append(const char *astring, const struct text_tag_list *tags, int conn_id)
Definition chatline.c:875
static gboolean set_toolbar_visibility(GtkWidget *w, gpointer data)
Definition chatline.c:1217
static gboolean inputline_handler(GtkWidget *w, GdkEventKey *ev)
Definition chatline.c:346
static void inputline_return(GtkEntry *w, gpointer data)
Definition chatline.c:140
void inputline_grab_focus(void)
Definition chatline.c:80
void chatline_scroll_to_bottom(bool delayed)
Definition chatline.c:1007
#define MAX_MATCHES
static gboolean move_toolkit(GtkWidget *toolkit_view, gpointer data)
Definition chatline.c:1156
static void inputline_make_tag(GtkEntry *entry, enum text_tag_type type)
Definition chatline.c:417
static struct tile * pos
Definition finddlg.c:53
GtkTextBuffer * message_buffer
Definition gui_main.c:179
GtkWidget * toplevel
Definition gui_main.c:126
GtkWidget * map_canvas
Definition gui_main.c:108
GtkTextView * main_message_area
Definition gui_main.c:178
#define GUI_GTK_OPTION(optname)
Definition gui_main.h:25
void setup_dialog(GtkWidget *shell, GtkWidget *parent)
Definition gui_stuff.c:287
void append_network_statusbar(const char *text, bool force)
Definition pages.c:894
GtkWidget * start_message_area
Definition pages.c:1457
static GtkWidget * persistent
GType type
Definition repodlgs.c:1313
#define set_output_window_text(_pstr_)
Definition chatline.h:31
const char * name
Definition inputfile.c:127
#define fc_assert_ret(condition)
Definition log.h:192
#define log_error(message,...)
Definition log.h:104
struct tile * index_to_tile(const struct civ_map *imap, int mindex)
Definition map.c:471
void link_mark_restore(enum text_link_type type, int id)
void center_tile_mapcanvas(const struct tile *ptile)
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_strdup(str)
Definition mem.h:43
#define fc_malloc(sz)
Definition mem.h:34
#define color_set(color_tgt, color)
int len
Definition packhand.c:127
struct player * player_by_number(const int player_id)
Definition player.c:849
int player_slot_count(void)
Definition player.c:418
enum m_pre_result match_prefix_full(m_pre_accessor_fn_t accessor_fn, size_t n_names, size_t max_len_name, m_pre_strncmp_fn_t cmp_fn, m_strlen_fn_t len_fn, const char *prefix, int *ind_result, int *matches, int max_matches, int *pnum_matches)
Definition shared.c:1606
#define MAX(x, y)
Definition shared.h:54
@ M_PRE_EXACT
Definition shared.h:214
@ M_PRE_ONLY
Definition shared.h:215
@ M_PRE_LAST
Definition shared.h:220
@ M_PRE_LONG
Definition shared.h:218
@ M_PRE_AMBIGUOUS
Definition shared.h:216
@ M_PRE_EMPTY
Definition shared.h:217
@ M_PRE_FAIL
Definition shared.h:219
size_t size
Definition specvec.h:72
struct sprite int int y
Definition sprite_g.h:31
struct sprite int x
Definition sprite_g.h:31
Definition city.h:317
struct conn_list * all_connections
Definition game.h:96
Definition colors.h:21
GtkWidget * main_widget
Definition chatline.c:59
GtkWidget * toggle_button
Definition chatline.c:63
GtkWidget * button_box
Definition chatline.c:61
GtkWidget * entry
Definition chatline.c:60
GtkWidget * toolbar
Definition chatline.c:62
bool toolbar_displayed
Definition chatline.c:64
Definition climisc.h:82
char name[MAX_LEN_NAME]
Definition player.h:251
Definition tile.h:50
Definition unit.h:140
struct civ_map map
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:960
size_t fc_strlcpy(char *dest, const char *src, size_t n)
Definition support.c:777
bool fc_isspace(char c)
Definition support.c:1240
struct tm * fc_localtime(const time_t *timep, struct tm *result)
Definition support.c:1301
int cat_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:986
int fc_strncasecmp(const char *str0, const char *str1, size_t n)
Definition support.c:235
#define sz_strlcpy(dest, src)
Definition support.h:195
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
struct city * tile_city(const struct tile *ptile)
Definition tile.c:83
#define unit_tile(_pu)
Definition unit.h:404
enum client_pages get_client_page(void)