1 /*
2  *     gtkatlantic - the gtk+ monopd client, enjoy network monopoly games
3  *
4  *
5  *  Copyright © 2002-2015 Sylvain Rochet
6  *
7  *  gtkatlantic 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; see the file COPYING. If not, see
19  *  <http://www.gnu.org/licenses/>.
20  */
21 
22 #include "config.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 
29 #include <gtk/gtk.h>
30 
31 #include "global.h"
32 #include "trade.h"
33 #include "game.h"
34 #include "client.h"
35 #include "interface.h"
36 #include "callback.h"
37 
38 
trade_initnew(gint32 tradeid)39 void trade_initnew(gint32 tradeid)  {
40 
41 	gint32 tradeslot;
42 	gchar *sendstr;
43 
44 	/* already created */
45 	if(get_trade_slot_with_tradeid(tradeid) >= 0)  return;
46 
47 	if(! game_get_valid_trade_slot(&tradeslot) )  {
48 
49 		/* no trade slot valid -> reject trade */
50 		sendstr = g_strdup_printf(".Tr%d\n", tradeid);
51 		client_send(global->game_connect, sendstr);
52 		g_free(sendstr);
53 		return;
54 	}
55 
56 	currentgame->trade[tradeslot].open = TRUE;
57 	currentgame->trade[tradeslot].tradeid = tradeid;
58 
59 	trade_create_panel(tradeslot);
60 }
61 
62 
trade_destroy(gint32 tradeid)63 void trade_destroy(gint32 tradeid)  {
64 
65 	gint32 tradeslot;
66 
67 	tradeslot = get_trade_slot_with_tradeid(tradeid);
68 	if(tradeslot < 0) return;
69 
70 	trade_destroy_slot(tradeslot);
71 }
72 
73 
trade_destroy_slot(gint32 tradeslot)74 void trade_destroy_slot(gint32 tradeslot)  {
75 
76 	if(!currentgame->trade[tradeslot].open) return;
77 
78 	gtk_widget_destroy(currentgame->trade[tradeslot].TradeWin);
79 	memset(&currentgame->trade[tradeslot], 0, sizeof(_trade) );
80 }
81 
82 
trade_create_panel(gint32 tradeslot)83 void trade_create_panel(gint32 tradeslot)  {
84 
85 	GtkWidget *TradeWin;
86 		GtkWidget *VBox;
87 			//components
88 		GtkWidget *HBox;
89 			GtkWidget *ScrollWinPlayer;
90 				GtkWidget *PlayerList;
91 			GtkWidget *VboxProposalButtons;
92 				GtkWidget *ScrollWinProposal;
93 					GtkWidget *ProposalList;
94 				GtkWidget *HBox_buttons;
95 
96 	GtkWidget *Button;
97 	GtkTreeSelection *select;
98 	GtkCellRenderer *renderer;
99 	GtkTreeViewColumn *column;
100 	GtkListStore *store;
101 
102 	TradeWin = currentgame->trade[tradeslot].TradeWin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
103 	gtk_widget_set_size_request(TradeWin, 600, 220);
104 	gtk_window_set_title(GTK_WINDOW(TradeWin), "GtkAtlantic: Trade");
105 	g_object_set_data(G_OBJECT(TradeWin), "command", GINT_TO_POINTER(TRADE_ACTION_REJECT));
106 	g_object_set_data(G_OBJECT(TradeWin), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
107 	g_signal_connect(G_OBJECT(TradeWin), "delete_event", G_CALLBACK(CallBack_trade_button), NULL);
108 
109 	/* component / hbox / buttons */
110 	VBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
111 	gtk_container_add(GTK_CONTAINER(TradeWin), VBox);
112 
113 	/* component */
114 	currentgame->trade[tradeslot].FrameComponent = gtk_frame_new("Add Component");
115 	gtk_container_set_border_width(GTK_CONTAINER(currentgame->trade[tradeslot].FrameComponent), BORDER);
116 	gtk_box_pack_start(GTK_BOX(VBox), currentgame->trade[tradeslot].FrameComponent, FALSE, FALSE, 0);
117 
118 	trade_rebuild_component(tradeslot);
119 	trade_rebuild_subcomponent(tradeslot);
120 
121 	/* player / current proposal */
122 	HBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, BORDER);
123 	gtk_container_set_border_width(GTK_CONTAINER(HBox), BORDER);
124 	gtk_box_pack_start(GTK_BOX(VBox), HBox, TRUE, TRUE, 0);
125 
126 	/* player list */
127 	ScrollWinPlayer = gtk_scrolled_window_new(NULL, NULL);
128 	gtk_widget_set_size_request(ScrollWinPlayer, 130, -1);
129 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWinPlayer), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
130 	gtk_box_pack_start(GTK_BOX(HBox), ScrollWinPlayer, FALSE, FALSE, 0);
131 
132 	PlayerList = currentgame->trade[tradeslot].PlayerList = gtk_tree_view_new();
133 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(PlayerList), FALSE);
134 	gtk_container_add(GTK_CONTAINER(ScrollWinPlayer), PlayerList);
135 	select = gtk_tree_view_get_selection(GTK_TREE_VIEW(PlayerList));
136 	gtk_tree_selection_set_mode(select, GTK_SELECTION_NONE);
137 
138 	renderer = gtk_cell_renderer_text_new();
139 	column = gtk_tree_view_column_new_with_attributes("Player", renderer, "text", TRADEPLAYERLIST_COLUMN_NAME, "cell-background", TRADEPLAYERLIST_BGCOLOR_NAME, NULL);
140 	gtk_tree_view_append_column(GTK_TREE_VIEW(PlayerList), column);
141 
142 	store = gtk_list_store_new(TRADEPLAYERLIST_COLUMN_NUM, G_TYPE_STRING, G_TYPE_STRING);
143 	gtk_tree_view_set_model(GTK_TREE_VIEW(PlayerList), GTK_TREE_MODEL(store));
144 	g_object_unref(store);
145 
146 
147 	trade_rebuild_playerlist(tradeslot);
148 	trade_rebuild_subcomponent(tradeslot);
149 
150 
151 	/* vbox for proposal & buttons */
152 	VboxProposalButtons = gtk_box_new(GTK_ORIENTATION_VERTICAL, BORDER);
153 	gtk_box_pack_start(GTK_BOX(HBox), VboxProposalButtons, TRUE, TRUE, 0);
154 
155 	/* current proposal */
156 	ScrollWinProposal = gtk_scrolled_window_new(NULL, NULL);
157 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(ScrollWinProposal), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
158 	gtk_box_pack_start(GTK_BOX(VboxProposalButtons), ScrollWinProposal, TRUE, TRUE, 0);
159 
160 	ProposalList = currentgame->trade[tradeslot].ProposalList = gtk_tree_view_new();
161 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(ProposalList), FALSE);
162 	gtk_container_add(GTK_CONTAINER(ScrollWinProposal), ProposalList);
163 	select = gtk_tree_view_get_selection(GTK_TREE_VIEW(ProposalList));
164 	g_object_set_data(G_OBJECT(select), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
165 	gtk_tree_selection_set_mode(select, GTK_SELECTION_SINGLE);
166 	g_signal_connect(G_OBJECT(select), "changed", G_CALLBACK(Callback_ProposalList_Select), NULL);
167 
168 	renderer = gtk_cell_renderer_text_new();
169 	column = gtk_tree_view_column_new_with_attributes("Type", renderer, "text", TRADEPROPOSALLIST_COLUMN_TYPE, NULL);
170 	gtk_tree_view_append_column(GTK_TREE_VIEW(ProposalList), column);
171 	column = gtk_tree_view_column_new_with_attributes("From", renderer, "text", TRADEPROPOSALLIST_COLUMN_FROM, NULL);
172 	gtk_tree_view_append_column(GTK_TREE_VIEW(ProposalList), column);
173 	column = gtk_tree_view_column_new_with_attributes("Gives", renderer, "text", TRADEPROPOSALLIST_COLUMN_GIVES, NULL);
174 	gtk_tree_view_append_column(GTK_TREE_VIEW(ProposalList), column);
175 	column = gtk_tree_view_column_new_with_attributes("To", renderer, "text", TRADEPROPOSALLIST_COLUMN_TO, NULL);
176 	gtk_tree_view_append_column(GTK_TREE_VIEW(ProposalList), column);
177 	column = gtk_tree_view_column_new_with_attributes("What", renderer, "text", TRADEPROPOSALLIST_COLUMN_WHAT, NULL);
178 	gtk_tree_view_append_column(GTK_TREE_VIEW(ProposalList), column);
179 
180 
181 	store = gtk_list_store_new(TRADEPROPOSALLIST_COLUMN_NUM, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT);
182 	gtk_tree_view_set_model(GTK_TREE_VIEW(ProposalList), GTK_TREE_MODEL(store));
183 	g_object_unref(store);
184 
185 	/* buttons */
186 	HBox_buttons = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
187 	gtk_box_pack_end(GTK_BOX(VboxProposalButtons), HBox_buttons, FALSE, FALSE, 0);
188 
189 	/* remove button */
190 	Button = gtk_button_new_with_label("Remove");
191 	g_object_set_data(G_OBJECT(Button), "command", GINT_TO_POINTER(TRADE_ACTION_REMOVE));
192 	g_object_set_data(G_OBJECT(Button), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
193 	g_signal_connect(G_OBJECT(Button), "clicked", G_CALLBACK(CallBack_trade_button), NULL);
194 	gtk_box_pack_start(GTK_BOX(HBox_buttons), Button, FALSE, FALSE, 0);
195 
196 	/* accept button */
197 	Button = gtk_button_new_with_label("Accept");
198 	g_object_set_data(G_OBJECT(Button), "command", GINT_TO_POINTER(TRADE_ACTION_ACCEPT));
199 	g_object_set_data(G_OBJECT(Button), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
200 	g_signal_connect(G_OBJECT(Button), "clicked", G_CALLBACK(CallBack_trade_button), NULL);
201 	gtk_box_pack_end(GTK_BOX(HBox_buttons), Button, FALSE, FALSE, 0);
202 
203 	/* reject button */
204 	Button = gtk_button_new_with_label("Reject");
205 	g_object_set_data(G_OBJECT(Button), "command", GINT_TO_POINTER(TRADE_ACTION_REJECT));
206 	g_object_set_data(G_OBJECT(Button), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
207 	g_signal_connect(G_OBJECT(Button), "clicked", G_CALLBACK(CallBack_trade_button), NULL);
208 	gtk_box_pack_end(GTK_BOX(HBox_buttons), Button, FALSE, FALSE, 0);
209 
210 	gtk_widget_show_all(TradeWin);
211 }
212 
213 
trade_rebuild_playerlist(gint32 tradeslot)214 void trade_rebuild_playerlist(gint32 tradeslot)  {
215 
216 	GtkWidget *PlayerList;
217 	gint32 i;
218 	GtkListStore *store;
219 
220 	if(!currentgame->trade[tradeslot].open)  return;
221 
222 	PlayerList = currentgame->trade[tradeslot].PlayerList;
223 	if(!PlayerList) return;
224 
225 	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(PlayerList)));
226 	gtk_list_store_clear(store);
227 
228 	for (i = 0; i < TRADE_MAX_PLAYER; i++)  {
229 		trade_player *p = &currentgame->trade[tradeslot].player[i];
230 		GtkTreeIter iter;
231 		if (!p->player) continue;
232 
233 		gtk_list_store_append(store, &iter);
234 		gtk_list_store_set(store, &iter,
235 		  TRADEPLAYERLIST_COLUMN_NAME, p->player->name,
236 		  TRADEPLAYERLIST_BGCOLOR_NAME, p->accept ? global->bg_green : global->bg_red,
237 		  -1);
238 	}
239 }
240 
241 
trade_rebuild_component(gint32 tradeslot)242 void trade_rebuild_component(gint32 tradeslot)  {
243 
244 	GtkWidget *box;
245 		GtkWidget *Combo;
246 
247 	guint32 i;
248 
249 	currentgame->trade[tradeslot].current_component = TRADE_TYPE_MONEY;
250 
251 	if(currentgame->trade[tradeslot].ComponentBox)  {
252 		gtk_widget_destroy(currentgame->trade[tradeslot].ComponentBox);
253 		currentgame->trade[tradeslot].ComponentBox = 0;
254 		currentgame->trade[tradeslot].SubComponentBox = 0;
255 	}
256 
257 	box = currentgame->trade[tradeslot].ComponentBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
258 	gtk_container_set_border_width(GTK_CONTAINER(box), 5);
259 	gtk_container_add(GTK_CONTAINER(currentgame->trade[tradeslot].FrameComponent), currentgame->trade[tradeslot].ComponentBox);
260 
261 	/* Choice component: Estate/Money/Card  */
262 	Combo = gtk_combo_box_text_new();
263 	g_object_set_data(G_OBJECT(Combo), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
264 	g_signal_connect(G_OBJECT(Combo), "changed", G_CALLBACK(CallBack_trade_sub_component), NULL);
265 	gtk_box_pack_start(GTK_BOX(box), Combo, FALSE, FALSE, 0);
266 
267 	gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), "Money");
268 	gtk_combo_box_set_active(GTK_COMBO_BOX(Combo), 0);
269 
270 	for(i = 0 ; i < data->number_estates ; i++)  {
271 		if(currentgame->estate[i].owner <= 0)  continue;
272 
273 		gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), "Estate");
274 		break;
275 	}
276 
277 	for(i = 0 ; i < MAX_CARDS ; i++)  {
278 		if(!currentgame->card[i].owner)  continue;
279 
280 		gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), "Card");
281 		break;
282 	}
283 
284 	gtk_widget_show_all(currentgame->trade[tradeslot].ComponentBox);
285 }
286 
287 
trade_rebuild_subcomponent(gint32 tradeslot)288 void trade_rebuild_subcomponent(gint32 tradeslot)  {
289 
290 	GtkWidget *box;
291 		GtkWidget *Label;
292 		GtkWidget *Entry;
293 		GtkWidget *Combo;
294 		GtkWidget *Button;
295 
296 	guint32 i;
297 	player *p;
298 
299 	if(!currentgame->trade[tradeslot].ComponentBox) return;
300 
301 	if(currentgame->trade[tradeslot].SubComponentBox)  {
302 		gtk_widget_destroy(currentgame->trade[tradeslot].SubComponentBox);
303 		currentgame->trade[tradeslot].SubComponentBox = 0;
304 	}
305 
306 	box = currentgame->trade[tradeslot].SubComponentBox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, BORDER);
307 	gtk_box_pack_start(GTK_BOX(currentgame->trade[tradeslot].ComponentBox), box, TRUE, TRUE, 0);
308 
309 	switch(currentgame->trade[tradeslot].current_component)  {
310 
311 		case TRADE_TYPE_ESTATE:
312 
313 			/* update button */
314 			Button = gtk_button_new_with_label("Update");
315 			g_object_set_data(G_OBJECT(Button), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
316 			g_signal_connect(G_OBJECT(Button), "clicked", G_CALLBACK(CallBack_trade_update_component), NULL);
317 			gtk_widget_set_halign(Button, GTK_ALIGN_CENTER);
318 			gtk_widget_set_valign(Button, GTK_ALIGN_CENTER);
319 			gtk_box_pack_end(GTK_BOX(box), Button, FALSE, FALSE, 0);
320 
321 			/* valid estates */
322 			Combo = gtk_combo_box_text_new();
323 			g_object_set_data(G_OBJECT(Button), "name_estate", Combo);
324 			gtk_widget_set_hexpand(Combo, TRUE);
325 			gtk_box_pack_start(GTK_BOX(box), Combo, TRUE, TRUE, 0);
326 
327 			for(i = 0 ; i < data->number_estates ; i++)  {
328 
329 				if(currentgame->estate[i].owner <= 0)  continue;
330 				gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), currentgame->estate[i].name);
331 			}
332 
333 			gtk_combo_box_set_active(GTK_COMBO_BOX(Combo), 0);
334 
335 			/* target label */
336 			Label = gtk_label_new("Target");
337 			gtk_box_pack_start(GTK_BOX(box), Label, FALSE, FALSE, 0);
338 
339 			/* valid target players */
340 			Combo = gtk_combo_box_text_new();
341 			g_object_set_data(G_OBJECT(Button), "name_target_player", Combo);
342 			gtk_box_pack_start(GTK_BOX(box), Combo, FALSE, FALSE, 0);
343 
344 			for (p = player_h; p; p = p->next) {
345 				if (p->spectator) continue;
346 				if (p->game != currentgame->gameid) continue;
347 				gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), p->name);
348 			}
349 
350 			gtk_combo_box_set_active(GTK_COMBO_BOX(Combo), 0);
351 
352 			break;
353 
354 
355 		case TRADE_TYPE_MONEY:
356 
357 			/* update button */
358 			Button = gtk_button_new_with_label("Update");
359 			g_object_set_data(G_OBJECT(Button), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
360 			g_signal_connect(G_OBJECT(Button), "clicked", G_CALLBACK(CallBack_trade_update_component), NULL);
361 			gtk_widget_set_halign(Button, GTK_ALIGN_CENTER);
362 			gtk_widget_set_valign(Button, GTK_ALIGN_CENTER);
363 			gtk_box_pack_end(GTK_BOX(box), Button, FALSE, FALSE, 0);
364 
365 			/* amount of money */
366 			Entry = gtk_entry_new();
367 			gtk_widget_set_hexpand(Entry, TRUE);
368 			g_object_set_data(G_OBJECT(Button), "amount_money", Entry);
369 			gtk_box_pack_start(GTK_BOX(box), Entry, TRUE, TRUE, 0);
370 
371 			/* from label */
372 			Label = gtk_label_new("From");
373 			gtk_box_pack_start(GTK_BOX(box), Label, FALSE, FALSE, 0);
374 
375 			/* valid from players */
376 			Combo = gtk_combo_box_text_new();
377 			g_object_set_data(G_OBJECT(Button), "name_from_player", Combo);
378 			gtk_box_pack_start(GTK_BOX(box), Combo, FALSE, FALSE, 0);
379 
380 			for (p = player_h; p; p = p->next)  {
381 				if (p->spectator) continue;
382 				if (p->game != currentgame->gameid) continue;
383 				gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), p->name);
384 
385 			}
386 
387 			gtk_combo_box_set_active(GTK_COMBO_BOX(Combo), 0);
388 
389 			/* to label */
390 			Label = gtk_label_new("To");
391 			gtk_box_pack_start(GTK_BOX(box), Label, FALSE, FALSE, 0);
392 
393 			/* valid to players */
394 			Combo = gtk_combo_box_text_new();
395 			g_object_set_data(G_OBJECT(Button), "name_to_player", Combo);
396 			gtk_box_pack_start(GTK_BOX(box), Combo, FALSE, FALSE, 0);
397 
398 			for (p = player_h; p; p = p->next) {
399 				if (p->spectator) continue;
400 				if (p->game != currentgame->gameid) continue;
401 				gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), p->name);
402 			}
403 			gtk_combo_box_set_active(GTK_COMBO_BOX(Combo), 0);
404 
405 			break;
406 
407 
408 		case TRADE_TYPE_CARD:
409 
410 			/* update button */
411 			Button = gtk_button_new_with_label("Update");
412 			g_object_set_data(G_OBJECT(Button), "tradeid", GINT_TO_POINTER(currentgame->trade[tradeslot].tradeid));
413 			g_signal_connect(G_OBJECT(Button), "clicked", G_CALLBACK(CallBack_trade_update_component), NULL);
414 			gtk_widget_set_halign(Button, GTK_ALIGN_CENTER);
415 			gtk_widget_set_valign(Button, GTK_ALIGN_CENTER);
416 			gtk_box_pack_end(GTK_BOX(box), Button, FALSE, FALSE, 0);
417 
418 			/* valid cards */
419 			Combo = gtk_combo_box_text_new();
420 			g_object_set_data(G_OBJECT(Button), "name_card", Combo);
421 			gtk_widget_set_hexpand(Combo, TRUE);
422 			gtk_box_pack_start(GTK_BOX(box), Combo, TRUE, TRUE, 0);
423 			for(i = 0 ; i < MAX_CARDS ; i++)  {
424 				gchar *tmp;
425 
426 				if(!currentgame->card[i].owner)  continue;
427 				tmp = g_strdup_printf("%d: %s", currentgame->card[i].cardid, currentgame->card[i].title);
428 				gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), tmp);
429 				g_free(tmp);
430 			}
431 			gtk_combo_box_set_active(GTK_COMBO_BOX(Combo), 0);
432 
433 			/* target label */
434 			Label = gtk_label_new("Target");
435 			gtk_box_pack_start(GTK_BOX(box), Label, FALSE, FALSE, 0);
436 
437 			/* valid target players */
438 			Combo = gtk_combo_box_text_new();
439 			g_object_set_data(G_OBJECT(Button), "name_target_player", Combo);
440 			gtk_box_pack_start(GTK_BOX(box), Combo, FALSE, FALSE, 0);
441 
442 			for (p = player_h; p; p = p->next) {
443 				if (p->spectator) continue;
444 				if (p->game != currentgame->gameid) continue;
445 				gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(Combo), p->name);
446 			}
447 			gtk_combo_box_set_active(GTK_COMBO_BOX(Combo), 0);
448 
449 			break;
450 	}
451 
452 	gtk_widget_show_all(currentgame->trade[tradeslot].SubComponentBox);
453 }
454 
455 
trade_update_revision(gint32 tradeid,gint32 revision)456 void trade_update_revision(gint32 tradeid, gint32 revision) {
457 	gint32 tradeslot;
458 
459 	tradeslot = get_trade_slot_with_tradeid(tradeid);
460 	if (tradeslot < 0) return;
461 
462 	currentgame->trade[tradeslot].revision = revision;
463 }
464 
465 
trade_update_player(gint32 tradeid,gint32 playerid,gboolean accept)466 void trade_update_player(gint32 tradeid, gint32 playerid, gboolean accept)  {
467 
468 	gint32 tradeslot;
469 	gint32 i;
470 
471 	tradeslot = get_trade_slot_with_tradeid(tradeid);
472 	if (tradeslot < 0) return;
473 
474 	for (i = 0; i < TRADE_MAX_PLAYER; i++) {
475 		trade_player *p = &currentgame->trade[tradeslot].player[i];
476 		if (!p->player) continue;
477 		if ((gint32)p->player->playerid != playerid) continue;
478 		p->accept = accept;
479 		trade_rebuild_playerlist(tradeslot);
480 		return;
481 	}
482 
483 	/* new participating player */
484 	for (i = 0; i < TRADE_MAX_PLAYER; i++) {
485 		trade_player *p = &currentgame->trade[tradeslot].player[i];
486 		if (p->player) continue;
487 		p->player = player_from_id(playerid);
488 		p->accept = accept;
489 		trade_rebuild_playerlist(tradeslot);
490 		return;
491 	}
492 }
493 
494 
trade_update_card(gint32 tradeid,gint32 cardid,gint32 targetplayer)495 void trade_update_card(gint32 tradeid, gint32 cardid, gint32 targetplayer)  {
496 
497 	gint32 tradeslot;
498 	GtkWidget *ProposalList;
499 	player *from, *to;
500 	gchar *cardname;
501 	GtkListStore *store;
502 	GtkTreeIter iter;
503 	gboolean valid;
504 
505 	tradeslot = get_trade_slot_with_tradeid(tradeid);
506 	if (tradeslot < 0) return;
507 
508 	ProposalList = currentgame->trade[tradeslot].ProposalList;
509 	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ProposalList)));
510 
511 	/* remove previous cardid */
512 	valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
513 	while (valid) {
514 		gint32 typeid, card;
515 		GtkTreeIter curiter = iter;
516 
517 		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); /* get next iter so we can remove the current entry safely */
518 		gtk_tree_model_get(GTK_TREE_MODEL(store), &curiter,
519 		  TRADEPROPOSALLIST_COLUMN_TYPE_ID, &typeid,
520 		  TRADEPROPOSALLIST_COLUMN_AUX_ID, &card,
521 		  -1);
522 
523 		if ( typeid == TRADE_TYPE_CARD  &&  card == cardid ) {
524 			gtk_list_store_remove(store, &curiter);
525 		}
526 	}
527 
528 	if (targetplayer < 0)  {
529 		return;
530 	}
531 
532 	from = player_from_id(currentgame->card[get_card_slot_with_cardid(cardid)].owner);
533 	if (!from) {
534 		return;
535 	}
536 
537 	to = player_from_id(targetplayer);
538 	if (!to) {
539 		return;
540 	}
541 
542 	cardname = g_strdup_printf("card %d: %s", cardid, currentgame->card[ get_card_slot_with_cardid(cardid)  ].title);
543 
544 	gtk_list_store_append(store, &iter);
545 	gtk_list_store_set(store, &iter,
546 	  TRADEPROPOSALLIST_COLUMN_TYPE,"CARD:",
547 	  TRADEPROPOSALLIST_COLUMN_FROM, from->name,
548 	  TRADEPROPOSALLIST_COLUMN_GIVES, "gives",
549 	  TRADEPROPOSALLIST_COLUMN_TO, to->name,
550 	  TRADEPROPOSALLIST_COLUMN_WHAT, cardname,
551 	  TRADEPROPOSALLIST_COLUMN_TYPE_ID, TRADE_TYPE_CARD,
552 	  TRADEPROPOSALLIST_COLUMN_FROM_ID, currentgame->card[ get_card_slot_with_cardid(cardid)  ].owner,
553 	  TRADEPROPOSALLIST_COLUMN_TO_ID, targetplayer,
554 	  TRADEPROPOSALLIST_COLUMN_AUX_ID, cardid,
555 	  -1);
556 
557 	g_free(cardname);
558 }
559 
560 
trade_update_estate(gint32 tradeid,gint32 estateid,gint32 targetplayer)561 void trade_update_estate(gint32 tradeid, gint32 estateid, gint32 targetplayer)  {
562 
563 	gint32 tradeslot;
564 	GtkWidget *ProposalList;
565 	player *from, *to;
566 	gchar *estatename;
567 	GtkListStore *store;
568 	GtkTreeIter iter;
569 	gboolean valid;
570 
571 	tradeslot = get_trade_slot_with_tradeid(tradeid);
572 	if (tradeslot < 0) return;
573 
574 	ProposalList = currentgame->trade[tradeslot].ProposalList;
575 	estatename = currentgame->estate[estateid].name;
576 	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ProposalList)));
577 
578 	/* remove previous estate */
579 	valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
580 	while (valid) {
581 		gint32 typeid, estate;
582 		GtkTreeIter curiter = iter;
583 
584 		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); /* get next iter so we can remove the current entry safely */
585 		gtk_tree_model_get(GTK_TREE_MODEL(store), &curiter,
586 		  TRADEPROPOSALLIST_COLUMN_TYPE_ID, &typeid,
587 		  TRADEPROPOSALLIST_COLUMN_AUX_ID, &estate,
588 		  -1);
589 
590 		if ( typeid == TRADE_TYPE_ESTATE  &&  estate == estateid ) {
591 			gtk_list_store_remove(store, &curiter);
592 		}
593 	}
594 
595 	if (targetplayer < 0) {
596 		return;
597 	}
598 
599 	from = player_from_id(currentgame->estate[estateid].owner);
600 	if (!from) {
601 		return;
602 	}
603 
604 	to = player_from_id(targetplayer);
605 	if (!to) {
606 		return;
607 	}
608 
609 	gtk_list_store_append(store, &iter);
610 	gtk_list_store_set(store, &iter,
611 	  TRADEPROPOSALLIST_COLUMN_TYPE,"ESTATE:",
612 	  TRADEPROPOSALLIST_COLUMN_FROM, from->name,
613 	  TRADEPROPOSALLIST_COLUMN_GIVES, "gives",
614 	  TRADEPROPOSALLIST_COLUMN_TO, to->name,
615 	  TRADEPROPOSALLIST_COLUMN_WHAT, estatename,
616 	  TRADEPROPOSALLIST_COLUMN_TYPE_ID, TRADE_TYPE_ESTATE,
617 	  TRADEPROPOSALLIST_COLUMN_FROM_ID, currentgame->estate[estateid].owner,
618 	  TRADEPROPOSALLIST_COLUMN_TO_ID, targetplayer,
619 	  TRADEPROPOSALLIST_COLUMN_AUX_ID, estateid,
620 	  -1);
621 }
622 
623 
trade_update_money(gint32 tradeid,gint32 playerfrom,gint32 playerto,gint32 money)624 void trade_update_money(gint32 tradeid, gint32 playerfrom, gint32 playerto, gint32 money)  {
625 
626 	gint32 tradeslot;
627 	GtkWidget *ProposalList;
628 	player *from, *to;
629 	gchar *moneystr;
630 	GtkListStore *store;
631 	GtkTreeIter iter;
632 	gboolean valid;
633 
634 	tradeslot = get_trade_slot_with_tradeid(tradeid);
635 	if (tradeslot < 0) return;
636 
637 	ProposalList = currentgame->trade[tradeslot].ProposalList;
638 	store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ProposalList)));
639 
640 	/* remove all money proposal if same playerfrom and same playerto */
641 	valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
642 	while (valid) {
643 		gint32 typeid, fromid, toid;
644 		GtkTreeIter curiter = iter;
645 
646 		valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter); /* get next iter so we can remove the current entry safely */
647 		gtk_tree_model_get(GTK_TREE_MODEL(store), &curiter,
648 		  TRADEPROPOSALLIST_COLUMN_TYPE_ID, &typeid,
649 		  TRADEPROPOSALLIST_COLUMN_FROM_ID, &fromid,
650 		  TRADEPROPOSALLIST_COLUMN_TO_ID, &toid,
651 		  -1);
652 
653 		if ( typeid == TRADE_TYPE_MONEY  &&  fromid == playerfrom  &&  toid == playerto ) {
654 			gtk_list_store_remove(store, &curiter);
655 		}
656 	}
657 
658 	if (money <= 0) {
659 		return;
660 	}
661 
662 	from = player_from_id(playerfrom);
663 	if (!from) {
664 		return;
665 	}
666 
667 	to = player_from_id(playerto);
668 	if (!to) {
669 		return;
670 	}
671 
672 	moneystr = g_strdup_printf("%d money", money);
673 
674 	gtk_list_store_append(store, &iter);
675 	gtk_list_store_set(store, &iter,
676 	  TRADEPROPOSALLIST_COLUMN_TYPE,"MONEY:",
677 	  TRADEPROPOSALLIST_COLUMN_FROM, from->name,
678 	  TRADEPROPOSALLIST_COLUMN_GIVES, "gives",
679 	  TRADEPROPOSALLIST_COLUMN_TO, to->name,
680 	  TRADEPROPOSALLIST_COLUMN_WHAT, moneystr,
681 	  TRADEPROPOSALLIST_COLUMN_TYPE_ID, TRADE_TYPE_MONEY,
682 	  TRADEPROPOSALLIST_COLUMN_FROM_ID, playerfrom,
683 	  TRADEPROPOSALLIST_COLUMN_TO_ID, playerto,
684 	  TRADEPROPOSALLIST_COLUMN_AUX_ID, 0,
685 	  -1);
686 
687 	g_free(moneystr);
688 }
689