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