Freeciv-3.1
Loading...
Searching...
No Matches
wldlg.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 <stdlib.h>
19#include <string.h>
20
21#include <gtk/gtk.h>
22#include <gdk/gdkkeysyms.h>
23
24/* utility */
25#include "fcintl.h"
26#include "log.h"
27#include "mem.h"
28#include "support.h"
29
30/* common */
31#include "city.h"
32#include "packets.h"
33#include "worklist.h"
34
35/* client */
36#include "citydlg_common.h"
37#include "client_main.h"
38#include "climisc.h"
39#include "global_worklist.h"
40#include "options.h"
41#include "text.h"
42#include "tilespec.h"
43
44/* client/gui-gtk-4.0 */
45#include "canvas.h"
46#include "citydlg.h"
47#include "graphics.h"
48#include "gui_main.h"
49#include "gui_stuff.h"
50#include "helpdlg.h"
51#include "inputdlg.h"
52
53#include "wldlg.h"
54
55static GtkWidget *worklists_shell;
56static GtkWidget *worklists_list;
57
58enum {
63};
64
65static GtkListStore *worklists_store;
66
67static int max_unit_height = -1, max_unit_width = -1;
68
69static void reset_global_worklist(GtkWidget *editor,
70 struct global_worklist *pgwl);
71static void popup_worklist(struct global_worklist *pgwl);
72static void popdown_worklist(struct global_worklist *pgwl);
73static void dst_row_callback(GtkTreeView *view, GtkTreePath *path,
74 GtkTreeViewColumn *col, gpointer data);
75
76/************************************************************************/
80{
81 max_unit_height = -1;
82 max_unit_width = -1;
83}
84
85/************************************************************************/
88static void update_max_unit_size(void)
89{
92
94 int x1, x2, y1, y2;
96 direction8_invalid());
97
98 sprite_get_bounding_box(sprite, &x1, &y1, &x2, &y2);
102}
103
104/************************************************************************/
107static void worklists_destroy_callback(GtkWidget *w, gpointer data)
108{
109 worklists_shell = NULL;
110}
111
112/************************************************************************/
116{
117 GtkTreeIter it;
118
119 gtk_list_store_clear(worklists_store);
121 gtk_list_store_append(worklists_store, &it);
122
123 gtk_list_store_set(worklists_store, &it,
124 0, global_worklist_name(pgwl),
125 1, global_worklist_id(pgwl),
126 -1);
128
130}
131
132/************************************************************************/
135static void worklists_response(GtkWidget *w, gint response)
136{
137 struct global_worklist *pgwl;
138 int id;
139 GtkTreeSelection *selection;
140 GtkTreeModel *model;
141 GtkTreeIter it;
142
143 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(worklists_list));
144
145 if (gtk_tree_selection_get_selected(selection, &model, &it)) {
146 gtk_tree_model_get(model, &it, 1, &id, -1);
147 pgwl = global_worklist_by_id(id);
148 } else {
149 pgwl = NULL;
150 id = -1;
151 }
152
153 switch (response) {
154 case WORKLISTS_NEW:
155 global_worklist_new(_("new"));
157 return;
158
159 case WORKLISTS_DELETE:
160 if (!pgwl) {
161 return;
162 }
163
164 popdown_worklist(pgwl);
167 return;
168
170 if (!pgwl) {
171 return;
172 }
173
174 popup_worklist(pgwl);
175 return;
176
177 default:
178 gtk_window_destroy(GTK_WINDOW(worklists_shell));
179 return;
180 }
181}
182
183/************************************************************************/
186static void cell_edited(GtkCellRendererText *cell,
187 const gchar *spath,
188 const gchar *text, gpointer data)
189{
190 GtkTreePath *path;
191 GtkTreeIter it;
192 struct global_worklist *pgwl;
193 int id;
194
195 path = gtk_tree_path_new_from_string(spath);
196 gtk_tree_model_get_iter(GTK_TREE_MODEL(worklists_store), &it, path);
197 gtk_tree_path_free(path);
198
199 gtk_tree_model_get(GTK_TREE_MODEL(worklists_store), &it, 1, &id, -1);
200 pgwl = global_worklist_by_id(id);
201
202 if (!pgwl) {
203 gtk_list_store_remove(worklists_store, &it);
204 return;
205 }
206
207 global_worklist_set_name(pgwl, text);
208 gtk_list_store_set(worklists_store, &it, 0, text, -1);
209
211}
212
213/************************************************************************/
216static GtkWidget *create_worklists_report(void)
217{
218 GtkWidget *shell, *list;
219 GtkWidget *vgrid, *label, *sw;
220 GtkCellRenderer *rend;
221 int grid_row = 0;
222
223 shell = gtk_dialog_new_with_buttons(_("Edit worklists"),
224 NULL,
225 0,
226 _("_New"),
228 _("_Delete"),
230 _("_Properties"),
232 _("_Close"),
234 NULL);
236
237 g_signal_connect(shell, "response",
238 G_CALLBACK(worklists_response), NULL);
239 g_signal_connect(shell, "destroy",
240 G_CALLBACK(worklists_destroy_callback), NULL);
241
242 vgrid = gtk_grid_new();
243 gtk_grid_set_row_spacing(GTK_GRID(vgrid), 2);
244 gtk_orientable_set_orientation(GTK_ORIENTABLE(vgrid),
245 GTK_ORIENTATION_VERTICAL);
246 gtk_box_append(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(shell))), vgrid);
247
248 worklists_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_INT);
249
250 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(worklists_store));
251 gtk_widget_set_hexpand(list, TRUE);
252 gtk_widget_set_vexpand(list, TRUE);
253
254 g_object_unref(worklists_store);
255 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), FALSE);
256
258
259 rend = gtk_cell_renderer_text_new();
260 g_object_set(rend, "editable", TRUE, NULL);
261 g_signal_connect(rend, "edited",
262 G_CALLBACK(cell_edited), NULL);
263 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list), -1, NULL,
264 rend, "text", 0, NULL);
265
266 sw = gtk_scrolled_window_new();
267 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw), 200);
268 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
269 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
270 GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
271 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), list);
272
273 label = g_object_new(GTK_TYPE_LABEL,
274 "use-underline", TRUE,
275 "mnemonic-widget", list,
276 "label", _("_Worklists:"),
277 "xalign", 0.0, "yalign", 0.5, NULL);
278
279 gtk_grid_attach(GTK_GRID(vgrid), label, 0, grid_row++, 1, 1);
280 gtk_grid_attach(GTK_GRID(vgrid), sw, 0, grid_row++, 1, 1);
281 gtk_widget_show(vgrid);
282
283 return shell;
284}
285
286/************************************************************************/
290{
291 if (worklists_shell == NULL) {
293
295 }
296
297 gtk_window_present(GTK_WINDOW(worklists_shell));
298}
299
300/****************************************************************
301 ...
302*****************************************************************/
303struct worklist_data {
305 struct city *pcity;
306
307 GtkWidget *editor;
308
309 GtkListStore *src, *dst;
310 GtkWidget *src_view, *dst_view;
311 GMenu *menu;
313 GActionGroup *group;
314 GtkTreeSelection *src_selection, *dst_selection;
315
316 GtkTreeViewColumn *src_col, *dst_col;
317
318 GtkWidget *change_cmd, *help_cmd;
320
321 bool future;
322};
323
324static GHashTable *hash;
325
326static void commit_worklist(struct worklist_data *ptr);
327
328/************************************************************************/
331void add_worklist_dnd_target(GtkWidget *w,
332 gboolean (drag_drop_cb)
333 (GtkDropTarget *target, const GValue *value,
334 double x, double y, gpointer data),
335 gpointer data)
336{
337 GtkDropTarget *dnd_tgt;
338
339 dnd_tgt = gtk_drop_target_new(G_TYPE_INT, GDK_ACTION_COPY);
340
341 g_signal_connect(dnd_tgt, "drop", G_CALLBACK(drag_drop_cb), data);
342
343 gtk_widget_add_controller(w, GTK_EVENT_CONTROLLER(dnd_tgt));
344}
345
346/************************************************************************/
349static GtkWidget *get_worklist(int global_worklist_id)
350{
351 if (hash) {
352 gpointer ret;
353
354 ret = g_hash_table_lookup(hash, GINT_TO_POINTER(global_worklist_id));
355 return ret;
356 } else {
357 return NULL;
358 }
359}
360
361/************************************************************************/
364static void insert_worklist(int global_worklist_id, GtkWidget *editor)
365{
366 if (!hash) {
367 hash = g_hash_table_new(g_direct_hash, g_direct_equal);
368 }
369 g_hash_table_insert(hash, GINT_TO_POINTER(global_worklist_id), editor);
370}
371
372/************************************************************************/
376{
377 if (hash) {
378 g_hash_table_remove(hash, GINT_TO_POINTER(global_worklist_id));
379 }
380}
381
382/************************************************************************/
385static void popup_worklist(struct global_worklist *pgwl)
386{
387 GtkWidget *shell;
388
389 if (!(shell = get_worklist(global_worklist_id(pgwl)))) {
390 GtkWidget *editor;
391
392 shell = gtk_dialog_new_with_buttons(global_worklist_name(pgwl),
393 GTK_WINDOW(worklists_shell),
394 GTK_DIALOG_DESTROY_WITH_PARENT,
395 _("_Close"),
396 GTK_RESPONSE_CLOSE,
397 NULL);
398 g_signal_connect(shell, "response", G_CALLBACK(gtk_window_destroy), NULL);
399 gtk_window_set_default_size(GTK_WINDOW(shell), 500, 400);
400
404
405 gtk_box_append(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(shell))), editor);
406 gtk_widget_show(editor);
407
409 }
410
411 gtk_window_present(GTK_WINDOW(shell));
412}
413
414/************************************************************************/
417static void popdown_worklist(struct global_worklist *pgwl)
418{
419 GtkWidget *shell;
420
421 if ((shell = get_worklist(global_worklist_id(pgwl)))) {
422 GtkWidget *parent;
423
424 parent = gtk_widget_get_ancestor(shell, GTK_TYPE_WINDOW);
425 gtk_window_destroy(GTK_WINDOW(parent));
426 }
427}
428
429/************************************************************************/
432static void worklist_destroy(GtkWidget *editor, gpointer data)
433{
434 struct worklist_data *ptr;
435
436 ptr = data;
437
438 if (ptr->global_worklist_id != -1) {
440 }
441
442 free(ptr);
443}
444
445/************************************************************************/
448static void menu_item_callback(GSimpleAction *action, GVariant *parameter,
449 gpointer data)
450{
451 struct global_worklist *pgwl;
452 const struct worklist *pwl;
453 struct worklist_data *ptr = (struct worklist_data *)data;
454 size_t i;
455
456 if (NULL == client.conn.playing) {
457 return;
458 }
459
460 pgwl = global_worklist_by_id(GPOINTER_TO_INT
461 (g_object_get_data(G_OBJECT(action), "id")));
462 if (pgwl == NULL) {
463 return;
464 }
465
466 pwl = global_worklist_get(pgwl);
467
468 for (i = 0; i < (size_t) worklist_length(pwl); i++) {
469 GtkTreeIter it;
470 cid id;
471 char buf[8192];
472
473 id = cid_encode(pwl->entries[i]);
474
475 gtk_list_store_append(ptr->dst, &it);
476 gtk_list_store_set(ptr->dst, &it, 0, (gint)id,
477 1, production_help(&(pwl->entries[i]),
478 buf, sizeof(buf)), -1);
479 }
480
481 commit_worklist(ptr);
482}
483
484/************************************************************************/
487static GMenu *create_wl_menu(struct worklist_data *ptr)
488{
489 GSimpleAction *act;
490 int current_size = 0;
491
492 if (ptr->menu == NULL) {
493 ptr->menu = g_menu_new();
494 ptr->menu_size = 0;
495 }
496
498 int id = global_worklist_id(pgwl);
499 char act_name[60];
500
501 fc_snprintf(act_name, sizeof(act_name), "wl%d", id);
502 act = g_simple_action_new(act_name, NULL);
503
504 g_object_set_data(G_OBJECT(act), "id",
505 GINT_TO_POINTER(global_worklist_id(pgwl)));
506 g_action_map_add_action(G_ACTION_MAP(ptr->group), G_ACTION(act));
507 g_signal_connect(act, "activate", G_CALLBACK(menu_item_callback), ptr);
508
509 fc_snprintf(act_name, sizeof(act_name), "win.wl%d", id);
510
511 if (ptr->menu_size > current_size) {
512 g_menu_remove(ptr->menu, current_size);
513 }
514 menu_item_insert_unref(ptr->menu, current_size++,
515 g_menu_item_new(global_worklist_name(pgwl), act_name));
517
518 act = g_simple_action_new("wledit", NULL);
519 g_action_map_add_action(G_ACTION_MAP(ptr->group), G_ACTION(act));
520 g_signal_connect(act, "activate",
521 G_CALLBACK(popup_worklists_report), NULL);
522
523 if (ptr->menu_size > current_size) {
524 g_menu_remove(ptr->menu, current_size);
525 }
526
527 menu_item_insert_unref(ptr->menu, current_size++,
528 g_menu_item_new(_("Edit Global _Worklists"), "win.wledit"));
529
530 if (ptr->menu_size < current_size) {
531 ptr->menu_size = current_size;
532 } else {
533 while (ptr->menu_size > current_size) {
534 g_menu_remove(ptr->menu, --ptr->menu_size);
535 }
536 }
537
538 return ptr->menu;
539}
540
541/************************************************************************/
544static void wl_help_from_iter(GtkTreeModel *model, GtkTreeIter *it)
545{
546 gint id;
547 struct universal target;
548
549 gtk_tree_model_get(model, it, 0, &id, -1);
550 target = cid_decode(id);
551
552 if (VUT_UTYPE == target.kind) {
554 HELP_UNIT);
555 } else if (is_great_wonder(target.value.building)) {
558 } else {
561 }
562}
563
564/************************************************************************/
567static void help_callback(GtkWidget *w, gpointer data)
568{
569 struct worklist_data *ptr;
570 GtkTreeSelection *selection;
571 GtkTreeModel *model;
572 GtkTreeIter it;
573
574 ptr = data;
575 selection = ptr->src_selection;
576
577 if (gtk_tree_selection_get_selected(selection, &model, &it)) {
578 wl_help_from_iter(model, &it);
579 } else {
581 }
582}
583
584/************************************************************************/
587static void change_callback(GtkWidget *w, gpointer data)
588{
589 struct worklist_data *ptr;
590 GtkTreeSelection *selection;
591 GtkTreeModel *model;
592 GtkTreeIter it;
593
594 ptr = data;
595 selection = ptr->src_selection;
596
597 if (gtk_tree_selection_get_selected(selection, &model, &it)) {
598 gint id;
599 struct universal univ;
600
601 gtk_tree_model_get(model, &it, 0, &id, -1);
602 univ = cid_production(id);
603 city_change_production(ptr->pcity, &univ);
604 }
605}
606
607/************************************************************************/
610static void future_callback(GtkToggleButton *toggle, gpointer data)
611{
612 struct worklist_data *ptr;
613
614 ptr = data;
615 ptr->future = !ptr->future;
616
618}
619
620/************************************************************************/
623static void queue_bubble_up(struct worklist_data *ptr)
624{
625 GtkTreePath *path;
626 GtkTreeViewColumn *col;
627 GtkTreeModel *model;
628
629 if (!gtk_widget_is_sensitive(ptr->dst_view)) {
630 return;
631 }
632
633 model = GTK_TREE_MODEL(ptr->dst);
634 gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr->dst_view), &path, &col);
635 if (path) {
636 GtkTreeIter it, it_prev;
637
638 if (gtk_tree_path_prev(path)) {
639 gtk_tree_model_get_iter(model, &it_prev, path);
640 it = it_prev;
641 gtk_tree_model_iter_next(model, &it);
642
643 gtk_list_store_swap(GTK_LIST_STORE(model), &it, &it_prev);
644
645 gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr->dst_view), path, col, FALSE);
646 commit_worklist(ptr);
647 }
648 }
649 gtk_tree_path_free(path);
650}
651
652/************************************************************************/
655static void queue_remove(struct worklist_data *ptr)
656{
657 GtkTreePath *path;
658 GtkTreeViewColumn *col;
659
660 gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr->dst_view), &path, &col);
661 if (path) {
662 dst_row_callback(GTK_TREE_VIEW(ptr->dst_view), path, col, ptr);
663 gtk_tree_path_free(path);
664 }
665}
666
667/************************************************************************/
670static void queue_bubble_down(struct worklist_data *ptr)
671{
672 GtkTreePath *path;
673 GtkTreeViewColumn *col;
674 GtkTreeModel *model;
675
676 if (!gtk_widget_is_sensitive(ptr->dst_view)) {
677 return;
678 }
679
680 model = GTK_TREE_MODEL(ptr->dst);
681 gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr->dst_view), &path, &col);
682 if (path) {
683 GtkTreeIter it, it_next;
684
685 gtk_tree_model_get_iter(model, &it, path);
686 it_next = it;
687 if (gtk_tree_model_iter_next(model, &it_next)) {
688 gtk_list_store_swap(GTK_LIST_STORE(model), &it, &it_next);
689
690 gtk_tree_path_next(path);
691 gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr->dst_view), path, col, FALSE);
692 commit_worklist(ptr);
693 }
694 }
695 gtk_tree_path_free(path);
696}
697
698/************************************************************************/
701static void queue_insert(struct worklist_data *ptr, bool prepend)
702{
703 GtkTreeModel *model;
704 GtkTreeIter it;
705 GtkTreePath *path;
706 GtkTreeModel *src_model, *dst_model;
707 GtkTreeIter src_it, dst_it;
708 gint i, ncols;
709
710 if (!gtk_widget_is_sensitive(ptr->dst_view)) {
711 return;
712 }
713
714 if (!gtk_tree_selection_get_selected(ptr->src_selection, &model, &it)) {
715 return;
716 }
717
718 path = gtk_tree_model_get_path(model, &it);
719
720 src_model = GTK_TREE_MODEL(ptr->src);
721 dst_model = GTK_TREE_MODEL(ptr->dst);
722
723 gtk_tree_model_get_iter(src_model, &src_it, path);
724 if (prepend) {
725 gtk_list_store_prepend(GTK_LIST_STORE(dst_model), &dst_it);
726 } else {
727 gtk_list_store_append(GTK_LIST_STORE(dst_model), &dst_it);
728 }
729
730 ncols = gtk_tree_model_get_n_columns(src_model);
731
732 for (i = 0; i < ncols; i++) {
733 GValue value = { 0, };
734
735 gtk_tree_model_get_value(src_model, &src_it, i, &value);
736 gtk_list_store_set_value(GTK_LIST_STORE(dst_model), &dst_it, i, &value);
737 }
738 commit_worklist(ptr);
739
740 gtk_tree_path_free(path);
741}
742
743/************************************************************************/
746static void queue_prepend(struct worklist_data *ptr)
747{
748 queue_insert(ptr, TRUE);
749}
750
751/************************************************************************/
754static void queue_append(struct worklist_data *ptr)
755{
756 queue_insert(ptr, FALSE);
757}
758
759/************************************************************************/
762static void src_row_callback(GtkTreeView *view, GtkTreePath *path,
763 GtkTreeViewColumn *col, gpointer data)
764{
765 struct worklist_data *ptr;
766 GtkTreeModel *src_model, *dst_model;
767 GtkTreeIter src_it, dst_it;
768 gint i, ncols;
769
770 ptr = data;
771
772 if (!gtk_widget_is_sensitive(ptr->dst_view)) {
773 return;
774 }
775
776 src_model = GTK_TREE_MODEL(ptr->src);
777 dst_model = GTK_TREE_MODEL(ptr->dst);
778
779 gtk_tree_model_get_iter(src_model, &src_it, path);
780 gtk_list_store_append(GTK_LIST_STORE(dst_model), &dst_it);
781
782 ncols = gtk_tree_model_get_n_columns(src_model);
783
784 for (i = 0; i < ncols; i++) {
785 GValue value = { 0, };
786
787 gtk_tree_model_get_value(src_model, &src_it, i, &value);
788 gtk_list_store_set_value(GTK_LIST_STORE(dst_model), &dst_it, i, &value);
789 }
790 commit_worklist(ptr);
791}
792
793/************************************************************************/
796static void dst_row_callback(GtkTreeView *view, GtkTreePath *path,
797 GtkTreeViewColumn *col, gpointer data)
798{
799 struct worklist_data *ptr;
800 GtkTreeModel *dst_model;
801 GtkTreeIter it;
802
803 ptr = data;
804 dst_model = GTK_TREE_MODEL(ptr->dst);
805
806 gtk_tree_model_get_iter(dst_model, &it, path);
807
808 gtk_list_store_remove(GTK_LIST_STORE(dst_model), &it);
809 commit_worklist(ptr);
810}
811
812/************************************************************************/
815static gboolean src_key_press_callback(GtkEventControllerKey *controller,
816 guint keyval, guint keycode,
817 GdkModifierType state, gpointer data)
818{
819 struct worklist_data *ptr;
820
821 ptr = data;
822
823 if (!gtk_widget_is_sensitive(ptr->dst_view)) {
824 return FALSE;
825 }
826
827 if ((state & GDK_SHIFT_MASK) && keyval == GDK_KEY_Insert) {
828 queue_prepend(ptr);
829 return TRUE;
830 } else if (keyval == GDK_KEY_Insert) {
831 queue_append(ptr);
832 return TRUE;
833 } else {
834 return FALSE;
835 }
836}
837
838/************************************************************************/
841static gboolean dst_key_press_callback(GtkEventControllerKey *controller,
842 guint keyval, guint keycode,
843 GdkModifierType state, gpointer data)
844{
845 GtkTreeModel *model;
846 struct worklist_data *ptr;
847
848 ptr = data;
849 model = GTK_TREE_MODEL(ptr->dst);
850
851 if (keyval == GDK_KEY_Delete) {
852 GtkTreeIter it, it_next;
853 bool deleted = FALSE;
854
855 if (gtk_tree_model_get_iter_first(model, &it)) {
856 bool more;
857
858 do {
859 it_next = it;
860 more = gtk_tree_model_iter_next(model, &it_next);
861
862 if (gtk_tree_selection_iter_is_selected(ptr->dst_selection, &it)) {
863 gtk_list_store_remove(GTK_LIST_STORE(model), &it);
864 deleted = TRUE;
865 }
866 it = it_next;
867
868 } while (more);
869 }
870
871 if (deleted) {
872 commit_worklist(ptr);
873 }
874
875 return TRUE;
876 } else if ((state & GDK_ALT_MASK) && keyval == GDK_KEY_Up) {
877 queue_bubble_up(ptr);
878
879 return TRUE;
880 } else if ((state & GDK_ALT_MASK) && keyval == GDK_KEY_Down) {
882
883 return TRUE;
884 } else {
885 return FALSE;
886 }
887}
888
889/************************************************************************/
892static void src_selection_callback(GtkTreeSelection *selection, gpointer data)
893{
894 struct worklist_data *ptr;
895
896 ptr = data;
897
898 /* Update widget sensitivity. */
899 if (gtk_tree_selection_get_selected(selection, NULL, NULL)) {
901 && (!ptr->pcity || city_owner(ptr->pcity) == client.conn.playing)) {
902 /* If ptr->pcity is NULL, this is a global worklist */
903 gtk_widget_set_sensitive(ptr->change_cmd, TRUE);
904 gtk_widget_set_sensitive(ptr->prepend_cmd, TRUE);
905 gtk_widget_set_sensitive(ptr->append_cmd, TRUE);
906 } else {
907 gtk_widget_set_sensitive(ptr->change_cmd, FALSE);
908 gtk_widget_set_sensitive(ptr->prepend_cmd, FALSE);
909 gtk_widget_set_sensitive(ptr->append_cmd, FALSE);
910 }
911 gtk_widget_set_sensitive(ptr->help_cmd, TRUE);
912 } else {
913 gtk_widget_set_sensitive(ptr->change_cmd, FALSE);
914 gtk_widget_set_sensitive(ptr->help_cmd, FALSE);
915 gtk_widget_set_sensitive(ptr->prepend_cmd, FALSE);
916 gtk_widget_set_sensitive(ptr->append_cmd, FALSE);
917 }
918}
919
920/************************************************************************/
923static void dst_selection_callback(GtkTreeSelection *selection, gpointer data)
924{
925 struct worklist_data *ptr;
926
927 ptr = data;
928
929 /* Update widget sensitivity. */
930 if (gtk_tree_selection_count_selected_rows(selection) > 0) {
931 int num_rows = 0;
932 GtkTreeIter it;
933
934 gtk_widget_set_sensitive(ptr->up_cmd, TRUE);
935 gtk_widget_set_sensitive(ptr->down_cmd, TRUE);
936 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ptr->dst), &it)) {
937 do {
938 num_rows++;
939 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ptr->dst), &it));
940 }
941 if (num_rows > 1) {
942 gtk_widget_set_sensitive(ptr->remove_cmd, TRUE);
943 } else {
944 gtk_widget_set_sensitive(ptr->remove_cmd, FALSE);
945 }
946 } else {
947 gtk_widget_set_sensitive(ptr->up_cmd, FALSE);
948 gtk_widget_set_sensitive(ptr->down_cmd, FALSE);
949 gtk_widget_set_sensitive(ptr->remove_cmd, FALSE);
950 }
951}
952
953/************************************************************************/
956static void cell_render_func(GtkTreeViewColumn *col, GtkCellRenderer *rend,
957 GtkTreeModel *model, GtkTreeIter *it,
958 gpointer data)
959{
960 gint id;
961 struct universal target;
962
963 gtk_tree_model_get(model, it, 0, &id, -1);
964 target = cid_production(id);
965
966 if (GTK_IS_CELL_RENDERER_PIXBUF(rend)) {
967 GdkPixbuf *pix;
968 struct sprite *sprite;
969
970 if (VUT_UTYPE == target.kind) {
972 direction8_invalid()),
974 } else {
976 }
978 g_object_set(rend, "pixbuf", pix, NULL);
979 g_object_unref(G_OBJECT(pix));
980 if (VUT_UTYPE == target.kind) {
982 }
983 } else {
984 struct city **pcity = data;
985 gint column;
986 char *row[4];
987 char buf[4][64];
988 guint i;
989 gboolean useless;
990
991 for (i = 0; i < ARRAY_SIZE(row); i++) {
992 row[i] = buf[i];
993 }
994 column = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(rend), "column"));
995
996 get_city_dialog_production_row(row, sizeof(buf[0]), &target, *pcity);
997 g_object_set(rend, "text", row[column], NULL);
998
999 if (NULL != *pcity && VUT_IMPROVEMENT == target.kind) {
1000 useless = is_improvement_redundant(*pcity, target.value.building);
1001 /* Mark building redundant if we are really certain that there is
1002 * no use for it. */
1003 g_object_set(rend, "strikethrough", useless, NULL);
1004 } else {
1005 g_object_set(rend, "strikethrough", FALSE, NULL);
1006 }
1007 }
1008}
1009
1010/************************************************************************/
1013static void populate_view(GtkTreeView *view, struct city **ppcity,
1014 GtkTreeViewColumn **pcol)
1015{
1016 static const char *titles[] =
1017 { N_("Type"), N_("Name"), N_("Info"), N_("Cost"), N_("Turns") };
1018
1019 static bool titles_done;
1020 guint i;
1021 GtkCellRenderer *rend;
1022 GtkTreeViewColumn *col;
1023
1024 intl_slist(ARRAY_SIZE(titles), titles, &titles_done);
1025
1026 /* Case i == 0 taken out of the loop to workaround gcc-4.2.1 bug
1027 * https://gcc.gnu.org/PR33381
1028 * Some values would 'stick' from i == 0 round. */
1029 i = 0;
1030
1031 rend = gtk_cell_renderer_pixbuf_new();
1032
1033 gtk_tree_view_insert_column_with_data_func(view, i, titles[i], rend,
1034 cell_render_func, ppcity, NULL);
1035 col = gtk_tree_view_get_column(view, i);
1036
1037 if (GUI_GTK_OPTION(show_task_icons)) {
1038 if (max_unit_width == -1 || max_unit_height == -1) {
1040 }
1041 } else {
1042 g_object_set(col, "visible", FALSE, NULL);
1043 }
1044 if (GUI_GTK_OPTION(show_task_icons)) {
1045 g_object_set(rend, "height", max_unit_height, NULL);
1046 }
1047
1048 for (i = 1; i < ARRAY_SIZE(titles); i++) {
1049 gint pos = i - 1;
1050
1051 rend = gtk_cell_renderer_text_new();
1052 g_object_set_data(G_OBJECT(rend), "column", GINT_TO_POINTER(pos));
1053
1054 gtk_tree_view_insert_column_with_data_func(view,
1055 i, titles[i], rend,
1056 cell_render_func, ppcity, NULL);
1057 col = gtk_tree_view_get_column(view, i);
1058
1059 if (pos >= 2) {
1060 g_object_set(G_OBJECT(rend), "xalign", 1.0, NULL);
1061 gtk_tree_view_column_set_alignment(col, 1.0);
1062 }
1063
1064 if (pos == 3) {
1065 *pcol = col;
1066 }
1067 if (GUI_GTK_OPTION(show_task_icons)) {
1068 g_object_set(rend, "height", max_unit_height, NULL);
1069 }
1070 }
1071}
1072
1073/************************************************************************/
1076static gboolean wl_right_button_up(GtkGestureClick *gesture,
1077 int n_press,
1078 double x, double y)
1079{
1080 GtkEventController *controller = GTK_EVENT_CONTROLLER(gesture);
1081 GtkWidget *w = gtk_event_controller_get_widget(controller);
1082 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(w));
1083 GtkTreePath *path;
1084 int bx, by;
1085
1086 gtk_tree_view_convert_widget_to_bin_window_coords(GTK_TREE_VIEW(w), x, y, &bx, &by);
1087
1088 if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(w), bx, by, &path, NULL, NULL, NULL)) {
1089 GtkTreeIter iter;
1090
1091 if (gtk_tree_model_get_iter(model, &iter, path)) {
1092 wl_help_from_iter(model, &iter);
1093 }
1094 }
1095
1096 return TRUE;
1097}
1098
1099/************************************************************************/
1102static gboolean drag_drop(GtkDropTarget *target, const GValue *value,
1103 double x, double y, gpointer data)
1104{
1105 struct worklist_data *ptr = (struct worklist_data *)data;
1106 GtkTreeIter it;
1107 char buf[8192];
1108 cid id = g_value_get_int(value);
1109 struct universal univ;
1110
1111 univ = cid_production(id);
1112 gtk_list_store_append(ptr->dst, &it);
1113 gtk_list_store_set(ptr->dst, &it, 0, id, 1,
1114 production_help(&univ, buf, sizeof(buf)), -1);
1115
1116 commit_worklist(ptr);
1117
1118 return TRUE;
1119}
1120
1121/************************************************************************/
1124GtkWidget *create_worklist(void)
1125{
1126 GtkWidget *editor, *table, *sw, *bbox;
1127 GtkWidget *src_view, *dst_view, *label, *button;
1128 GtkWidget *aux_menu;
1129 GMenu *menu;
1130 GtkWidget *table2, *arrow, *check;
1131 GtkSizeGroup *sgroup;
1132 GtkListStore *src_store, *dst_store;
1133 struct worklist_data *ptr;
1134 int editor_row = 0;
1135 GtkEventController *controller;
1136 GtkGesture *gesture;
1137 GtkDropTarget *dnd_tgt;
1138
1139 ptr = fc_malloc(sizeof(*ptr));
1140
1141 src_store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
1142 dst_store = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
1143
1144 ptr->global_worklist_id = -1;
1145 ptr->pcity = NULL;
1146 ptr->src = src_store;
1147 ptr->dst = dst_store;
1148 ptr->future = FALSE;
1149
1150 /* Create shell. */
1151 editor = gtk_grid_new();
1152 gtk_grid_set_row_spacing(GTK_GRID(editor), 6);
1153 gtk_orientable_set_orientation(GTK_ORIENTABLE(editor),
1154 GTK_ORIENTATION_VERTICAL);
1155 g_signal_connect(editor, "destroy", G_CALLBACK(worklist_destroy), ptr);
1156 g_object_set_data(G_OBJECT(editor), "data", ptr);
1157
1158 ptr->editor = editor;
1159
1160 /* Add source and target lists. */
1161 table = gtk_grid_new();
1162 gtk_grid_attach(GTK_GRID(editor), table, 0, editor_row++, 1, 1);
1163
1164 sgroup = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
1165
1166 sw = gtk_scrolled_window_new();
1167 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
1168 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1169 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1170 gtk_grid_attach(GTK_GRID(table), sw, 3, 1, 2, 1);
1171
1172 src_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(src_store));
1173 gtk_widget_set_hexpand(src_view, TRUE);
1174 gtk_widget_set_vexpand(src_view, TRUE);
1175 g_object_unref(src_store);
1176 gtk_size_group_add_widget(sgroup, src_view);
1177 gtk_widget_set_name(src_view, "small_font");
1178 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(src_view), 1);
1179
1180 gesture = gtk_gesture_click_new();
1181 gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 3);
1182 controller = GTK_EVENT_CONTROLLER(gesture);
1183 g_signal_connect(controller, "pressed",
1184 G_CALLBACK(wl_right_button_up), NULL);
1185 gtk_widget_add_controller(src_view, controller);
1186
1187 populate_view(GTK_TREE_VIEW(src_view), &ptr->pcity, &ptr->src_col);
1188 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), src_view);
1189
1190 label = g_object_new(GTK_TYPE_LABEL,
1191 "use-underline", TRUE,
1192 "mnemonic-widget", src_view,
1193 "label", _("Source _Tasks:"),
1194 "xalign", 0.0, "yalign", 0.5, NULL);
1195 gtk_grid_attach(GTK_GRID(table), label, 3, 0, 1, 1);
1196
1197 check = gtk_check_button_new_with_mnemonic(_("Show _Future Targets"));
1198 gtk_grid_attach(GTK_GRID(table), check, 4, 0, 1, 1);
1199 g_signal_connect(check, "toggled", G_CALLBACK(future_callback), ptr);
1200
1201 table2 = gtk_grid_new();
1202 gtk_grid_attach(GTK_GRID(table), table2, 2, 1, 1, 1);
1203
1204 button = gtk_button_new();
1205 gtk_widget_set_margin_top(button, 24);
1206 gtk_widget_set_margin_bottom(button, 24);
1207 ptr->prepend_cmd = button;
1208 gtk_button_set_has_frame(GTK_BUTTON(button), FALSE);
1209 gtk_grid_attach(GTK_GRID(table2), button, 0, 0, 1, 1);
1210
1211 arrow = gtk_image_new_from_icon_name("pan-start-symbolic");
1212 gtk_button_set_child(GTK_BUTTON(button), arrow);
1213 g_signal_connect_swapped(button, "clicked",
1214 G_CALLBACK(queue_prepend), ptr);
1215 gtk_widget_set_sensitive(ptr->prepend_cmd, FALSE);
1216
1217 button = gtk_button_new();
1218 ptr->up_cmd = button;
1219 gtk_button_set_has_frame(GTK_BUTTON(button), FALSE);
1220 gtk_grid_attach(GTK_GRID(table2), button, 0, 1, 1, 1);
1221
1222 arrow = gtk_image_new_from_icon_name("pan-up-symbolic");
1223 gtk_button_set_child(GTK_BUTTON(button), arrow);
1224 g_signal_connect_swapped(button, "clicked",
1225 G_CALLBACK(queue_bubble_up), ptr);
1226 gtk_widget_set_sensitive(ptr->up_cmd, FALSE);
1227
1228 button = gtk_button_new();
1229 ptr->down_cmd = button;
1230 gtk_button_set_has_frame(GTK_BUTTON(button), FALSE);
1231 gtk_grid_attach(GTK_GRID(table2), button, 0, 2, 1, 1);
1232
1233 arrow = gtk_image_new_from_icon_name("pan-down-symbolic");
1234 gtk_button_set_child(GTK_BUTTON(button), arrow);
1235 g_signal_connect_swapped(button, "clicked",
1236 G_CALLBACK(queue_bubble_down), ptr);
1237 gtk_widget_set_sensitive(ptr->down_cmd, FALSE);
1238
1239 button = gtk_button_new();
1240 gtk_widget_set_margin_top(button, 24);
1241 gtk_widget_set_margin_bottom(button, 24);
1242 ptr->append_cmd = button;
1243 gtk_button_set_has_frame(GTK_BUTTON(button), FALSE);
1244 gtk_grid_attach(GTK_GRID(table2), button, 0, 3, 1, 1);
1245
1246 arrow = gtk_image_new_from_icon_name("pan-start-symbolic");
1247 gtk_button_set_child(GTK_BUTTON(button), arrow);
1248 g_signal_connect_swapped(button, "clicked",
1249 G_CALLBACK(queue_append), ptr);
1250 gtk_widget_set_sensitive(ptr->append_cmd, FALSE);
1251
1252 button = gtk_button_new();
1253 gtk_widget_set_margin_top(button, 24);
1254 gtk_widget_set_margin_bottom(button, 24);
1255 ptr->remove_cmd = button;
1256 gtk_button_set_has_frame(GTK_BUTTON(button), FALSE);
1257 gtk_grid_attach(GTK_GRID(table2), button, 0, 4, 1, 1);
1258
1259 arrow = gtk_image_new_from_icon_name("pan-end-symbolic");
1260 gtk_button_set_child(GTK_BUTTON(button), arrow);
1261 g_signal_connect_swapped(button, "clicked",
1262 G_CALLBACK(queue_remove), ptr);
1263 gtk_widget_set_sensitive(ptr->remove_cmd, FALSE);
1264
1265 sw = gtk_scrolled_window_new();
1266 gtk_scrolled_window_set_has_frame(GTK_SCROLLED_WINDOW(sw), TRUE);
1267 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1268 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1269 gtk_grid_attach(GTK_GRID(table), sw, 0, 1, 2, 1);
1270
1271 dst_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(dst_store));
1272 gtk_widget_set_hexpand(dst_view, TRUE);
1273 gtk_widget_set_vexpand(dst_view, TRUE);
1274 g_object_unref(dst_store);
1275 gtk_size_group_add_widget(sgroup, dst_view);
1276 gtk_widget_set_name(dst_view, "small_font");
1277 gtk_tree_view_set_tooltip_column(GTK_TREE_VIEW(dst_view), 1);
1278
1279 gesture = gtk_gesture_click_new();
1280 gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), 3);
1281 controller = GTK_EVENT_CONTROLLER(gesture);
1282 g_signal_connect(controller, "pressed",
1283 G_CALLBACK(wl_right_button_up), NULL);
1284 gtk_widget_add_controller(dst_view, controller);
1285
1286 populate_view(GTK_TREE_VIEW(dst_view), &ptr->pcity, &ptr->dst_col);
1287 gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(sw), dst_view);
1288
1289 label = g_object_new(GTK_TYPE_LABEL,
1290 "use-underline", TRUE,
1291 "mnemonic-widget", dst_view,
1292 "label", _("Target _Worklist:"),
1293 "xalign", 0.0, "yalign", 0.5, NULL);
1294 gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1);
1295
1296 /* Add bottom menu and buttons. */
1297 bbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2);
1298 gtk_box_set_spacing(GTK_BOX(bbox), 10);
1299 gtk_grid_attach(GTK_GRID(editor), bbox, 0, editor_row++, 1, 1);
1300
1301 ptr->menu = NULL;
1302 aux_menu = aux_menu_new();
1303 ptr->group = G_ACTION_GROUP(g_simple_action_group_new());
1304 menu = create_wl_menu(ptr);
1305 gtk_menu_button_set_menu_model(GTK_MENU_BUTTON(aux_menu), G_MENU_MODEL(menu));
1306
1307 gtk_box_append(GTK_BOX(bbox), aux_menu);
1308 gtk_widget_insert_action_group(aux_menu, "win", ptr->group);
1309
1310 button = icon_label_button_new("help-browser", _("Help"));
1311 gtk_box_append(GTK_BOX(bbox), button);
1312 g_signal_connect(button, "clicked",
1313 G_CALLBACK(help_callback), ptr);
1314 ptr->help_cmd = button;
1315 gtk_widget_set_sensitive(ptr->help_cmd, FALSE);
1316
1317 button = gtk_button_new_with_mnemonic(_("Change Prod_uction"));
1318 gtk_box_append(GTK_BOX(bbox), button);
1319 g_signal_connect(button, "clicked",
1320 G_CALLBACK(change_callback), ptr);
1321 ptr->change_cmd = button;
1322 gtk_widget_set_sensitive(ptr->change_cmd, FALSE);
1323
1324 ptr->src_view = src_view;
1325 ptr->dst_view = dst_view;
1326 ptr->src_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(src_view));
1327 ptr->dst_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(dst_view));
1328 gtk_tree_selection_set_mode(ptr->dst_selection, GTK_SELECTION_MULTIPLE);
1329
1330 /* DND and other state changing callbacks. */
1331 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(dst_view), TRUE);
1332
1333 dnd_tgt = gtk_drop_target_new(G_TYPE_INT, GDK_ACTION_COPY);
1334
1335 g_signal_connect(dnd_tgt, "drop", G_CALLBACK(drag_drop), ptr);
1336 gtk_widget_add_controller(GTK_WIDGET(dst_view), GTK_EVENT_CONTROLLER(dnd_tgt));
1337
1338 controller = gtk_event_controller_key_new();
1339 g_signal_connect(controller, "key-pressed",
1340 G_CALLBACK(src_key_press_callback), ptr);
1341 gtk_widget_add_controller(src_view, controller);
1342
1343 controller = gtk_event_controller_key_new();
1344 g_signal_connect(controller, "key-pressed",
1345 G_CALLBACK(dst_key_press_callback), ptr);
1346 gtk_widget_add_controller(dst_view, controller);
1347
1348 g_signal_connect(src_view, "row_activated",
1349 G_CALLBACK(src_row_callback), ptr);
1350
1351 g_signal_connect(dst_view, "row_activated",
1352 G_CALLBACK(dst_row_callback), ptr);
1353
1354 g_signal_connect(ptr->src_selection, "changed",
1355 G_CALLBACK(src_selection_callback), ptr);
1356 g_signal_connect(ptr->dst_selection, "changed",
1357 G_CALLBACK(dst_selection_callback), ptr);
1358
1359
1360 gtk_widget_show(table);
1361 gtk_widget_show(bbox);
1362
1363 return editor;
1364}
1365
1366/************************************************************************/
1369static GdkContentProvider *drag_prepare(GtkDragSource *source,
1370 double x, double y,
1371 gpointer data)
1372{
1373 GtkTreeIter it;
1374 struct worklist_data *ptr = (struct worklist_data *)data;
1375
1376 if (gtk_tree_selection_get_selected(ptr->src_selection, NULL, &it)) {
1377 gint id;
1378 GdkContentProvider *provider;
1379
1380 gtk_tree_model_get(GTK_TREE_MODEL(ptr->src), &it, 0, &id, -1);
1381
1382 provider = gdk_content_provider_new_typed(G_TYPE_INT, id);
1383
1384 gtk_drag_source_set_content(source, provider);
1385
1386 return provider;
1387 }
1388
1389 return NULL;
1390}
1391
1392/************************************************************************/
1395static void drag_begin(GtkDragSource *source, GdkDrag *drag,
1396 gpointer *data)
1397{
1398 GdkContentProvider *content = gtk_drag_source_get_content(source);
1399 GValue val = { 0, };
1400 GdkPaintable *paintable;
1401 cid id;
1402 struct universal target;
1403 struct sprite *sprite;
1404 GdkPixbuf *pix;
1405 GtkWidget *img;
1406
1407 g_value_init(&val, G_TYPE_INT);
1408 if (gdk_content_provider_get_value(content, &val, NULL)) {
1409 id = g_value_get_int(&val);
1410 target = cid_production(id);
1411
1412 if (VUT_UTYPE == target.kind) {
1414 direction8_invalid()),
1416 } else {
1418 }
1420 img = gtk_image_new_from_pixbuf(pix);
1421
1422 paintable = gtk_image_get_paintable(GTK_IMAGE(img));
1423
1424 gtk_drag_source_set_icon(source, paintable, 0, 0);
1425 g_object_unref(paintable);
1426 }
1427}
1428
1429/************************************************************************/
1432void reset_city_worklist(GtkWidget *editor, struct city *pcity)
1433{
1434 struct worklist_data *ptr;
1435 GtkDragSource *dnd_src;
1436
1437 ptr = g_object_get_data(G_OBJECT(editor), "data");
1438
1439 ptr->global_worklist_id = -1;
1440 ptr->pcity = pcity;
1441
1442 gtk_list_store_clear(ptr->src);
1443 gtk_list_store_clear(ptr->dst);
1444
1445 g_object_set(ptr->src_col, "visible", TRUE, NULL);
1446 g_object_set(ptr->dst_col, "visible", TRUE, NULL);
1447
1448 dnd_src = gtk_drag_source_new();
1449
1450 g_signal_connect(dnd_src, "prepare", G_CALLBACK(drag_prepare), ptr);
1451 g_signal_connect(dnd_src, "drag-begin", G_CALLBACK(drag_begin), ptr);
1452
1453 gtk_widget_add_controller(GTK_WIDGET(ptr->src_view),
1454 GTK_EVENT_CONTROLLER(dnd_src));
1455}
1456
1457/************************************************************************/
1460static void reset_global_worklist(GtkWidget *editor,
1461 struct global_worklist *pgwl)
1462{
1463 struct worklist_data *ptr;
1464
1465 ptr = g_object_get_data(G_OBJECT(editor), "data");
1466
1468 ptr->pcity = NULL;
1469
1470 gtk_list_store_clear(ptr->src);
1471 gtk_list_store_clear(ptr->dst);
1472
1473 gtk_widget_hide(ptr->change_cmd);
1474 g_object_set(ptr->src_col, "visible", FALSE, NULL);
1475 g_object_set(ptr->dst_col, "visible", FALSE, NULL);
1476
1477 gtk_tree_view_unset_rows_drag_source(GTK_TREE_VIEW(ptr->src_view));
1478}
1479
1480/************************************************************************/
1483void refresh_worklist(GtkWidget *editor)
1484{
1485 struct worklist_data *ptr;
1486 struct worklist queue;
1487 struct universal targets[MAX_NUM_PRODUCTION_TARGETS];
1488 int i, targets_used;
1489 struct item items[MAX_NUM_PRODUCTION_TARGETS];
1490 bool selected;
1491 gint id;
1492 GtkTreeIter it;
1493 GtkTreePath *path;
1494 GtkTreeModel *model;
1495 gboolean exists;
1496
1497 ptr = g_object_get_data(G_OBJECT(editor), "data");
1498
1499 /* Refresh source tasks. */
1500 if (gtk_tree_selection_get_selected(ptr->src_selection, NULL, &it)) {
1501 gtk_tree_model_get(GTK_TREE_MODEL(ptr->src), &it, 0, &id, -1);
1502 selected = TRUE;
1503 } else {
1504 selected = FALSE;
1505 }
1506
1507 /* These behave just right if ptr->pcity is NULL -> in case of global
1508 * worklist. */
1509 targets_used = collect_eventually_buildable_targets(targets, ptr->pcity,
1510 ptr->future);
1511 name_and_sort_items(targets, targets_used, items, FALSE, ptr->pcity);
1512
1513 /* Re-purpose existing items in the list store -- this avoids the
1514 * UI jumping around (especially as the set of source tasks doesn't
1515 * actually change much in practice). */
1516 model = GTK_TREE_MODEL(ptr->src);
1517 exists = gtk_tree_model_get_iter_first(model, &it);
1518
1519 path = NULL;
1520 for (i = 0; i < targets_used; i++) {
1521 char buf[8192];
1522
1523 if (!exists) {
1524 gtk_list_store_append(ptr->src, &it);
1525 }
1526
1527 gtk_list_store_set(ptr->src, &it, 0, (gint)cid_encode(items[i].item),
1528 1, production_help(&(items[i].item),
1529 buf, sizeof(buf)), -1);
1530
1531 if (selected && cid_encode(items[i].item) == id) {
1532 path = gtk_tree_model_get_path(GTK_TREE_MODEL(ptr->src), &it);
1533 }
1534
1535 if (exists) {
1536 exists = gtk_tree_model_iter_next(model, &it);
1537 }
1538 }
1539
1540 /* If the list got shorter, delete any excess items. */
1541 if (exists) {
1542 GtkTreeIter it_next;
1543 bool more;
1544
1545 do {
1546 it_next = it;
1547 more = gtk_tree_model_iter_next(model, &it_next);
1548
1549 gtk_list_store_remove(ptr->src, &it);
1550 it = it_next;
1551 } while (more);
1552 }
1553
1554 /* Select the same item that was previously selected, if any. */
1555 if (path) {
1556 gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr->src_view), path, NULL, FALSE);
1557 gtk_tree_path_free(path);
1558 }
1559
1560 /* refresh target worklist. */
1561 model = GTK_TREE_MODEL(ptr->dst);
1562 exists = gtk_tree_model_get_iter_first(model, &it);
1563
1564 /* dance around worklist braindamage. */
1565 if (ptr->pcity != NULL) {
1566 city_get_queue(ptr->pcity, &queue);
1567 } else {
1568 const struct global_worklist *pgwl;
1569
1571
1572 fc_assert(NULL != pgwl);
1573
1574 worklist_copy(&queue, global_worklist_get(pgwl));
1575 }
1576
1577 for (i = 0; i < worklist_length(&queue); i++) {
1578 struct universal target = queue.entries[i];
1579 char buf[8192];
1580
1581 if (!exists) {
1582 gtk_list_store_append(ptr->dst, &it);
1583 }
1584
1585 gtk_list_store_set(ptr->dst, &it, 0, (gint)cid_encode(target),
1586 1, production_help(&target,
1587 buf, sizeof(buf)), -1);
1588
1589 if (exists) {
1590 exists = gtk_tree_model_iter_next(model, &it);
1591 }
1592 }
1593
1594 if (exists) {
1595 GtkTreeIter it_next;
1596 bool more;
1597
1598 do {
1599 it_next = it;
1600 more = gtk_tree_model_iter_next(model, &it_next);
1601
1602 gtk_list_store_remove(ptr->dst, &it);
1603 it = it_next;
1604 } while (more);
1605 }
1606
1607 create_wl_menu(ptr);
1608
1609 /* Update widget sensitivity. */
1610 if (ptr->pcity) {
1612 && city_owner(ptr->pcity) == client.conn.playing)) {
1613 gtk_widget_set_sensitive(ptr->dst_view, TRUE);
1614 } else {
1615 gtk_widget_set_sensitive(ptr->dst_view, FALSE);
1616 }
1617 } else {
1618 gtk_widget_set_sensitive(ptr->dst_view, TRUE);
1619 }
1620}
1621
1622/************************************************************************/
1625static void commit_worklist(struct worklist_data *ptr)
1626{
1627 struct worklist queue;
1628 GtkTreeModel *model;
1629 GtkTreeIter it;
1630 size_t i;
1631
1632 model = GTK_TREE_MODEL(ptr->dst);
1633
1634 worklist_init(&queue);
1635
1636 i = 0;
1637 if (gtk_tree_model_get_iter_first(model, &it)) {
1638 do {
1639 gint id;
1640 struct universal univ;
1641
1642 /* oops, the player has a worklist longer than what we can store. */
1643 if (i >= MAX_LEN_WORKLIST) {
1644 break;
1645 }
1646
1647 gtk_tree_model_get(model, &it, 0, &id, -1);
1648 univ = cid_production(id);
1649 worklist_append(&queue, &univ);
1650
1651 i++;
1652 } while (gtk_tree_model_iter_next(model, &it));
1653 }
1654
1655 /* Dance around worklist braindamage. */
1656 if (ptr->pcity) {
1657 if (!city_set_queue(ptr->pcity, &queue)) {
1658 /* Failed to change worklist. This means worklist visible
1659 * on screen is not true. */
1661 }
1662 } else {
1663 struct global_worklist *pgwl;
1664
1666 if (pgwl) {
1667 global_worklist_set(pgwl, &queue);
1668 }
1669 }
1670}
#define city_owner(_pcity_)
Definition city.h:543
void get_city_dialog_production_row(char *buf[], size_t column_size, struct universal *target, struct city *pcity)
bool city_set_queue(struct city *pcity, const struct worklist *pqueue)
int city_change_production(struct city *pcity, struct universal *target)
void city_get_queue(struct city *pcity, struct worklist *pqueue)
struct civclient client
bool can_client_issue_orders(void)
int collect_eventually_buildable_targets(struct universal *targets, struct city *pcity, bool advanced_tech)
Definition climisc.c:812
void name_and_sort_items(struct universal *targets, int num_targets, struct item *items, bool show_cost, struct city *pcity)
Definition climisc.c:643
cid cid_encode(struct universal target)
Definition climisc.c:476
struct universal cid_decode(cid id)
Definition climisc.c:519
#define MAX_NUM_PRODUCTION_TARGETS
Definition climisc.h:89
#define cid_production
Definition climisc.h:71
int cid
Definition climisc.h:31
static struct fc_sockaddr_list * list
Definition clinet.c:102
int int id
Definition editgui_g.h:28
static struct editor_state * editor
Definition editor.c:100
#define _(String)
Definition fcintl.h:67
#define N_(String)
Definition fcintl.h:69
struct global_worklist * global_worklist_by_id(int id)
bool global_worklist_set(struct global_worklist *pgwl, const struct worklist *pwl)
const char * global_worklist_name(const struct global_worklist *pgwl)
int global_worklist_id(const struct global_worklist *pgwl)
void global_worklist_destroy(struct global_worklist *pgwl)
const struct worklist * global_worklist_get(const struct global_worklist *pgwl)
struct global_worklist * global_worklist_new(const char *name)
void global_worklist_set_name(struct global_worklist *pgwl, const char *name)
#define global_worklists_iterate(pgwl)
#define global_worklists_iterate_end
static struct tile * pos
Definition finddlg.c:53
static GtkWidget * source
Definition gotodlg.c:58
GtkWidget * toplevel
Definition gui_main.c:124
#define GUI_GTK_OPTION(optname)
Definition gui_main.h:25
void setup_dialog(GtkWidget *shell, GtkWidget *parent)
Definition gui_stuff.c:281
void intl_slist(int n, const char **s, bool *done)
Definition gui_stuff.c:105
void popup_help_dialog_string(const char *item)
Definition helpdlg.c:211
void popup_help_dialog_typed(const char *item, enum help_page_type htype)
Definition helpdlg.c:195
static struct gui_dialog * shell
Definition messagedlg.c:39
void free_sprite(struct sprite *s)
Definition sprite.c:278
struct sprite * sprite_scale(struct sprite *src, int new_w, int new_h)
Definition sprite.c:291
GdkPixbuf * sprite_get_pixbuf(struct sprite *sprite)
Definition sprite.c:402
void sprite_get_bounding_box(struct sprite *sprite, int *start_x, int *start_y, int *end_x, int *end_y)
Definition sprite.c:321
static GHashTable * hash
Definition wldlg.c:320
static void dst_selection_callback(GtkTreeSelection *selection, gpointer data)
Definition wldlg.c:889
static void worklists_response(GtkWidget *w, gint response)
Definition wldlg.c:133
void update_worklist_report_dialog(void)
Definition wldlg.c:115
static void queue_remove(struct worklist_data *ptr)
Definition wldlg.c:622
static GtkWidget * get_worklist(int global_worklist_id)
Definition wldlg.c:347
static void reset_global_worklist(GtkWidget *editor, struct global_worklist *pgwl)
Definition wldlg.c:1306
static void future_callback(GtkToggleButton *toggle, gpointer data)
Definition wldlg.c:577
void blank_max_unit_size(void)
Definition wldlg.c:79
static void src_selection_callback(GtkTreeSelection *selection, gpointer data)
Definition wldlg.c:858
static void help_callback(GtkWidget *w, gpointer data)
Definition wldlg.c:519
static void src_row_callback(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
Definition wldlg.c:730
static void dst_row_callback(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *col, gpointer data)
Definition wldlg.c:764
static void delete_worklist(int global_worklist_id)
Definition wldlg.c:373
void refresh_worklist(GtkWidget *editor)
Definition wldlg.c:1329
void add_worklist_dnd_target(GtkWidget *w)
Definition wldlg.c:337
static gboolean src_key_press_callback(GtkWidget *w, GdkEventKey *ev, gpointer data)
Definition wldlg.c:783
static void popdown_worklist(struct global_worklist *pgwl)
Definition wldlg.c:417
static GtkWidget * worklists_shell
Definition wldlg.c:55
static void popup_worklist(struct global_worklist *pgwl)
Definition wldlg.c:383
static GtkWidget * worklists_list
Definition wldlg.c:56
static void queue_bubble_up(struct worklist_data *ptr)
Definition wldlg.c:590
static GtkWidget * create_worklists_report(void)
Definition wldlg.c:212
static void cell_edited(GtkCellRendererText *cell, const gchar *spath, const gchar *text, gpointer data)
Definition wldlg.c:184
static void cell_render_func(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model, GtkTreeIter *it, gpointer data)
Definition wldlg.c:932
static gboolean dst_key_press_callback(GtkWidget *w, GdkEventKey *ev, gpointer data)
Definition wldlg.c:808
static void worklist_destroy(GtkWidget *editor, gpointer data)
Definition wldlg.c:432
static void worklists_destroy_callback(GtkWidget *w, gpointer data)
Definition wldlg.c:107
static void change_callback(GtkWidget *w, gpointer data)
Definition wldlg.c:554
@ WORKLISTS_NEW
Definition wldlg.c:59
@ WORKLISTS_CLOSE
Definition wldlg.c:62
@ WORKLISTS_DELETE
Definition wldlg.c:60
@ WORKLISTS_PROPERTIES
Definition wldlg.c:61
void reset_city_worklist(GtkWidget *editor, struct city *pcity)
Definition wldlg.c:1281
static void queue_insert(struct worklist_data *ptr, bool prepend)
Definition wldlg.c:668
static void menu_item_callback(GtkMenuItem *item, struct worklist_data *ptr)
Definition wldlg.c:448
static void populate_view(GtkTreeView *view, struct city **ppcity, GtkTreeViewColumn **pcol)
Definition wldlg.c:991
static void insert_worklist(int global_worklist_id, GtkWidget *editor)
Definition wldlg.c:362
static void queue_append(struct worklist_data *ptr)
Definition wldlg.c:722
GtkWidget * create_worklist(void)
Definition wldlg.c:1054
static void commit_worklist(struct worklist_data *ptr)
Definition wldlg.c:1472
static void queue_bubble_down(struct worklist_data *ptr)
Definition wldlg.c:637
static int max_unit_width
Definition wldlg.c:67
static int max_unit_height
Definition wldlg.c:67
static void update_max_unit_size(void)
Definition wldlg.c:88
static void queue_prepend(struct worklist_data *ptr)
Definition wldlg.c:714
static GtkListStore * worklists_store
Definition wldlg.c:65
void popup_worklists_report(void)
Definition wldlg.c:286
GtkWidget * icon_label_button_new(const gchar *icon_name, const gchar *label_text)
Definition gui_stuff.c:76
void refresh_all_city_worklists(void)
Definition citydlg.c:3653
GtkWidget * aux_menu_new(void)
Definition gui_stuff.c:243
#define menu_item_insert_unref(menu, index, item)
Definition gui_stuff.h:142
static void drag_begin(GtkDragSource *source, GdkDrag *drag, gpointer *data)
Definition wldlg.c:1395
static gboolean wl_right_button_up(GtkGestureClick *gesture, int n_press, double x, double y)
Definition wldlg.c:1076
static void wl_help_from_iter(GtkTreeModel *model, GtkTreeIter *it)
Definition wldlg.c:544
static GMenu * create_wl_menu(struct worklist_data *ptr)
Definition wldlg.c:487
static GdkContentProvider * drag_prepare(GtkDragSource *source, double x, double y, gpointer data)
Definition wldlg.c:1369
static gboolean drag_drop(GtkDropTarget *target, const GValue *value, double x, double y, gpointer data)
Definition wldlg.c:1102
#define HELP_WORKLIST_EDITOR_ITEM
@ HELP_IMPROVEMENT
Definition helpdlg_g.h:20
@ HELP_UNIT
Definition helpdlg_g.h:20
@ HELP_WONDER
Definition helpdlg_g.h:21
bool is_improvement_redundant(const struct city *pcity, const struct impr_type *pimprove)
bool is_great_wonder(const struct impr_type *pimprove)
const char * improvement_name_translation(const struct impr_type *pimprove)
#define fc_assert(condition)
Definition log.h:176
#define fc_malloc(sz)
Definition mem.h:34
#define ARRAY_SIZE(x)
Definition shared.h:85
#define MAX(x, y)
Definition shared.h:54
Definition city.h:309
struct connection conn
Definition client_main.h:96
struct player * playing
Definition connection.h:156
Definition mapimg.c:367
Definition climisc.h:82
enum universals_n kind
Definition fc_types.h:758
universals_u value
Definition fc_types.h:757
GtkWidget * prepend_cmd
Definition wldlg.c:315
GActionGroup * group
Definition wldlg.c:313
GtkWidget * dst_view
Definition wldlg.c:309
GtkTreeViewColumn * dst_col
Definition wldlg.c:312
GtkTreeSelection * dst_selection
Definition wldlg.c:310
int menu_size
Definition wldlg.c:312
GtkWidget * src_view
Definition wldlg.c:309
GtkWidget * up_cmd
Definition wldlg.c:315
bool future
Definition wldlg.c:317
GtkTreeSelection * src_selection
Definition wldlg.c:310
GtkListStore * src
Definition wldlg.c:308
GtkTreeViewColumn * src_col
Definition wldlg.c:312
GtkWidget * remove_cmd
Definition wldlg.c:315
GtkListStore * dst
Definition wldlg.c:308
GtkWidget * editor
Definition wldlg.c:306
int global_worklist_id
Definition wldlg.c:303
GMenu * menu
Definition wldlg.c:311
GtkWidget * append_cmd
Definition wldlg.c:315
GtkWidget * help_cmd
Definition wldlg.c:314
struct city * pcity
Definition wldlg.c:304
GtkWidget * down_cmd
Definition wldlg.c:315
GtkWidget * change_cmd
Definition wldlg.c:314
struct universal entries[MAX_LEN_WORKLIST]
Definition worklist.h:30
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:969
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
const char * production_help(const struct universal *uni, char *buf, size_t bufsize)
Definition text.c:2120
struct sprite * get_building_sprite(const struct tileset *t, const struct impr_type *pimprove)
Definition tilespec.c:6494
struct sprite * get_unittype_sprite(const struct tileset *t, const struct unit_type *punittype, enum direction8 facing)
Definition tilespec.c:6516
const struct unit_type * utype
Definition fc_types.h:604
const struct impr_type * building
Definition fc_types.h:598
const char * utype_name_translation(const struct unit_type *punittype)
Definition unittype.c:1612
#define unit_type_iterate(_p)
Definition unittype.h:841
#define unit_type_iterate_end
Definition unittype.h:848
void worklist_copy(struct worklist *dst, const struct worklist *src)
Definition worklist.c:112
void worklist_init(struct worklist *pwl)
Definition worklist.c:38
bool worklist_append(struct worklist *pwl, const struct universal *prod)
Definition worklist.c:147
int worklist_length(const struct worklist *pwl)
Definition worklist.c:57
#define MAX_LEN_WORKLIST
Definition worklist.h:24