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 
22 #include <gtk/gtk.h>
23 #include <gdk/gdkkeysyms.h>
24 
25 /* utility */
26 #include "log.h"
27 #include "shared.h"
28 #include "string_vector.h"
29 #include "support.h"
30 
31 /* common */
32 #include "events.h"
33 #include "fcintl.h"
34 #include "government.h"
35 #include "multipliers.h"
36 
37 /* client */
38 #include "client_main.h"
39 #include "options.h"
40 
41 /* client/gui-gtk-3.22 */
42 #include "chatline.h"
43 #include "cityrep.h"
44 #include "dialogs.h"
45 #include "gui_main.h"
46 #include "gui_stuff.h"
47 #include "ratesdlg.h"
48 
49 #include "gamedlgs.h"
50 
51 /******************************************************************/
52 static GtkWidget *rates_dialog_shell;
53 static GtkWidget *rates_gov_label;
54 static GtkWidget *rates_tax_toggle, *rates_lux_toggle, *rates_sci_toggle;
55 static GtkWidget *rates_tax_label, *rates_lux_label, *rates_sci_label;
56 static GtkWidget *rates_tax_scale, *rates_lux_scale, *rates_sci_scale;
57 
58 static GtkWidget *multiplier_dialog_shell;
59 static GtkWidget *multipliers_scale[MAX_NUM_MULTIPLIERS];
60 
61 static gulong     rates_tax_sig, rates_lux_sig, rates_sci_sig;
62 /******************************************************************/
63 
64 static int rates_tax_value, rates_lux_value, rates_sci_value;
65 
66 static void rates_changed_callback(GtkWidget *range);
67 
68 /**************************************************************************
69   Set tax values to display
70 **************************************************************************/
rates_set_values(int tax,int no_tax_scroll,int lux,int no_lux_scroll,int sci,int no_sci_scroll)71 static void rates_set_values(int tax, int no_tax_scroll,
72                              int lux, int no_lux_scroll,
73                              int sci, int no_sci_scroll)
74 {
75   char buf[64];
76   int tax_lock, lux_lock, sci_lock;
77   int maxrate;
78 
79   tax_lock = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rates_tax_toggle));
80   lux_lock = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rates_lux_toggle));
81   sci_lock = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rates_sci_toggle));
82 
83   if (NULL != client.conn.playing) {
84     maxrate = get_player_bonus(client.conn.playing, EFT_MAX_RATES);
85   } else {
86     maxrate = 100;
87   }
88 
89   /* This's quite a simple-minded "double check".. */
90   tax = MIN(tax, maxrate);
91   lux = MIN(lux, maxrate);
92   sci = MIN(sci, maxrate);
93 
94   if (tax + sci + lux != 100) {
95     if (tax != rates_tax_value) {
96       if (!lux_lock) {
97         lux = MIN(MAX(100 - tax - sci, 0), maxrate);
98       }
99       if (!sci_lock) {
100         sci = MIN(MAX(100 - tax - lux, 0), maxrate);
101       }
102     } else if (lux != rates_lux_value) {
103       if (!tax_lock) {
104         tax = MIN(MAX(100 - lux - sci, 0), maxrate);
105       }
106       if (!sci_lock) {
107         sci = MIN(MAX(100 - lux - tax, 0), maxrate);
108       }
109     } else if (sci != rates_sci_value) {
110       if (!lux_lock) {
111         lux = MIN(MAX(100 - tax - sci, 0), maxrate);
112       }
113       if (!tax_lock) {
114         tax = MIN(MAX(100 - lux - sci, 0), maxrate);
115       }
116     }
117 
118     if (tax + sci + lux != 100) {
119       tax = rates_tax_value;
120       lux = rates_lux_value;
121       sci = rates_sci_value;
122 
123       rates_tax_value = -1;
124       rates_lux_value = -1;
125       rates_sci_value = -1;
126 
127       no_tax_scroll = 0;
128       no_lux_scroll = 0;
129       no_sci_scroll = 0;
130     }
131   }
132 
133   if (tax != rates_tax_value) {
134     fc_snprintf(buf, sizeof(buf), "%3d%%", tax);
135 
136     if (strcmp(buf, gtk_label_get_text(GTK_LABEL(rates_tax_label))) != 0) {
137       gtk_label_set_text(GTK_LABEL(rates_tax_label), buf);
138     }
139     if (!no_tax_scroll) {
140       g_signal_handler_block(rates_tax_scale, rates_tax_sig);
141       gtk_range_set_value(GTK_RANGE(rates_tax_scale), tax / 10);
142       g_signal_handler_unblock(rates_tax_scale, rates_tax_sig);
143     }
144     rates_tax_value = tax;
145   }
146 
147   if (lux != rates_lux_value) {
148     fc_snprintf(buf, sizeof(buf), "%3d%%", lux);
149 
150     if (strcmp(buf, gtk_label_get_text(GTK_LABEL(rates_lux_label))) != 0) {
151       gtk_label_set_text(GTK_LABEL(rates_lux_label), buf);
152     }
153     if (!no_lux_scroll) {
154       g_signal_handler_block(rates_lux_scale, rates_lux_sig);
155       gtk_range_set_value(GTK_RANGE(rates_lux_scale), lux / 10);
156       g_signal_handler_unblock(rates_lux_scale, rates_lux_sig);
157     }
158     rates_lux_value = lux;
159   }
160 
161   if (sci != rates_sci_value) {
162     fc_snprintf(buf, sizeof(buf), "%3d%%", sci);
163 
164     if (strcmp(buf, gtk_label_get_text(GTK_LABEL(rates_sci_label))) != 0) {
165       gtk_label_set_text(GTK_LABEL(rates_sci_label), buf);
166     }
167     if (!no_sci_scroll) {
168       g_signal_handler_block(rates_sci_scale, rates_sci_sig);
169       gtk_range_set_value(GTK_RANGE(rates_sci_scale), sci / 10);
170       g_signal_handler_unblock(rates_sci_scale, rates_sci_sig);
171     }
172     rates_sci_value = sci;
173   }
174 }
175 
176 /**************************************************************************
177   User changes rates
178 **************************************************************************/
rates_changed_callback(GtkWidget * range)179 static void rates_changed_callback(GtkWidget *range)
180 {
181   int percent = gtk_range_get_value(GTK_RANGE(range));
182 
183   if (range == rates_tax_scale) {
184     int tax_value;
185 
186     tax_value = 10 * percent;
187     tax_value = MIN(tax_value, 100);
188     rates_set_values(tax_value, 1, rates_lux_value, 0, rates_sci_value, 0);
189   } else if (range == rates_lux_scale) {
190     int lux_value;
191 
192     lux_value = 10 * percent;
193     lux_value = MIN(lux_value, 100);
194     rates_set_values(rates_tax_value, 0, lux_value, 1, rates_sci_value, 0);
195   } else {
196     int sci_value;
197 
198     sci_value = 10 * percent;
199     sci_value = MIN(sci_value, 100);
200     rates_set_values(rates_tax_value, 0, rates_lux_value, 0, sci_value, 1);
201   }
202 }
203 
204 /**************************************************************************
205   User has responded to rates dialog
206 **************************************************************************/
rates_command_callback(GtkWidget * w,gint response_id)207 static void rates_command_callback(GtkWidget *w, gint response_id)
208 {
209   if (response_id == GTK_RESPONSE_OK) {
210     dsend_packet_player_rates(&client.conn, rates_tax_value, rates_lux_value,
211                               rates_sci_value);
212   }
213   gtk_widget_destroy(rates_dialog_shell);
214 }
215 
216 /**************************************************************************
217   Convert real multiplier display value to scale value
218 **************************************************************************/
mult_to_scale(const struct multiplier * pmul,int val)219 static int mult_to_scale(const struct multiplier *pmul, int val)
220 {
221   return (val - pmul->start) / pmul->step;
222 }
223 
224 /**************************************************************************
225   Convert scale units to real multiplier display value
226 **************************************************************************/
scale_to_mult(const struct multiplier * pmul,int scale)227 static int scale_to_mult(const struct multiplier *pmul, int scale)
228 {
229   return scale * pmul->step + pmul->start;
230 }
231 
232 /**************************************************************************
233   Format value for multiplier scales
234 **************************************************************************/
multiplier_value_callback(GtkScale * scale,gdouble value,void * udata)235 static gchar *multiplier_value_callback(GtkScale *scale, gdouble value,
236                                         void *udata)
237 {
238   const struct multiplier *pmul = udata;
239 
240   return g_strdup_printf("%d", scale_to_mult(pmul, value));
241 }
242 
243 /**************************************************************************
244   User has responded to multipliers dialog
245 **************************************************************************/
multipliers_command_callback(GtkWidget * w,gint response_id)246 static void multipliers_command_callback(GtkWidget *w, gint response_id)
247 {
248   struct packet_player_multiplier mul;
249 
250   if (response_id == GTK_RESPONSE_OK && can_client_issue_orders()) {
251     multipliers_iterate(m) {
252       Multiplier_type_id i = multiplier_index(m);
253       int value = gtk_range_get_value(GTK_RANGE(multipliers_scale[i]));
254 
255       mul.multipliers[i] = scale_to_mult(m, value);
256     } multipliers_iterate_end;
257     mul.count = multiplier_count();
258     send_packet_player_multiplier(&client.conn, &mul);
259   }
260   gtk_widget_destroy(multiplier_dialog_shell);
261 }
262 
263 /**************************************************************************
264   Update values in multipliers dialog
265 **************************************************************************/
multiplier_dialog_update_values(bool set_positions)266 static void multiplier_dialog_update_values(bool set_positions)
267 {
268   multipliers_iterate(pmul) {
269     Multiplier_type_id multiplier = multiplier_index(pmul);
270     int val = player_multiplier_value(client_player(), pmul);
271     int target = player_multiplier_target_value(client_player(), pmul);
272 
273     fc_assert(multiplier < ARRAY_SIZE(multipliers_scale));
274     fc_assert(scale_to_mult(pmul, mult_to_scale(pmul, val)) == val);
275     fc_assert(scale_to_mult(pmul, mult_to_scale(pmul, target)) == target);
276 
277     gtk_scale_clear_marks(GTK_SCALE(multipliers_scale[multiplier]));
278     gtk_scale_add_mark(GTK_SCALE(multipliers_scale[multiplier]),
279                        mult_to_scale(pmul, val), GTK_POS_BOTTOM,
280                        /* TRANS: current value of policy in force */
281                        Q_("?multiplier:Now"));
282 
283     if (set_positions) {
284       gtk_range_set_value(GTK_RANGE(multipliers_scale[multiplier]),
285                           mult_to_scale(pmul, target));
286     }
287   } multipliers_iterate_end;
288 }
289 
290 /**************************************************************************
291   Callback when server indicates multiplier values have changed
292 **************************************************************************/
real_multipliers_dialog_update(void * unused)293 void real_multipliers_dialog_update(void *unused)
294 {
295   if (!multiplier_dialog_shell) {
296     return;
297   }
298 
299   /* If dialog belongs to a player rather than an observer, we don't
300    * want to lose any local changes made by the player.
301    * This dialog should be the only way the values can change, anyway. */
302   multiplier_dialog_update_values(!can_client_issue_orders());
303 }
304 
305 /****************************************************************
306   Create multipliers dialog
307 ****************************************************************/
create_multiplier_dialog(void)308 static GtkWidget *create_multiplier_dialog(void)
309 {
310   GtkWidget     *shell, *content;
311   GtkWidget     *label, *scale;
312 
313   if (can_client_issue_orders()) {
314     shell = gtk_dialog_new_with_buttons(_("Change policies"),
315                                         NULL,
316                                         0,
317                                         _("_Cancel"),
318                                         GTK_RESPONSE_CANCEL,
319                                         _("_OK"),
320                                         GTK_RESPONSE_OK,
321                                         NULL);
322   } else {
323     shell = gtk_dialog_new_with_buttons(_("Policies"),
324                                         NULL,
325                                         0,
326                                         _("_Close"),
327                                         GTK_RESPONSE_CLOSE,
328                                         NULL);
329   }
330   setup_dialog(shell, toplevel);
331 
332   gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_MOUSE);
333   content = gtk_dialog_get_content_area(GTK_DIALOG(shell));
334 
335   if (can_client_issue_orders()) {
336     label = gtk_label_new(_("Changes will not take effect until next turn."));
337     gtk_box_pack_start( GTK_BOX( content ), label, FALSE, FALSE, 0);
338   }
339 
340   multipliers_iterate(pmul) {
341     Multiplier_type_id multiplier = multiplier_index(pmul);
342 
343     fc_assert(multiplier < ARRAY_SIZE(multipliers_scale));
344     label = gtk_label_new(multiplier_name_translation(pmul));
345     /* Map each multiplier step to a single step on the UI, to enforce
346      * the step size. */
347     scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL,
348                                      mult_to_scale(pmul, pmul->start),
349                                      mult_to_scale(pmul, pmul->stop), 1);
350     multipliers_scale[multiplier] = scale;
351     gtk_range_set_increments(GTK_RANGE(multipliers_scale[multiplier]),
352                              1, MAX(2, mult_to_scale(pmul, pmul->stop) / 10));
353     g_signal_connect(multipliers_scale[multiplier], "format-value",
354                      G_CALLBACK(multiplier_value_callback), pmul);
355     g_signal_connect(multipliers_scale[multiplier], "destroy",
356                      G_CALLBACK(gtk_widget_destroyed),
357                      &multipliers_scale[multiplier]);
358     gtk_box_pack_start( GTK_BOX( content ), label, TRUE, TRUE, 5 );
359     gtk_box_pack_start( GTK_BOX( content ), scale, TRUE, TRUE, 5 );
360     gtk_widget_set_sensitive(multipliers_scale[multiplier],
361                              can_client_issue_orders());
362   } multipliers_iterate_end;
363 
364   multiplier_dialog_update_values(TRUE);
365 
366   g_signal_connect(shell, "destroy",
367                    G_CALLBACK(gtk_widget_destroyed), &multiplier_dialog_shell);
368 
369   g_signal_connect(shell, "response",
370                    G_CALLBACK(multipliers_command_callback), NULL);
371 
372   gtk_widget_show_all(content);
373 
374   return shell;
375 }
376 
377 /****************************************************************
378   Popup multipliers dialog
379 *****************************************************************/
popup_multiplier_dialog(void)380 void popup_multiplier_dialog(void)
381 {
382   if (!multiplier_dialog_shell) {
383     multiplier_dialog_shell = create_multiplier_dialog();
384   }
385 
386   if (!multiplier_dialog_shell) {
387     return;
388   }
389 
390   gtk_window_present(GTK_WINDOW(multiplier_dialog_shell));
391 }
392 
393 /****************************************************************
394   Create rates dialog
395 *****************************************************************/
create_rates_dialog(void)396 static GtkWidget *create_rates_dialog(void)
397 {
398   GtkWidget     *shell, *content;
399   GtkWidget	*frame, *hgrid;
400   int i;
401 
402   if (!can_client_issue_orders()) {
403     return NULL;
404   }
405 
406   shell = gtk_dialog_new_with_buttons(_("Select tax, luxury and science rates"),
407                                       NULL,
408                                       0,
409                                       _("_Cancel"),
410                                       GTK_RESPONSE_CANCEL,
411                                       _("_OK"),
412                                       GTK_RESPONSE_OK,
413                                       NULL);
414   setup_dialog(shell, toplevel);
415   gtk_dialog_set_default_response(GTK_DIALOG(shell), GTK_RESPONSE_OK);
416   gtk_window_set_position(GTK_WINDOW(shell), GTK_WIN_POS_MOUSE);
417   content = gtk_dialog_get_content_area(GTK_DIALOG(shell));
418 
419   rates_gov_label = gtk_label_new("");
420   gtk_box_pack_start( GTK_BOX( content ), rates_gov_label, TRUE, TRUE, 5 );
421 
422   frame = gtk_frame_new( _("Tax") );
423   gtk_box_pack_start( GTK_BOX( content ), frame, TRUE, TRUE, 5 );
424 
425   hgrid = gtk_grid_new();
426   gtk_grid_set_column_spacing(GTK_GRID(hgrid), 10);
427   gtk_container_add(GTK_CONTAINER(frame), hgrid);
428 
429   rates_tax_scale =
430     gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 10, 1);
431   gtk_range_set_increments(GTK_RANGE(rates_tax_scale), 1, 1);
432   for (i = 0; i <= 10; i++) {
433     gtk_scale_add_mark(GTK_SCALE(rates_tax_scale), i, GTK_POS_TOP, NULL);
434   }
435   gtk_widget_set_size_request(rates_tax_scale, 300, 40);
436   gtk_scale_set_digits(GTK_SCALE(rates_tax_scale), 0);
437   gtk_scale_set_draw_value(GTK_SCALE(rates_tax_scale), FALSE);
438   gtk_container_add(GTK_CONTAINER(hgrid), rates_tax_scale);
439 
440   rates_tax_label = gtk_label_new("  0%");
441   gtk_container_add(GTK_CONTAINER(hgrid), rates_tax_label);
442   gtk_widget_set_size_request(rates_tax_label, 40, -1);
443 
444   rates_tax_toggle = gtk_check_button_new_with_label( _("Lock") );
445   gtk_container_add(GTK_CONTAINER(hgrid), rates_tax_toggle);
446 
447   frame = gtk_frame_new( _("Luxury") );
448   gtk_box_pack_start( GTK_BOX( content ), frame, TRUE, TRUE, 5 );
449 
450   hgrid = gtk_grid_new();
451   gtk_grid_set_column_spacing(GTK_GRID(hgrid), 10);
452   gtk_container_add(GTK_CONTAINER(frame), hgrid);
453 
454   rates_lux_scale =
455     gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 10, 1);
456   gtk_range_set_increments(GTK_RANGE(rates_lux_scale), 1, 1);
457   for (i = 0; i <= 10; i++) {
458     gtk_scale_add_mark(GTK_SCALE(rates_lux_scale), i, GTK_POS_TOP, NULL);
459   }
460   gtk_widget_set_size_request(rates_lux_scale, 300, 40);
461   gtk_scale_set_digits(GTK_SCALE(rates_lux_scale), 0);
462   gtk_scale_set_draw_value(GTK_SCALE(rates_lux_scale), FALSE);
463   gtk_container_add(GTK_CONTAINER(hgrid), rates_lux_scale);
464 
465   rates_lux_label = gtk_label_new("  0%");
466   gtk_container_add(GTK_CONTAINER(hgrid), rates_lux_label);
467   gtk_widget_set_size_request(rates_lux_label, 40, -1);
468 
469   rates_lux_toggle = gtk_check_button_new_with_label( _("Lock") );
470   gtk_container_add(GTK_CONTAINER(hgrid), rates_lux_toggle);
471 
472   frame = gtk_frame_new( _("Science") );
473   gtk_box_pack_start( GTK_BOX( content ), frame, TRUE, TRUE, 5 );
474 
475   hgrid = gtk_grid_new();
476   gtk_grid_set_column_spacing(GTK_GRID(hgrid), 10);
477   gtk_container_add(GTK_CONTAINER(frame), hgrid);
478 
479   rates_sci_scale =
480     gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, 0, 10, 1);
481   gtk_range_set_increments(GTK_RANGE(rates_sci_scale), 1, 1);
482   for (i = 0; i <= 10; i++) {
483     gtk_scale_add_mark(GTK_SCALE(rates_sci_scale), i, GTK_POS_TOP, NULL);
484   }
485   gtk_widget_set_size_request(rates_sci_scale, 300, 40);
486   gtk_scale_set_digits(GTK_SCALE(rates_sci_scale), 0);
487   gtk_scale_set_draw_value(GTK_SCALE(rates_sci_scale), FALSE);
488   gtk_container_add(GTK_CONTAINER(hgrid), rates_sci_scale);
489 
490   rates_sci_label = gtk_label_new("  0%");
491   gtk_container_add(GTK_CONTAINER(hgrid), rates_sci_label);
492   gtk_widget_set_size_request(rates_sci_label, 40, -1);
493 
494   rates_sci_toggle = gtk_check_button_new_with_label( _("Lock") );
495   gtk_container_add(GTK_CONTAINER(hgrid), rates_sci_toggle);
496 
497 
498   g_signal_connect(shell, "response",
499 		   G_CALLBACK(rates_command_callback), NULL);
500   g_signal_connect(shell, "destroy",
501 		   G_CALLBACK(gtk_widget_destroyed), &rates_dialog_shell);
502 
503   gtk_widget_show_all(shell);
504 
505   rates_tax_value=-1;
506   rates_lux_value=-1;
507   rates_sci_value=-1;
508 
509   rates_tax_sig =
510     g_signal_connect_after(rates_tax_scale, "value-changed",
511 			   G_CALLBACK(rates_changed_callback), NULL);
512 
513   rates_lux_sig =
514     g_signal_connect_after(rates_lux_scale, "value-changed",
515 			   G_CALLBACK(rates_changed_callback), NULL);
516 
517   rates_sci_sig =
518     g_signal_connect_after(rates_sci_scale, "value-changed",
519 			   G_CALLBACK(rates_changed_callback), NULL);
520 
521   rates_set_values(client.conn.playing->economic.tax, 0,
522 		   client.conn.playing->economic.luxury, 0,
523 		   client.conn.playing->economic.science, 0);
524   return shell;
525 }
526 
527 
528 /****************************************************************
529   Popup rates dialog
530 *****************************************************************/
popup_rates_dialog(void)531 void popup_rates_dialog(void)
532 {
533   if (!can_client_issue_orders()) {
534     return;
535   }
536 
537   if (!rates_dialog_shell) {
538     rates_dialog_shell = create_rates_dialog();
539   }
540   if (!rates_dialog_shell) {
541     return;
542   }
543 
544   gchar *buf = g_strdup_printf(_("%s max rate: %d%%"),
545       government_name_for_player(client.conn.playing),
546       get_player_bonus(client.conn.playing, EFT_MAX_RATES));
547   gtk_label_set_text(GTK_LABEL(rates_gov_label), buf);
548   g_free(buf);
549   gtk_range_set_fill_level(GTK_RANGE(rates_tax_scale),
550                            get_player_bonus(client.conn.playing,
551                                             EFT_MAX_RATES)/10);
552   gtk_range_set_fill_level(GTK_RANGE(rates_lux_scale),
553                            get_player_bonus(client.conn.playing,
554                                             EFT_MAX_RATES)/10);
555   gtk_range_set_fill_level(GTK_RANGE(rates_sci_scale),
556                            get_player_bonus(client.conn.playing,
557                                             EFT_MAX_RATES)/10);
558 
559   gtk_window_present(GTK_WINDOW(rates_dialog_shell));
560 }
561