1 /* Pioneers - Implementation of the excellent Settlers of Catan board game.
2 * Go buy a copy.
3 *
4 * Copyright (C) 1999 Dave Cole
5 * Copyright (C) 2003 Bas Wijnen <shevek@fmf.nl>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 #include "config.h"
23 #include "frontend.h"
24 #include "common_gtk.h"
25 #include "audio.h"
26
27 static GtkWidget *chat_entry; /* messages text widget */
28 static GtkListStore *chat_completion_model = NULL;
29 static gboolean chat_grab_focus_on_update = FALSE; /**< Flag to indicate
30 * whether the chat widget should grab the focus whenever a GUI_UPDATE is sent */
31
32 enum {
33 CHAT_PLAYER_NUM, /**< Player number */
34 CHAT_PLAYER_ICON, /**< The player icon */
35 CHAT_BEEP_TEXT, /**< Text for the completion */
36 CHAT_COLUMN_LAST
37 };
38
chat_cb(GtkEntry * entry,G_GNUC_UNUSED gpointer user_data)39 static void chat_cb(GtkEntry * entry, G_GNUC_UNUSED gpointer user_data)
40 {
41 const gchar *text = gtk_entry_get_text(entry);
42
43 if (text[0] != '\0') {
44 gchar buff[MAX_CHAT + 1];
45 gint idx;
46
47 strncpy(buff, text, sizeof(buff) - 1);
48 buff[sizeof(buff) - 1] = '\0';
49 /* Replace newlines in message with spaces. In a line
50 * oriented protocol, newlines are a bit confusing :-)
51 */
52 for (idx = 0; buff[idx] != '\0'; idx++)
53 if (buff[idx] == '\n')
54 buff[idx] = ' ';
55
56 cb_chat(buff);
57 gtk_entry_set_text(entry, "");
58 }
59 }
60
chat_build_panel(void)61 GtkWidget *chat_build_panel(void)
62 {
63 GtkWidget *hbox;
64 GtkWidget *label;
65 GtkEntryCompletion *completion;
66 GtkCellRenderer *cell;
67
68 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
69 gtk_widget_show(hbox);
70
71 label = gtk_label_new(NULL);
72 /* Label text */
73 gtk_label_set_markup(GTK_LABEL(label), _("<b>Chat</b>"));
74 gtk_widget_show(label);
75 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
76
77 chat_entry = gtk_entry_new();
78 gtk_entry_set_max_length(GTK_ENTRY(chat_entry), MAX_CHAT);
79 g_signal_connect(G_OBJECT(chat_entry), "activate",
80 G_CALLBACK(chat_cb), NULL);
81 gtk_widget_show(chat_entry);
82 gtk_box_pack_start(GTK_BOX(hbox), chat_entry, TRUE, TRUE, 0);
83
84 completion = gtk_entry_completion_new();
85 gtk_entry_set_completion(GTK_ENTRY(chat_entry), completion);
86
87 chat_completion_model =
88 gtk_list_store_new(CHAT_COLUMN_LAST, G_TYPE_INT,
89 GDK_TYPE_PIXBUF, G_TYPE_STRING);
90 gtk_entry_completion_set_model(completion,
91 GTK_TREE_MODEL
92 (chat_completion_model));
93 g_object_unref(chat_completion_model);
94
95 /* In GTK 2.4 the text column cannot be set with g_object_set yet.
96 * Set the column, clear the renderers, and add our own. */
97 gtk_entry_completion_set_text_column(completion, CHAT_BEEP_TEXT);
98
99 gtk_cell_layout_clear(GTK_CELL_LAYOUT(completion));
100
101 cell = gtk_cell_renderer_pixbuf_new();
102 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(completion),
103 cell, FALSE);
104 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(completion),
105 cell,
106 "pixbuf", CHAT_PLAYER_ICON, NULL);
107
108 cell = gtk_cell_renderer_text_new();
109 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(completion),
110 cell, TRUE);
111 gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(completion),
112 cell, "text", CHAT_BEEP_TEXT, NULL);
113
114 gtk_entry_completion_set_minimum_key_length(completion, 2);
115 g_object_unref(completion);
116
117 return hbox;
118 }
119
chat_set_grab_focus_on_update(gboolean grab)120 void chat_set_grab_focus_on_update(gboolean grab)
121 {
122 chat_grab_focus_on_update = grab;
123 }
124
chat_set_focus(void)125 void chat_set_focus(void)
126 {
127 if (chat_grab_focus_on_update && !gtk_widget_is_focus(chat_entry)) {
128 gtk_widget_grab_focus(chat_entry);
129 gtk_editable_set_position(GTK_EDITABLE(chat_entry), -1);
130 }
131 }
132
chat_player_name(gint player_num,const gchar * name)133 void chat_player_name(gint player_num, const gchar * name)
134 {
135 GtkTreeIter iter;
136 enum TFindResult found;
137 GdkPixbuf *pixbuf;
138
139 found =
140 find_integer_in_tree(GTK_TREE_MODEL(chat_completion_model),
141 &iter, CHAT_PLAYER_NUM, player_num);
142
143 switch (found) {
144 case FIND_NO_MATCH:
145 gtk_list_store_append(chat_completion_model, &iter);
146 break;
147 case FIND_MATCH_INSERT_BEFORE:
148 gtk_list_store_insert_before(chat_completion_model, &iter,
149 &iter);
150 break;
151 case FIND_MATCH_EXACT:
152 break;
153 };
154
155 /* connected icon */
156 pixbuf = player_create_icon(player_num, TRUE);
157 gtk_list_store_set(chat_completion_model, &iter,
158 CHAT_PLAYER_NUM, player_num,
159 CHAT_PLAYER_ICON, pixbuf,
160 CHAT_BEEP_TEXT, g_strdup_printf("/beep %s",
161 name), -1);
162 g_object_unref(pixbuf);
163 }
164
chat_player_style(gint player_num)165 void chat_player_style(gint player_num)
166 {
167 GtkTreeIter iter;
168 enum TFindResult found;
169 GdkPixbuf *pixbuf;
170
171 found =
172 find_integer_in_tree(GTK_TREE_MODEL(chat_completion_model),
173 &iter, CHAT_PLAYER_NUM, player_num);
174
175 g_return_if_fail(found == FIND_MATCH_EXACT);
176
177 /* connected icon */
178 pixbuf = player_create_icon(player_num, TRUE);
179 gtk_list_store_set(chat_completion_model, &iter,
180 CHAT_PLAYER_ICON, pixbuf, -1);
181 g_object_unref(pixbuf);
182 }
183
chat_player_quit(gint player_num)184 void chat_player_quit(gint player_num)
185 {
186 GtkTreeIter iter;
187 enum TFindResult found;
188
189 found =
190 find_integer_in_tree(GTK_TREE_MODEL(chat_completion_model),
191 &iter, CHAT_PLAYER_NUM, player_num);
192 if (found == FIND_MATCH_EXACT) {
193 /* not connected icon */
194 GdkPixbuf *pixbuf = player_create_icon(player_num, FALSE);
195
196 gtk_list_store_set(chat_completion_model,
197 &iter, CHAT_PLAYER_ICON, pixbuf, -1);
198 g_object_unref(pixbuf);
199 }
200 }
201
chat_spectator_quit(gint spectator_num)202 void chat_spectator_quit(gint spectator_num)
203 {
204 GtkTreeIter iter;
205 enum TFindResult found;
206
207 found =
208 find_integer_in_tree(GTK_TREE_MODEL(chat_completion_model),
209 &iter, CHAT_PLAYER_NUM, spectator_num);
210 if (found == FIND_MATCH_EXACT) {
211 gtk_list_store_remove(chat_completion_model, &iter);
212 }
213 }
214
chat_clear_names(void)215 void chat_clear_names(void)
216 {
217 gtk_list_store_clear(chat_completion_model);
218 }
219
220 /** Beep a player (if the name is found)
221 * @param beeping_player The player that sent the /beep
222 * @param name The name of the beeped player
223 */
beep_player(gint beeping_player,const gchar * name)224 static void beep_player(gint beeping_player, const gchar * name)
225 {
226 gint beeped_player = find_player_by_name(name);
227 if (beeped_player != -1) {
228 if (beeped_player == my_player_num()) {
229 play_sound(SOUND_BEEP);
230 frontend_gui_update();
231 if (beeping_player == my_player_num())
232 log_message(MSG_BEEP, _("Beeper test.\n"));
233 else
234 log_message(MSG_BEEP,
235 _("%s beeped you.\n"),
236 player_name(beeping_player,
237 TRUE));
238 } else if (beeping_player == my_player_num()) {
239 log_message(MSG_BEEP, _("You beeped %s.\n"), name);
240 }
241 } else {
242 if (beeping_player == my_player_num()) {
243 /* No success */
244 log_message(MSG_BEEP,
245 _("You could not beep %s.\n"), name);
246 }
247 }
248 }
249
chat_parser(gint player_num,const gchar * chat)250 void chat_parser(gint player_num, const gchar * chat)
251 {
252 int tempchatcolor = MSG_INFO;
253 gchar *chat_str;
254 gchar *chat_alloc;
255 const gchar *joining_text;
256
257 /* If the chat matches chat from the AI, translate it.
258 * FIXME: There should be a flag to indicate the player is an AI,
259 * so that chat from human players will not be translated
260 */
261 chat_alloc = g_strdup(_(chat));
262 chat_str = chat_alloc;
263
264 if (!strncmp(chat_str, "/beep", 5)) {
265 chat_str += 5;
266 chat_str += strspn(chat_str, " \t");
267 beep_player(player_num, chat_str);
268 g_free(chat_alloc);
269 return;
270 } else if (!strncmp(chat_str, "/me", 3)) {
271 /* IRC-compatible /me */
272 chat_str += 3;
273 chat_str += strspn(chat_str, " \t") - 1;
274 chat_str[0] = ':';
275 }
276
277 switch (chat_str[0]) {
278 case ':':
279 chat_str += 1;
280 joining_text = " ";
281 break;
282 case ';':
283 chat_str += 1;
284 joining_text = "";
285 break;
286 default:
287 joining_text = _(" said: ");
288 break;
289 }
290
291 if (color_chat_enabled) {
292 if (player_is_spectator(player_num))
293 tempchatcolor = MSG_SPECTATOR_CHAT;
294 else
295 switch (player_num) {
296 case 0:
297 tempchatcolor = MSG_PLAYER1;
298 break;
299 case 1:
300 tempchatcolor = MSG_PLAYER2;
301 break;
302 case 2:
303 tempchatcolor = MSG_PLAYER3;
304 break;
305 case 3:
306 tempchatcolor = MSG_PLAYER4;
307 break;
308 case 4:
309 tempchatcolor = MSG_PLAYER5;
310 break;
311 case 5:
312 tempchatcolor = MSG_PLAYER6;
313 break;
314 case 6:
315 tempchatcolor = MSG_PLAYER7;
316 break;
317 case 7:
318 tempchatcolor = MSG_PLAYER8;
319 break;
320 default:
321 g_assert_not_reached();
322 break;
323 }
324 } else {
325 tempchatcolor = MSG_CHAT;
326 }
327 log_message_chat(player_name(player_num, TRUE), joining_text,
328 tempchatcolor, chat_str);
329 g_free(chat_alloc);
330 return;
331 }
332