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)
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 ***********************************************************************/
15 #include <fc_config.h>
22 #include <gdk/gdkkeysyms.h>
36 #include "citydlg_common.h"
37 #include "client_main.h"
39 #include "global_worklist.h"
43 /* client/gui-gtk-3.0 */
48 #include "gui_stuff.h"
54 static GtkWidget
*worklists_shell
;
55 static GtkWidget
*worklists_list
;
64 static GtkListStore
*worklists_store
;
66 static int max_unit_height
= -1, max_unit_width
= -1;
68 static void reset_global_worklist(GtkWidget
*editor
,
69 struct global_worklist
*pgwl
);
70 static void popup_worklist(struct global_worklist
*pgwl
);
71 static void popdown_worklist(struct global_worklist
*pgwl
);
72 static void dst_row_callback(GtkTreeView
*view
, GtkTreePath
*path
,
73 GtkTreeViewColumn
*col
, gpointer data
);
75 /****************************************************************
76 Illegal initialization value for max unit size variables
77 *****************************************************************/
78 void blank_max_unit_size(void)
84 /****************************************************************
85 Setup max unit sprite size.
86 *****************************************************************/
87 static void update_max_unit_size(void)
92 unit_type_iterate(i
) {
94 struct sprite
*sprite
= get_unittype_sprite(tileset
, i
,
95 direction8_invalid(), TRUE
);
97 sprite_get_bounding_box(sprite
, &x1
, &y1
, &x2
, &y2
);
98 max_unit_width
= MAX(max_unit_width
, x2
- x1
);
99 max_unit_height
= MAX(max_unit_height
, y2
- y1
);
100 } unit_type_iterate_end
;
104 /****************************************************************
105 Worklists dialog being destroyed
106 *****************************************************************/
107 static void worklists_destroy_callback(GtkWidget
*w
, gpointer data
)
109 worklists_shell
= NULL
;
113 /****************************************************************
114 Refresh global worklists list
115 *****************************************************************/
116 void update_worklist_report_dialog(void)
120 gtk_list_store_clear(worklists_store
);
121 global_worklists_iterate(pgwl
) {
122 gtk_list_store_append(worklists_store
, &it
);
124 gtk_list_store_set(worklists_store
, &it
,
125 0, global_worklist_name(pgwl
),
126 1, global_worklist_id(pgwl
),
128 } global_worklists_iterate_end
;
131 /****************************************************************
132 User has responded to worklist report
133 *****************************************************************/
134 static void worklists_response(GtkWidget
*w
, gint response
)
136 struct global_worklist
*pgwl
;
138 GtkTreeSelection
*selection
;
142 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(worklists_list
));
144 if (gtk_tree_selection_get_selected(selection
, &model
, &it
)) {
145 gtk_tree_model_get(model
, &it
, 1, &id
, -1);
146 pgwl
= global_worklist_by_id(id
);
154 global_worklist_new(_("new"));
155 update_worklist_report_dialog();
158 case WORKLISTS_DELETE
:
163 popdown_worklist(pgwl
);
164 global_worklist_destroy(pgwl
);
165 update_worklist_report_dialog();
168 case WORKLISTS_PROPERTIES
:
173 popup_worklist(pgwl
);
177 gtk_widget_destroy(worklists_shell
);
182 /****************************************************************
184 *****************************************************************/
185 static void cell_edited(GtkCellRendererText
*cell
,
187 const gchar
*text
, gpointer data
)
191 struct global_worklist
*pgwl
;
194 path
= gtk_tree_path_new_from_string(spath
);
195 gtk_tree_model_get_iter(GTK_TREE_MODEL(worklists_store
), &it
, path
);
196 gtk_tree_path_free(path
);
198 gtk_tree_model_get(GTK_TREE_MODEL(worklists_store
), &it
, 1, &id
, -1);
199 pgwl
= global_worklist_by_id(id
);
202 gtk_list_store_remove(worklists_store
, &it
);
206 global_worklist_set_name(pgwl
, text
);
207 gtk_list_store_set(worklists_store
, &it
, 0, text
, -1);
210 /****************************************************************
211 Bring up the global worklist report.
212 *****************************************************************/
213 static GtkWidget
*create_worklists_report(void)
215 GtkWidget
*shell
, *list
;
216 GtkWidget
*vbox
, *label
, *sw
;
217 GtkCellRenderer
*rend
;
219 shell
= gtk_dialog_new_with_buttons(_("Edit worklists"),
226 GTK_STOCK_PROPERTIES
,
227 WORKLISTS_PROPERTIES
,
231 setup_dialog(shell
, toplevel
);
232 gtk_window_set_position(GTK_WINDOW(shell
), GTK_WIN_POS_MOUSE
);
234 g_signal_connect(shell
, "response",
235 G_CALLBACK(worklists_response
), NULL
);
236 g_signal_connect(shell
, "destroy",
237 G_CALLBACK(worklists_destroy_callback
), NULL
);
239 vbox
= gtk_grid_new();
240 gtk_grid_set_row_spacing(GTK_GRID(vbox
), 2);
241 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox
),
242 GTK_ORIENTATION_VERTICAL
);
243 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell
))), vbox
);
245 worklists_store
= gtk_list_store_new(2, G_TYPE_STRING
, G_TYPE_INT
);
247 list
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(worklists_store
));
248 gtk_widget_set_hexpand(list
, TRUE
);
249 gtk_widget_set_vexpand(list
, TRUE
);
251 g_object_unref(worklists_store
);
252 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list
), FALSE
);
254 worklists_list
= list
;
256 rend
= gtk_cell_renderer_text_new();
257 g_object_set(rend
, "editable", TRUE
, NULL
);
258 g_signal_connect(rend
, "edited",
259 G_CALLBACK(cell_edited
), NULL
);
260 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(list
), -1, NULL
,
261 rend
, "text", 0, NULL
);
263 sw
= gtk_scrolled_window_new(NULL
, NULL
);
264 gtk_scrolled_window_set_min_content_height(GTK_SCROLLED_WINDOW(sw
), 200);
265 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
266 GTK_SHADOW_ETCHED_IN
);
267 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
268 GTK_POLICY_NEVER
, GTK_POLICY_ALWAYS
);
269 gtk_container_add(GTK_CONTAINER(sw
), list
);
271 label
= g_object_new(GTK_TYPE_LABEL
,
272 "use-underline", TRUE
,
273 "mnemonic-widget", list
,
274 "label", _("_Worklists:"),
275 "xalign", 0.0, "yalign", 0.5, NULL
);
277 gtk_container_add(GTK_CONTAINER(vbox
), label
);
278 gtk_container_add(GTK_CONTAINER(vbox
), sw
);
279 gtk_widget_show_all(vbox
);
284 /****************************************************************
285 Open worklists report
286 *****************************************************************/
287 void popup_worklists_report(void)
289 if (!worklists_shell
) {
290 worklists_shell
= create_worklists_report();
292 update_worklist_report_dialog();
295 gtk_window_present(GTK_WINDOW(worklists_shell
));
301 /****************************************************************
303 *****************************************************************/
304 struct worklist_data
{
305 int global_worklist_id
;
310 GtkListStore
*src
, *dst
;
311 GtkWidget
*src_view
, *dst_view
;
312 GtkTreeSelection
*src_selection
, *dst_selection
;
314 GtkTreeViewColumn
*src_col
, *dst_col
;
316 GtkWidget
*add_cmd
, *change_cmd
, *help_cmd
;
317 GtkWidget
*up_cmd
, *down_cmd
, *prepend_cmd
, *append_cmd
, *remove_cmd
;
322 static GHashTable
*hash
;
324 static void commit_worklist(struct worklist_data
*ptr
);
328 TARGET_GTK_TREE_MODEL_ROW
331 static GtkTargetEntry wl_dnd_targets
[] = {
332 { "GTK_TREE_MODEL_ROW", GTK_TARGET_SAME_APP
, TARGET_GTK_TREE_MODEL_ROW
},
337 /****************************************************************
339 *****************************************************************/
340 void add_worklist_dnd_target(GtkWidget
*w
)
342 gtk_drag_dest_set(w
, GTK_DEST_DEFAULT_ALL
,
343 wl_dnd_targets
, G_N_ELEMENTS(wl_dnd_targets
),
347 /****************************************************************
349 *****************************************************************/
350 static GtkWidget
*get_worklist(int global_worklist_id
)
355 ret
= g_hash_table_lookup(hash
, GINT_TO_POINTER(global_worklist_id
));
362 /****************************************************************
363 Insert worklist to editor
364 *****************************************************************/
365 static void insert_worklist(int global_worklist_id
, GtkWidget
*editor
)
368 hash
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
370 g_hash_table_insert(hash
, GINT_TO_POINTER(global_worklist_id
), editor
);
373 /****************************************************************
374 Remove worklist from hash
375 *****************************************************************/
376 static void delete_worklist(int global_worklist_id
)
379 g_hash_table_remove(hash
, GINT_TO_POINTER(global_worklist_id
));
383 /****************************************************************
384 User responded to worklist report
385 *****************************************************************/
386 static void worklist_response(GtkWidget
*shell
, gint response
)
388 gtk_widget_destroy(shell
);
391 /****************************************************************
392 Worklist editor window used by the global worklist report.
393 *****************************************************************/
394 static void popup_worklist(struct global_worklist
*pgwl
)
398 if (!(shell
= get_worklist(global_worklist_id(pgwl
)))) {
401 shell
= gtk_dialog_new_with_buttons(global_worklist_name(pgwl
),
402 GTK_WINDOW(worklists_shell
),
403 GTK_DIALOG_DESTROY_WITH_PARENT
,
407 gtk_window_set_role(GTK_WINDOW(shell
), "worklist");
408 gtk_window_set_position(GTK_WINDOW(shell
), GTK_WIN_POS_MOUSE
);
409 g_signal_connect(shell
, "response", G_CALLBACK(worklist_response
), NULL
);
410 gtk_window_set_default_size(GTK_WINDOW(shell
), 500, 400);
412 editor
= create_worklist();
413 reset_global_worklist(editor
, pgwl
);
414 insert_worklist(global_worklist_id(pgwl
), editor
);
416 gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(shell
))), editor
);
417 gtk_widget_show(editor
);
419 refresh_worklist(editor
);
422 gtk_window_present(GTK_WINDOW(shell
));
425 /****************************************************************
427 *****************************************************************/
428 static void popdown_worklist(struct global_worklist
*pgwl
)
432 if ((shell
= get_worklist(global_worklist_id(pgwl
)))) {
435 parent
= gtk_widget_get_toplevel(shell
);
436 gtk_widget_destroy(parent
);
440 /****************************************************************
442 *****************************************************************/
443 static void worklist_destroy(GtkWidget
*editor
, gpointer data
)
445 struct worklist_data
*ptr
;
449 if (ptr
->global_worklist_id
!= -1) {
450 delete_worklist(ptr
->global_worklist_id
);
456 /****************************************************************
457 Item activated from menu
458 *****************************************************************/
459 static void menu_item_callback(GtkMenuItem
*item
, struct worklist_data
*ptr
)
461 struct global_worklist
*pgwl
;
462 const struct worklist
*pwl
;
465 if (NULL
== client
.conn
.playing
) {
469 pgwl
= global_worklist_by_id(GPOINTER_TO_INT
470 (g_object_get_data(G_OBJECT(item
), "id")));
474 pwl
= global_worklist_get(pgwl
);
476 for (i
= 0; i
< (size_t) worklist_length(pwl
); i
++) {
480 id
= cid_encode(pwl
->entries
[i
]);
482 gtk_list_store_append(ptr
->dst
, &it
);
483 gtk_list_store_set(ptr
->dst
, &it
, 0, (gint
) id
, -1);
486 commit_worklist(ptr
);
489 /****************************************************************
490 Open menu for adding items to worklist
491 *****************************************************************/
492 static void popup_add_menu(GtkMenuShell
*menu
, gpointer data
)
496 gtk_container_foreach(GTK_CONTAINER(menu
),
497 (GtkCallback
) gtk_widget_destroy
, NULL
);
499 global_worklists_iterate(pgwl
) {
500 item
= gtk_menu_item_new_with_label(global_worklist_name(pgwl
));
501 g_object_set_data(G_OBJECT(item
), "id",
502 GINT_TO_POINTER(global_worklist_id(pgwl
)));
503 gtk_widget_show(item
);
505 gtk_container_add(GTK_CONTAINER(menu
), item
);
506 g_signal_connect(item
, "activate",
507 G_CALLBACK(menu_item_callback
), data
);
508 } global_worklists_iterate_end
;
510 item
= gtk_separator_menu_item_new();
511 gtk_widget_show(item
);
513 gtk_container_add(GTK_CONTAINER(menu
), item
);
515 item
= gtk_menu_item_new_with_mnemonic(_("Edit Global _Worklists"));
516 gtk_widget_show(item
);
518 gtk_container_add(GTK_CONTAINER(menu
), item
);
519 g_signal_connect(item
, "activate",
520 G_CALLBACK(popup_worklists_report
), NULL
);
523 /****************************************************************
525 *****************************************************************/
526 static void help_callback(GtkWidget
*w
, gpointer data
)
528 struct worklist_data
*ptr
;
529 GtkTreeSelection
*selection
;
534 selection
= ptr
->src_selection
;
536 if (gtk_tree_selection_get_selected(selection
, &model
, &it
)) {
538 struct universal target
;
540 gtk_tree_model_get(model
, &it
, 0, &id
, -1);
541 target
= cid_decode(id
);
543 if (VUT_UTYPE
== target
.kind
) {
544 popup_help_dialog_typed(utype_name_translation(target
.value
.utype
),
546 } else if (is_great_wonder(target
.value
.building
)) {
547 popup_help_dialog_typed(improvement_name_translation(target
.value
.building
),
550 popup_help_dialog_typed(improvement_name_translation(target
.value
.building
),
554 popup_help_dialog_string(HELP_WORKLIST_EDITOR_ITEM
);
558 /****************************************************************
559 "Change Production" clicked
560 *****************************************************************/
561 static void change_callback(GtkWidget
*w
, gpointer data
)
563 struct worklist_data
*ptr
;
564 GtkTreeSelection
*selection
;
569 selection
= ptr
->src_selection
;
571 if (gtk_tree_selection_get_selected(selection
, &model
, &it
)) {
573 struct universal univ
;
575 gtk_tree_model_get(model
, &it
, 0, &id
, -1);
576 univ
= cid_production(id
);
577 city_change_production(ptr
->pcity
, &univ
);
581 /****************************************************************
582 Showing of future targets toggled
583 *****************************************************************/
584 static void future_callback(GtkToggleButton
*toggle
, gpointer data
)
586 struct worklist_data
*ptr
;
589 ptr
->future
= !ptr
->future
;
591 refresh_worklist(ptr
->editor
);
594 /****************************************************************
595 Move item up in worklist
596 *****************************************************************/
597 static void queue_bubble_up(struct worklist_data
*ptr
)
600 GtkTreeViewColumn
*col
;
603 if (!gtk_widget_is_sensitive(ptr
->dst_view
)) {
607 model
= GTK_TREE_MODEL(ptr
->dst
);
608 gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr
->dst_view
), &path
, &col
);
610 GtkTreeIter it
, it_prev
;
612 if (gtk_tree_path_prev(path
)) {
613 gtk_tree_model_get_iter(model
, &it_prev
, path
);
615 gtk_tree_model_iter_next(model
, &it
);
617 gtk_list_store_swap(GTK_LIST_STORE(model
), &it
, &it_prev
);
619 gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr
->dst_view
), path
, col
, FALSE
);
620 commit_worklist(ptr
);
623 gtk_tree_path_free(path
);
626 /****************************************************************
627 Removal of the item requested
628 *****************************************************************/
629 static void queue_remove(struct worklist_data
*ptr
)
632 GtkTreeViewColumn
*col
;
634 gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr
->dst_view
), &path
, &col
);
636 dst_row_callback(GTK_TREE_VIEW(ptr
->dst_view
), path
, col
, ptr
);
637 gtk_tree_path_free(path
);
641 /****************************************************************
642 Move item down in queue
643 *****************************************************************/
644 static void queue_bubble_down(struct worklist_data
*ptr
)
647 GtkTreeViewColumn
*col
;
650 if (!gtk_widget_is_sensitive(ptr
->dst_view
)) {
654 model
= GTK_TREE_MODEL(ptr
->dst
);
655 gtk_tree_view_get_cursor(GTK_TREE_VIEW(ptr
->dst_view
), &path
, &col
);
657 GtkTreeIter it
, it_next
;
659 gtk_tree_model_get_iter(model
, &it
, path
);
661 if (gtk_tree_model_iter_next(model
, &it_next
)) {
662 gtk_list_store_swap(GTK_LIST_STORE(model
), &it
, &it_next
);
664 gtk_tree_path_next(path
);
665 gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr
->dst_view
), path
, col
, FALSE
);
666 commit_worklist(ptr
);
669 gtk_tree_path_free(path
);
672 /****************************************************************
674 *****************************************************************/
675 static void queue_insert(struct worklist_data
*ptr
, bool prepend
)
681 GtkTreeModel
*src_model
, *dst_model
;
682 GtkTreeIter src_it
, dst_it
;
685 if (!gtk_widget_is_sensitive(ptr
->dst_view
)) {
689 if (!gtk_tree_selection_get_selected(ptr
->src_selection
, &model
, &it
)) {
693 path
= gtk_tree_model_get_path(model
, &it
);
695 src_model
= GTK_TREE_MODEL(ptr
->src
);
696 dst_model
= GTK_TREE_MODEL(ptr
->dst
);
698 gtk_tree_model_get_iter(src_model
, &src_it
, path
);
700 gtk_list_store_prepend(GTK_LIST_STORE(dst_model
), &dst_it
);
702 gtk_list_store_append(GTK_LIST_STORE(dst_model
), &dst_it
);
705 ncols
= gtk_tree_model_get_n_columns(src_model
);
707 for (i
= 0; i
< ncols
; i
++) {
708 GValue value
= { 0, };
710 gtk_tree_model_get_value(src_model
, &src_it
, i
, &value
);
711 gtk_list_store_set_value(GTK_LIST_STORE(dst_model
), &dst_it
, i
, &value
);
713 commit_worklist(ptr
);
715 gtk_tree_path_free(path
);
718 /****************************************************************
719 Prepend item to worklist
720 *****************************************************************/
721 static void queue_prepend(struct worklist_data
*ptr
)
723 queue_insert(ptr
, TRUE
);
726 /****************************************************************
727 Append item to worklist
728 *****************************************************************/
729 static void queue_append(struct worklist_data
*ptr
)
731 queue_insert(ptr
, FALSE
);
734 /****************************************************************
736 *****************************************************************/
737 static void src_row_callback(GtkTreeView
*view
, GtkTreePath
*path
,
738 GtkTreeViewColumn
*col
, gpointer data
)
740 struct worklist_data
*ptr
;
741 GtkTreeModel
*src_model
, *dst_model
;
742 GtkTreeIter src_it
, dst_it
;
747 if (!gtk_widget_is_sensitive(ptr
->dst_view
)) {
751 src_model
= GTK_TREE_MODEL(ptr
->src
);
752 dst_model
= GTK_TREE_MODEL(ptr
->dst
);
754 gtk_tree_model_get_iter(src_model
, &src_it
, path
);
755 gtk_list_store_append(GTK_LIST_STORE(dst_model
), &dst_it
);
757 ncols
= gtk_tree_model_get_n_columns(src_model
);
759 for (i
= 0; i
< ncols
; i
++) {
760 GValue value
= { 0, };
762 gtk_tree_model_get_value(src_model
, &src_it
, i
, &value
);
763 gtk_list_store_set_value(GTK_LIST_STORE(dst_model
), &dst_it
, i
, &value
);
765 commit_worklist(ptr
);
768 /****************************************************************
769 Destination row activated
770 *****************************************************************/
771 static void dst_row_callback(GtkTreeView
*view
, GtkTreePath
*path
,
772 GtkTreeViewColumn
*col
, gpointer data
)
774 struct worklist_data
*ptr
;
775 GtkTreeModel
*dst_model
;
779 dst_model
= GTK_TREE_MODEL(ptr
->dst
);
781 gtk_tree_model_get_iter(dst_model
, &it
, path
);
783 gtk_list_store_remove(GTK_LIST_STORE(dst_model
), &it
);
784 commit_worklist(ptr
);
787 /****************************************************************
789 *****************************************************************/
790 static gboolean
src_key_press_callback(GtkWidget
*w
, GdkEventKey
*ev
,
793 struct worklist_data
*ptr
;
797 if (!gtk_widget_is_sensitive(ptr
->dst_view
)) {
801 if ((ev
->state
& GDK_SHIFT_MASK
) && ev
->keyval
== GDK_KEY_Insert
) {
804 } else if (ev
->keyval
== GDK_KEY_Insert
) {
812 /****************************************************************
813 Key press for destination
814 *****************************************************************/
815 static gboolean
dst_key_press_callback(GtkWidget
*w
, GdkEventKey
*ev
,
819 struct worklist_data
*ptr
;
822 model
= GTK_TREE_MODEL(ptr
->dst
);
824 if (ev
->keyval
== GDK_KEY_Delete
) {
825 GtkTreeIter it
, it_next
;
826 bool deleted
= FALSE
;
828 if (gtk_tree_model_get_iter_first(model
, &it
)) {
833 more
= gtk_tree_model_iter_next(model
, &it_next
);
835 if (gtk_tree_selection_iter_is_selected(ptr
->dst_selection
, &it
)) {
836 gtk_list_store_remove(GTK_LIST_STORE(model
), &it
);
845 commit_worklist(ptr
);
849 } else if ((ev
->state
& GDK_MOD1_MASK
) && ev
->keyval
== GDK_KEY_Up
) {
850 queue_bubble_up(ptr
);
853 } else if ((ev
->state
& GDK_MOD1_MASK
) && ev
->keyval
== GDK_KEY_Down
) {
854 queue_bubble_down(ptr
);
862 /****************************************************************
863 Selection from source
864 *****************************************************************/
865 static void src_selection_callback(GtkTreeSelection
*selection
, gpointer data
)
867 struct worklist_data
*ptr
;
871 /* update widget sensitivity. */
872 if (gtk_tree_selection_get_selected(selection
, NULL
, NULL
)) {
873 if (can_client_issue_orders()
874 && (!ptr
->pcity
|| city_owner(ptr
->pcity
) == client
.conn
.playing
)) {
875 /* if ptr->pcity is NULL, this is a global worklist */
876 gtk_widget_set_sensitive(ptr
->change_cmd
, TRUE
);
877 gtk_widget_set_sensitive(ptr
->prepend_cmd
, TRUE
);
878 gtk_widget_set_sensitive(ptr
->append_cmd
, TRUE
);
880 gtk_widget_set_sensitive(ptr
->change_cmd
, FALSE
);
881 gtk_widget_set_sensitive(ptr
->prepend_cmd
, FALSE
);
882 gtk_widget_set_sensitive(ptr
->append_cmd
, FALSE
);
884 gtk_widget_set_sensitive(ptr
->help_cmd
, TRUE
);
886 gtk_widget_set_sensitive(ptr
->change_cmd
, FALSE
);
887 gtk_widget_set_sensitive(ptr
->help_cmd
, FALSE
);
888 gtk_widget_set_sensitive(ptr
->prepend_cmd
, FALSE
);
889 gtk_widget_set_sensitive(ptr
->append_cmd
, FALSE
);
893 /****************************************************************
894 Selection from destination
895 *****************************************************************/
896 static void dst_selection_callback(GtkTreeSelection
*selection
, gpointer data
)
898 struct worklist_data
*ptr
;
902 /* update widget sensitivity. */
903 if (gtk_tree_selection_count_selected_rows(selection
) > 0) {
907 gtk_widget_set_sensitive(ptr
->up_cmd
, TRUE
);
908 gtk_widget_set_sensitive(ptr
->down_cmd
, TRUE
);
909 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ptr
->dst
), &it
)) {
912 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(ptr
->dst
), &it
));
915 gtk_widget_set_sensitive(ptr
->remove_cmd
, TRUE
);
917 gtk_widget_set_sensitive(ptr
->remove_cmd
, FALSE
);
920 gtk_widget_set_sensitive(ptr
->up_cmd
, FALSE
);
921 gtk_widget_set_sensitive(ptr
->down_cmd
, FALSE
);
922 gtk_widget_set_sensitive(ptr
->remove_cmd
, FALSE
);
926 /****************************************************************
927 Drag&drop to destination
928 *****************************************************************/
929 static gboolean
dst_dnd_callback(GtkWidget
*w
, GdkDragContext
*context
,
930 struct worklist_data
*ptr
)
932 commit_worklist(ptr
);
936 /****************************************************************
938 *****************************************************************/
939 static void cell_render_func(GtkTreeViewColumn
*col
, GtkCellRenderer
*rend
,
940 GtkTreeModel
*model
, GtkTreeIter
*it
,
944 struct universal target
;
946 gtk_tree_model_get(model
, it
, 0, &id
, -1);
947 target
= cid_production(id
);
949 if (GTK_IS_CELL_RENDERER_PIXBUF(rend
)) {
951 struct sprite
*sprite
;
953 if (VUT_UTYPE
== target
.kind
) {
954 sprite
= sprite_scale(get_unittype_sprite(tileset
, target
.value
.utype
,
955 direction8_invalid(), TRUE
),
956 max_unit_width
, max_unit_height
);
959 sprite
= get_building_sprite(tileset
, target
.value
.building
);
962 pix
= sprite_get_pixbuf(sprite
);
963 g_object_set(rend
, "pixbuf", pix
, NULL
);
964 g_object_unref(G_OBJECT(pix
));
965 if (VUT_UTYPE
== target
.kind
) {
969 struct city
**pcity
= data
;
976 for (i
= 0; i
< ARRAY_SIZE(row
); i
++) {
979 column
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(rend
), "column"));
981 get_city_dialog_production_row(row
, sizeof(buf
[0]), &target
, *pcity
);
982 g_object_set(rend
, "text", row
[column
], NULL
);
984 if (NULL
!= *pcity
&& VUT_IMPROVEMENT
== target
.kind
) {
985 useless
= is_improvement_redundant(*pcity
, target
.value
.building
);
986 /* Mark building redundant if we are really certain that there is
988 g_object_set(rend
, "strikethrough", useless
, NULL
);
990 g_object_set(rend
, "strikethrough", FALSE
, NULL
);
995 /****************************************************************
996 Populate view with buildable item information
997 *****************************************************************/
998 static void populate_view(GtkTreeView
*view
, struct city
**ppcity
,
999 GtkTreeViewColumn
**pcol
)
1001 static const char *titles
[] =
1002 { N_("Type"), N_("Name"), N_("Info"), N_("Cost"), N_("Turns") };
1004 static bool titles_done
;
1006 GtkCellRenderer
*rend
;
1007 GtkTreeViewColumn
*col
;
1009 intl_slist(ARRAY_SIZE(titles
), titles
, &titles_done
);
1011 /* Case i == 0 taken out of the loop to workaround gcc-4.2.1 bug
1012 * http://gcc.gnu.org/PR33381
1013 * Some values would 'stick' from i == 0 round. */
1016 rend
= gtk_cell_renderer_pixbuf_new();
1018 gtk_tree_view_insert_column_with_data_func(view
,
1019 i
, titles
[i
], rend
, cell_render_func
, ppcity
, NULL
);
1020 col
= gtk_tree_view_get_column(view
, i
);
1022 if (gui_options
.gui_gtk3_show_task_icons
) {
1023 if (max_unit_width
== -1 || max_unit_height
== -1) {
1024 update_max_unit_size();
1027 g_object_set(col
, "visible", FALSE
, NULL
);
1029 if (gui_options
.gui_gtk3_show_task_icons
) {
1030 g_object_set(rend
, "height", max_unit_height
, NULL
);
1033 for (i
= 1; i
< ARRAY_SIZE(titles
); i
++) {
1037 rend
= gtk_cell_renderer_text_new();
1038 g_object_set_data(G_OBJECT(rend
), "column", GINT_TO_POINTER(pos
));
1040 gtk_tree_view_insert_column_with_data_func(view
,
1041 i
, titles
[i
], rend
, cell_render_func
, ppcity
, NULL
);
1042 col
= gtk_tree_view_get_column(view
, i
);
1045 g_object_set(G_OBJECT(rend
), "xalign", 1.0, NULL
);
1046 gtk_tree_view_column_set_alignment(col
, 1.0);
1052 if (gui_options
.gui_gtk3_show_task_icons
) {
1053 g_object_set(rend
, "height", max_unit_height
, NULL
);
1058 /****************************************************************
1059 Worklist editor shell.
1060 *****************************************************************/
1061 GtkWidget
*create_worklist(void)
1063 GtkWidget
*editor
, *table
, *sw
, *bbox
;
1064 GtkWidget
*src_view
, *dst_view
, *label
, *button
;
1065 GtkWidget
*menubar
, *item
, *menu
, *image
;
1066 GtkWidget
*table2
, *arrow
, *check
;
1067 GtkSizeGroup
*group
;
1068 GtkListStore
*src_store
, *dst_store
;
1069 struct worklist_data
*ptr
;
1071 ptr
= fc_malloc(sizeof(*ptr
));
1073 src_store
= gtk_list_store_new(1, G_TYPE_INT
);
1074 dst_store
= gtk_list_store_new(1, G_TYPE_INT
);
1076 ptr
->global_worklist_id
= -1;
1078 ptr
->src
= src_store
;
1079 ptr
->dst
= dst_store
;
1080 ptr
->future
= FALSE
;
1083 editor
= gtk_grid_new();
1084 gtk_grid_set_row_spacing(GTK_GRID(editor
), 6);
1085 gtk_orientable_set_orientation(GTK_ORIENTABLE(editor
),
1086 GTK_ORIENTATION_VERTICAL
);
1087 g_signal_connect(editor
, "destroy", G_CALLBACK(worklist_destroy
), ptr
);
1088 g_object_set_data(G_OBJECT(editor
), "data", ptr
);
1090 ptr
->editor
= editor
;
1092 /* add source and target lists. */
1093 table
= gtk_grid_new();
1094 gtk_container_add(GTK_CONTAINER(editor
), table
);
1096 group
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
1098 sw
= gtk_scrolled_window_new(NULL
, NULL
);
1099 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
1100 GTK_SHADOW_ETCHED_IN
);
1101 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
1102 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
1103 gtk_grid_attach(GTK_GRID(table
), sw
, 3, 1, 2, 1);
1105 src_view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(src_store
));
1106 gtk_widget_set_hexpand(src_view
, TRUE
);
1107 gtk_widget_set_vexpand(src_view
, TRUE
);
1108 g_object_unref(src_store
);
1109 gtk_size_group_add_widget(group
, src_view
);
1110 gtk_widget_set_name(src_view
, "small_font");
1112 populate_view(GTK_TREE_VIEW(src_view
), &ptr
->pcity
, &ptr
->src_col
);
1113 gtk_container_add(GTK_CONTAINER(sw
), src_view
);
1115 label
= g_object_new(GTK_TYPE_LABEL
,
1116 "use-underline", TRUE
,
1117 "mnemonic-widget", src_view
,
1118 "label", _("Source _Tasks:"),
1119 "xalign", 0.0, "yalign", 0.5, NULL
);
1120 gtk_grid_attach(GTK_GRID(table
), label
, 3, 0, 1, 1);
1122 check
= gtk_check_button_new_with_mnemonic(_("Show _Future Targets"));
1123 gtk_grid_attach(GTK_GRID(table
), check
, 4, 0, 1, 1);
1124 g_signal_connect(check
, "toggled", G_CALLBACK(future_callback
), ptr
);
1126 table2
= gtk_grid_new();
1127 gtk_grid_attach(GTK_GRID(table
), table2
, 2, 1, 1, 1);
1129 button
= gtk_button_new();
1130 gtk_widget_set_margin_top(button
, 24);
1131 gtk_widget_set_margin_bottom(button
, 24);
1132 ptr
->prepend_cmd
= button
;
1133 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
1134 gtk_grid_attach(GTK_GRID(table2
), button
, 0, 0, 1, 1);
1136 arrow
= gtk_arrow_new(GTK_ARROW_LEFT
, GTK_SHADOW_NONE
);
1137 gtk_container_add(GTK_CONTAINER(button
), arrow
);
1138 g_signal_connect_swapped(button
, "clicked",
1139 G_CALLBACK(queue_prepend
), ptr
);
1140 gtk_widget_set_sensitive(ptr
->prepend_cmd
, FALSE
);
1142 button
= gtk_button_new();
1143 ptr
->up_cmd
= button
;
1144 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
1145 gtk_grid_attach(GTK_GRID(table2
), button
, 0, 1, 1, 1);
1147 arrow
= gtk_arrow_new(GTK_ARROW_UP
, GTK_SHADOW_NONE
);
1148 gtk_container_add(GTK_CONTAINER(button
), arrow
);
1149 g_signal_connect_swapped(button
, "clicked",
1150 G_CALLBACK(queue_bubble_up
), ptr
);
1151 gtk_widget_set_sensitive(ptr
->up_cmd
, FALSE
);
1153 button
= gtk_button_new();
1154 ptr
->down_cmd
= button
;
1155 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
1156 gtk_grid_attach(GTK_GRID(table2
), button
, 0, 2, 1, 1);
1158 arrow
= gtk_arrow_new(GTK_ARROW_DOWN
, GTK_SHADOW_IN
);
1159 gtk_container_add(GTK_CONTAINER(button
), arrow
);
1160 g_signal_connect_swapped(button
, "clicked",
1161 G_CALLBACK(queue_bubble_down
), ptr
);
1162 gtk_widget_set_sensitive(ptr
->down_cmd
, FALSE
);
1164 button
= gtk_button_new();
1165 gtk_widget_set_margin_top(button
, 24);
1166 gtk_widget_set_margin_bottom(button
, 24);
1167 ptr
->append_cmd
= button
;
1168 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
1169 gtk_grid_attach(GTK_GRID(table2
), button
, 0, 3, 1, 1);
1171 arrow
= gtk_arrow_new(GTK_ARROW_LEFT
, GTK_SHADOW_NONE
);
1172 gtk_container_add(GTK_CONTAINER(button
), arrow
);
1173 g_signal_connect_swapped(button
, "clicked",
1174 G_CALLBACK(queue_append
), ptr
);
1175 gtk_widget_set_sensitive(ptr
->append_cmd
, FALSE
);
1177 button
= gtk_button_new();
1178 gtk_widget_set_margin_top(button
, 24);
1179 gtk_widget_set_margin_bottom(button
, 24);
1180 ptr
->remove_cmd
= button
;
1181 gtk_button_set_relief(GTK_BUTTON(button
), GTK_RELIEF_NONE
);
1182 gtk_grid_attach(GTK_GRID(table2
), button
, 0, 4, 1, 1);
1184 arrow
= gtk_arrow_new(GTK_ARROW_RIGHT
, GTK_SHADOW_IN
);
1185 gtk_container_add(GTK_CONTAINER(button
), arrow
);
1186 g_signal_connect_swapped(button
, "clicked",
1187 G_CALLBACK(queue_remove
), ptr
);
1188 gtk_widget_set_sensitive(ptr
->remove_cmd
, FALSE
);
1190 sw
= gtk_scrolled_window_new(NULL
, NULL
);
1191 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw
),
1192 GTK_SHADOW_ETCHED_IN
);
1193 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
),
1194 GTK_POLICY_NEVER
, GTK_POLICY_AUTOMATIC
);
1195 gtk_grid_attach(GTK_GRID(table
), sw
, 0, 1, 2, 1);
1197 dst_view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(dst_store
));
1198 gtk_widget_set_hexpand(dst_view
, TRUE
);
1199 gtk_widget_set_vexpand(dst_view
, TRUE
);
1200 g_object_unref(dst_store
);
1201 gtk_size_group_add_widget(group
, dst_view
);
1202 gtk_widget_set_name(dst_view
, "small_font");
1204 populate_view(GTK_TREE_VIEW(dst_view
), &ptr
->pcity
, &ptr
->dst_col
);
1205 gtk_container_add(GTK_CONTAINER(sw
), dst_view
);
1207 label
= g_object_new(GTK_TYPE_LABEL
,
1208 "use-underline", TRUE
,
1209 "mnemonic-widget", dst_view
,
1210 "label", _("Target _Worklist:"),
1211 "xalign", 0.0, "yalign", 0.5, NULL
);
1212 gtk_grid_attach(GTK_GRID(table
), label
, 0, 0, 1, 1);
1214 /* add bottom menu and buttons. */
1215 bbox
= gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
);
1216 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox
), GTK_BUTTONBOX_END
);
1217 gtk_box_set_spacing(GTK_BOX(bbox
), 10);
1218 gtk_container_add(GTK_CONTAINER(editor
), bbox
);
1220 menubar
= gtk_aux_menu_bar_new();
1221 gtk_container_add(GTK_CONTAINER(bbox
), menubar
);
1222 gtk_button_box_set_child_secondary(GTK_BUTTON_BOX(bbox
), menubar
, TRUE
);
1224 menu
= gtk_menu_new();
1226 image
= gtk_image_new_from_stock(GTK_STOCK_ADD
, GTK_ICON_SIZE_MENU
);
1227 item
= gtk_image_menu_item_new_with_mnemonic(_("_Add Global Worklist"));
1228 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
), image
);
1229 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), menu
);
1230 gtk_menu_shell_append(GTK_MENU_SHELL(menubar
), item
);
1231 g_signal_connect(menu
, "show",
1232 G_CALLBACK(popup_add_menu
), ptr
);
1233 ptr
->add_cmd
= item
;
1234 gtk_widget_set_sensitive(ptr
->add_cmd
, FALSE
);
1236 button
= gtk_button_new_from_stock(GTK_STOCK_HELP
);
1237 gtk_container_add(GTK_CONTAINER(bbox
), button
);
1238 g_signal_connect(button
, "clicked",
1239 G_CALLBACK(help_callback
), ptr
);
1240 ptr
->help_cmd
= button
;
1241 gtk_widget_set_sensitive(ptr
->help_cmd
, FALSE
);
1243 button
= gtk_button_new_with_mnemonic(_("Change Prod_uction"));
1244 gtk_container_add(GTK_CONTAINER(bbox
), button
);
1245 g_signal_connect(button
, "clicked",
1246 G_CALLBACK(change_callback
), ptr
);
1247 ptr
->change_cmd
= button
;
1248 gtk_widget_set_sensitive(ptr
->change_cmd
, FALSE
);
1250 ptr
->src_view
= src_view
;
1251 ptr
->dst_view
= dst_view
;
1252 ptr
->src_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(src_view
));
1253 ptr
->dst_selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(dst_view
));
1254 gtk_tree_selection_set_mode(ptr
->dst_selection
, GTK_SELECTION_MULTIPLE
);
1256 /* DND and other state changing callbacks. */
1257 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(dst_view
), TRUE
);
1258 g_signal_connect(dst_view
, "drag_end",
1259 G_CALLBACK(dst_dnd_callback
), ptr
);
1261 g_signal_connect(src_view
, "row_activated",
1262 G_CALLBACK(src_row_callback
), ptr
);
1263 g_signal_connect(src_view
, "key_press_event",
1264 G_CALLBACK(src_key_press_callback
), ptr
);
1266 g_signal_connect(dst_view
, "row_activated",
1267 G_CALLBACK(dst_row_callback
), ptr
);
1268 g_signal_connect(dst_view
, "key_press_event",
1269 G_CALLBACK(dst_key_press_callback
), ptr
);
1271 g_signal_connect(ptr
->src_selection
, "changed",
1272 G_CALLBACK(src_selection_callback
), ptr
);
1273 g_signal_connect(ptr
->dst_selection
, "changed",
1274 G_CALLBACK(dst_selection_callback
), ptr
);
1277 gtk_widget_show_all(table
);
1278 gtk_widget_show_all(bbox
);
1284 /****************************************************************
1285 Reset worklist for city
1286 *****************************************************************/
1287 void reset_city_worklist(GtkWidget
*editor
, struct city
*pcity
)
1289 struct worklist_data
*ptr
;
1291 ptr
= g_object_get_data(G_OBJECT(editor
), "data");
1293 ptr
->global_worklist_id
= -1;
1296 gtk_list_store_clear(ptr
->src
);
1297 gtk_list_store_clear(ptr
->dst
);
1299 g_object_set(ptr
->src_col
, "visible", TRUE
, NULL
);
1300 g_object_set(ptr
->dst_col
, "visible", TRUE
, NULL
);
1302 gtk_tree_view_enable_model_drag_source(GTK_TREE_VIEW(ptr
->src_view
),
1305 G_N_ELEMENTS(wl_dnd_targets
),
1309 /****************************************************************
1310 Reset one of the global worklists
1311 *****************************************************************/
1312 static void reset_global_worklist(GtkWidget
*editor
,
1313 struct global_worklist
*pgwl
)
1315 struct worklist_data
*ptr
;
1317 ptr
= g_object_get_data(G_OBJECT(editor
), "data");
1319 ptr
->global_worklist_id
= global_worklist_id(pgwl
);
1322 gtk_list_store_clear(ptr
->src
);
1323 gtk_list_store_clear(ptr
->dst
);
1325 gtk_widget_hide(ptr
->change_cmd
);
1326 g_object_set(ptr
->src_col
, "visible", FALSE
, NULL
);
1327 g_object_set(ptr
->dst_col
, "visible", FALSE
, NULL
);
1329 gtk_tree_view_unset_rows_drag_source(GTK_TREE_VIEW(ptr
->src_view
));
1332 /****************************************************************
1333 Refresh worklist info
1334 *****************************************************************/
1335 void refresh_worklist(GtkWidget
*editor
)
1337 struct worklist_data
*ptr
;
1338 struct worklist queue
;
1339 struct universal targets
[MAX_NUM_PRODUCTION_TARGETS
];
1340 int i
, targets_used
;
1341 struct item items
[MAX_NUM_PRODUCTION_TARGETS
];
1346 GtkTreeModel
*model
;
1349 ptr
= g_object_get_data(G_OBJECT(editor
), "data");
1351 /* refresh source tasks. */
1352 if (gtk_tree_selection_get_selected(ptr
->src_selection
, NULL
, &it
)) {
1353 gtk_tree_model_get(GTK_TREE_MODEL(ptr
->src
), &it
, 0, &id
, -1);
1358 gtk_list_store_clear(ptr
->src
);
1360 /* These behave just right if ptr->pcity is NULL -> in case of global
1362 targets_used
= collect_eventually_buildable_targets(targets
, ptr
->pcity
,
1364 name_and_sort_items(targets
, targets_used
, items
, FALSE
, ptr
->pcity
);
1367 for (i
= 0; i
< targets_used
; i
++) {
1368 gtk_list_store_append(ptr
->src
, &it
);
1369 gtk_list_store_set(ptr
->src
, &it
, 0, (gint
) cid_encode(items
[i
].item
), -1);
1371 if (selected
&& cid_encode(items
[i
].item
) == id
) {
1372 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(ptr
->src
), &it
);
1376 gtk_tree_view_set_cursor(GTK_TREE_VIEW(ptr
->src_view
), path
, NULL
, FALSE
);
1377 gtk_tree_path_free(path
);
1381 /* refresh target worklist. */
1382 model
= GTK_TREE_MODEL(ptr
->dst
);
1383 exists
= gtk_tree_model_get_iter_first(model
, &it
);
1385 /* dance around worklist braindamage. */
1386 if (ptr
->pcity
!= NULL
) {
1387 city_get_queue(ptr
->pcity
, &queue
);
1389 const struct global_worklist
*pgwl
;
1391 pgwl
= global_worklist_by_id(ptr
->global_worklist_id
);
1393 fc_assert(NULL
!= pgwl
);
1395 worklist_copy(&queue
, global_worklist_get(pgwl
));
1398 for (i
= 0; i
< worklist_length(&queue
); i
++) {
1399 struct universal target
= queue
.entries
[i
];
1402 gtk_list_store_append(ptr
->dst
, &it
);
1405 gtk_list_store_set(ptr
->dst
, &it
, 0, (gint
) cid_encode(target
), -1);
1408 exists
= gtk_tree_model_iter_next(model
, &it
);
1413 GtkTreeIter it_next
;
1418 more
= gtk_tree_model_iter_next(model
, &it_next
);
1420 gtk_list_store_remove(ptr
->dst
, &it
);
1425 /* update widget sensitivity. */
1427 if ((can_client_issue_orders()
1428 && city_owner(ptr
->pcity
) == client
.conn
.playing
)) {
1429 gtk_widget_set_sensitive(ptr
->add_cmd
, TRUE
);
1430 gtk_widget_set_sensitive(ptr
->dst_view
, TRUE
);
1432 gtk_widget_set_sensitive(ptr
->add_cmd
, FALSE
);
1433 gtk_widget_set_sensitive(ptr
->dst_view
, FALSE
);
1436 gtk_widget_set_sensitive(ptr
->add_cmd
, TRUE
);
1437 gtk_widget_set_sensitive(ptr
->dst_view
, TRUE
);
1441 /****************************************************************
1442 Commit worklist data to worklist
1443 *****************************************************************/
1444 static void commit_worklist(struct worklist_data
*ptr
)
1446 struct worklist queue
;
1447 GtkTreeModel
*model
;
1451 model
= GTK_TREE_MODEL(ptr
->dst
);
1453 worklist_init(&queue
);
1456 if (gtk_tree_model_get_iter_first(model
, &it
)) {
1459 struct universal univ
;
1461 /* oops, the player has a worklist longer than what we can store. */
1462 if (i
>= MAX_LEN_WORKLIST
) {
1466 gtk_tree_model_get(model
, &it
, 0, &id
, -1);
1467 univ
= cid_production(id
);
1468 worklist_append(&queue
, &univ
);
1471 } while (gtk_tree_model_iter_next(model
, &it
));
1474 /* dance around worklist braindamage. */
1476 if (!city_set_queue(ptr
->pcity
, &queue
)) {
1477 /* Failed to change worklist. This means worklist visible
1478 * on screen is not true. */
1479 refresh_worklist(ptr
->editor
);
1482 struct global_worklist
*pgwl
;
1484 pgwl
= global_worklist_by_id(ptr
->global_worklist_id
);
1486 global_worklist_set(pgwl
, &queue
);