1 /***********************************************************************
2 Freeciv - Copyright (C) 2005 - The Freeciv Project
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>
18 #include <limits.h> /* USHRT_MAX */
21 #include <gdk/gdkkeysyms.h>
24 #include "bitvector.h"
25 #include "fc_cmdline.h"
31 #include "fc_interface.h"
33 #include "government.h"
40 #include "client_main.h"
43 #include "mapview_common.h"
46 /* client/gui-gtk-3.0 */
49 #include "gui_stuff.h"
56 /* Forward declarations. */
60 struct property_editor
;
63 /****************************************************************************
64 Miscellaneous helpers.
65 ****************************************************************************/
66 static GdkPixbuf
*create_pixbuf_from_layers(const struct tile
*ptile
,
67 const struct unit
*punit
,
68 const struct city
*pcity
,
69 enum layer_category category
);
70 static GdkPixbuf
*create_tile_pixbuf(const struct tile
*ptile
);
71 static GdkPixbuf
*create_unit_pixbuf(const struct unit
*punit
);
72 static GdkPixbuf
*create_city_pixbuf(const struct city
*pcity
);
74 static void add_column(GtkWidget
*view
,
80 GCallback edit_callback
,
81 gpointer callback_userdata
);
83 static bool can_create_unit_at_tile(struct tile
*ptile
);
85 static int get_next_unique_tag(void);
87 /* 'struct stored_tag_hash' and related functions. */
88 #define SPECHASH_TAG stored_tag
89 #define SPECHASH_INT_KEY_TYPE
90 #define SPECHASH_INT_DATA_TYPE
93 /* NB: If packet definitions change, be sure to
94 * update objbind_pack_current_values!!! */
100 struct packet_edit_tile
*tile
;
101 struct packet_edit_startpos_full
*startpos
;
102 struct packet_edit_city
*city
;
103 struct packet_edit_unit
*unit
;
104 struct packet_edit_player
*player
;
106 struct packet_edit_game
*game
;
107 struct packet_edit_scenario_desc
*desc
;
111 /* Helpers for the OPID_TILE_VISION property. */
112 struct tile_vision_data
{
113 bv_player tile_known
, tile_seen
[V_COUNT
];
115 const char *vision_layer_get_name(enum vision_layer
);
117 #define PF_MAX_CLAUSES 16
118 #define PF_DISJUNCTION_SEPARATOR "|"
119 #define PF_CONJUNCTION_SEPARATOR "&"
126 struct pf_conjunction
{
127 struct pf_pattern conjunction
[PF_MAX_CLAUSES
];
131 struct property_filter
{
132 struct pf_conjunction disjunction
[PF_MAX_CLAUSES
];
136 static struct property_filter
*property_filter_new(const char *filter
);
137 static bool property_filter_match(struct property_filter
*pf
,
138 const struct objprop
*op
);
139 static void property_filter_free(struct property_filter
*pf
);
142 /****************************************************************************
143 Object type declarations.
145 To add a new object type:
146 1. Add a value in enum editor_object_type in client/editor.h.
147 2. Add a string name to objtype_get_name.
148 3. Add code in objtype_get_id_from_object.
149 4. Add code in objtype_get_object_from_id.
150 5. Add a case handler in objtype_is_conserved, and if
151 the object type is not conserved, then also in
152 objbind_request_destroy_object and property_page_create_objects.
153 6. Add an if-block in objbind_get_value_from_object.
154 7. Add an if-block in objbind_get_allowed_value_span.
155 9. Add a case handler in property_page_setup_objprops.
156 10. Add a case handler in property_page_add_objbinds_from_tile
159 Furthermore, if the object type is to be editable:
160 11. Define its edit packet in common/packets.def.
161 12. Add the packet handler in server/edithand.c.
162 13. Add its edit packet type to union packetdata.
163 14. Add an if-block in objbind_pack_current_values.
164 15. Add an if-block in objbind_pack_modified_value.
165 16. Add code in property_page_new_packet.
166 17. Add code in property_page_send_packet.
167 18. Add calls to editgui_notify_object_changed in
168 client/packhand.c or where applicable.
170 ****************************************************************************/
172 /* OBJTYPE_* enum values defined in client/editor.h */
174 static const char *objtype_get_name(enum editor_object_type objtype
);
175 static int objtype_get_id_from_object(enum editor_object_type objtype
,
177 static gpointer
objtype_get_object_from_id(enum editor_object_type objtype
,
179 static bool objtype_is_conserved(enum editor_object_type objtype
);
182 /****************************************************************************
183 Value type declarations.
185 To add a new value type:
186 1. Add a value in enum value_types.
187 2. Add its field in union propval_data.
188 3. Add a case handler in valtype_get_name.
189 4. Add a case handler in propval_copy if needed.
190 5. Add a case handler in propval_free if needed.
191 6. Add a case handler in propval_equal if needed.
192 7. Add a case handler in objprop_get_gtype.
193 8. Add a case handler in property_page_set_store_value.
194 9. Add a case handler in propval_as_string if needed.
195 ****************************************************************************/
202 VALTYPE_BUILT_ARRAY
, /* struct built_status[B_LAST] */
203 VALTYPE_INVENTIONS_ARRAY
, /* bool[A_LAST] */
208 VALTYPE_NATION_HASH
, /* struct nation_hash */
210 VALTYPE_TILE_VISION_DATA
/* struct tile_vision_data */
213 static const char *valtype_get_name(enum value_types valtype
);
216 /****************************************************************************
217 Propstate and propval declarations.
219 To add a new member to union propval_data, see the steps for adding a
220 new value type above.
222 New property values are "constructed" by objbind_get_value_from_object.
223 ****************************************************************************/
229 const char *v_const_string
;
231 struct built_status
*v_built
;
232 bv_special v_bv_special
;
235 struct nation_type
*v_nation
;
236 struct nation_hash
*v_nation_hash
;
237 struct government
*v_gov
;
239 struct tile_vision_data
*v_tile_vision
;
243 union propval_data data
;
244 enum value_types valtype
;
248 static void propval_free(struct propval
*pv
);
249 static void propval_free_data(struct propval
*pv
);
250 static struct propval
*propval_copy(struct propval
*pv
);
251 static bool propval_equal(struct propval
*pva
, struct propval
*pvb
);
255 struct propval
*property_value
;
258 static struct propstate
*propstate_new(struct objprop
*op
,
260 static void propstate_destroy(struct propstate
*ps
);
261 static void propstate_clear_value(struct propstate
*ps
);
262 static void propstate_set_value(struct propstate
*ps
,
264 static struct propval
*propstate_get_value(struct propstate
*ps
);
266 #define SPECHASH_TAG propstate
267 #define SPECHASH_INT_KEY_TYPE
268 #define SPECHASH_IDATA_TYPE struct propstate *
269 #define SPECHASH_IDATA_FREE propstate_destroy
270 #include "spechash.h"
273 /****************************************************************************
274 Objprop declarations.
276 To add a new object property:
277 1. Add a value in enum object_property_ids (grouped by
279 2. Define the property in property_page_setup_objprops.
280 3. Add a case handler in objbind_get_value_from_object
281 in the appropriate object type block.
282 4. Add a case handler in objprop_setup_widget.
283 5. Add a case handler in objprop_refresh_widget.
285 Furthermore, if the property is editable:
286 5. Add a field for this property in the edit
287 packet for this object type in common/packets.def.
288 6. Add a case handler in objbind_pack_modified_value.
289 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
290 !!! 7. Add code to set the packet field in !!!
291 !!! objbind_pack_current_values. !!!
292 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
293 8. Add code to handle changes in the packet field in
294 server/edithand.c handle_edit_<objtype>.
296 If the property makes use of an extviewer:
297 9. Handle widget creation in extviewer_new.
298 10. Handle refresh in extviewer_refresh_widgets.
299 11. Handle clear in extviewer_clear_widgets.
300 12. Handle any signal callbacks (e.g. toggled) if needed.
302 TODO: Add more object properties.
303 ****************************************************************************/
304 enum object_property_ids
{
313 #endif /* FREECIV_DEBUG */
321 OPID_TILE_VISION
, /* tile_known and tile_seen */
326 OPID_STARTPOS_EXCLUDE
,
327 OPID_STARTPOS_NATIONS
,
332 #endif /* FREECIV_DEBUG */
336 OPID_UNIT_MOVES_LEFT
,
339 OPID_UNIT_DONE_MOVING
,
347 #endif /* FREECIV_DEBUG */
353 OPID_CITY_FOOD_STOCK
,
354 OPID_CITY_SHIELD_STOCK
,
362 #endif /* FREECIV_DEBUG */
363 OPID_PLAYER_INVENTIONS
,
364 OPID_PLAYER_SCENARIO_RESERVED
,
370 OPID_GAME_SCENARIO_NAME
,
371 OPID_GAME_SCENARIO_AUTHORS
,
372 OPID_GAME_SCENARIO_DESC
,
373 OPID_GAME_SCENARIO_RANDSTATE
,
374 OPID_GAME_SCENARIO_PLAYERS
,
375 OPID_GAME_STARTPOS_NATIONS
,
376 OPID_GAME_PREVENT_CITIES
,
377 OPID_GAME_LAKE_FLOODING
,
378 OPID_GAME_RULESET_LOCKED
381 enum object_property_flags
{
383 OPF_EDITABLE
= 1 << 0,
384 OPF_IN_LISTVIEW
= 1 << 1,
385 OPF_HAS_WIDGET
= 1 << 2
392 enum object_property_flags flags
;
393 enum value_types valtype
;
395 GtkTreeViewColumn
*view_column
;
397 struct extviewer
*extviewer
;
398 struct property_page
*parent_page
;
401 static struct objprop
*objprop_new(int id
,
404 enum object_property_flags flags
,
405 enum value_types valtype
,
406 struct property_page
*parent
);
407 static int objprop_get_id(const struct objprop
*op
);
408 static const char *objprop_get_name(const struct objprop
*op
);
409 static const char *objprop_get_tooltip(const struct objprop
*op
);
410 static enum value_types
objprop_get_valtype(const struct objprop
*op
);
411 static struct property_page
*
412 objprop_get_property_page(const struct objprop
*op
);
414 static bool objprop_show_in_listview(const struct objprop
*op
);
415 static bool objprop_is_sortable(const struct objprop
*op
);
416 static bool objprop_is_readonly(const struct objprop
*op
);
417 static bool objprop_has_widget(const struct objprop
*op
);
419 static GType
objprop_get_gtype(const struct objprop
*op
);
420 static const char *objprop_get_attribute_type_string(const struct objprop
*op
);
421 static void objprop_set_column_id(struct objprop
*op
, int col_id
);
422 static int objprop_get_column_id(const struct objprop
*op
);
423 static void objprop_set_treeview_column(struct objprop
*op
,
424 GtkTreeViewColumn
*col
);
425 static GtkTreeViewColumn
*objprop_get_treeview_column(const struct objprop
*op
);
426 static GtkCellRenderer
*objprop_create_cell_renderer(const struct objprop
*op
);
428 static void objprop_setup_widget(struct objprop
*op
);
429 static GtkWidget
*objprop_get_widget(struct objprop
*op
);
430 static void objprop_set_child_widget(struct objprop
*op
,
431 const char *widget_name
,
433 static GtkWidget
*objprop_get_child_widget(struct objprop
*op
,
434 const char *widget_name
);
435 static void objprop_set_extviewer(struct objprop
*op
,
436 struct extviewer
*ev
);
437 static struct extviewer
*objprop_get_extviewer(struct objprop
*op
);
438 static void objprop_refresh_widget(struct objprop
*op
,
440 static void objprop_widget_entry_changed(GtkEntry
*entry
, gpointer userdata
);
441 static void objprop_widget_spin_button_changed(GtkSpinButton
*spin
,
443 static void objprop_widget_toggle_button_changed(GtkToggleButton
*button
,
446 #define SPECHASH_TAG objprop
447 #define SPECHASH_INT_KEY_TYPE
448 #define SPECHASH_IDATA_TYPE struct objprop *
449 #include "spechash.h"
452 /****************************************************************************
453 Objbind declarations.
454 ****************************************************************************/
456 enum editor_object_type objtype
;
458 struct property_page
*parent_property_page
;
459 struct propstate_hash
*propstate_table
;
460 GtkTreeRowReference
*rowref
;
463 static struct objbind
*objbind_new(enum editor_object_type objtype
,
465 static void objbind_destroy(struct objbind
*ob
);
466 static enum editor_object_type
objbind_get_objtype(const struct objbind
*ob
);
467 static void objbind_bind_properties(struct objbind
*ob
,
468 struct property_page
*pp
);
469 static gpointer
objbind_get_object(struct objbind
*ob
);
470 static int objbind_get_object_id(struct objbind
*ob
);
471 static void objbind_request_destroy_object(struct objbind
*ob
);
472 static struct propval
*objbind_get_value_from_object(struct objbind
*ob
,
474 static bool objbind_get_allowed_value_span(struct objbind
*ob
,
480 static void objbind_set_modified_value(struct objbind
*ob
,
483 static struct propval
*objbind_get_modified_value(struct objbind
*ob
,
485 static void objbind_clear_modified_value(struct objbind
*ob
,
487 static bool objbind_property_is_modified(struct objbind
*ob
,
489 static bool objbind_has_modified_properties(struct objbind
*ob
);
490 static void objbind_clear_all_modified_values(struct objbind
*ob
);
491 static void objbind_pack_current_values(struct objbind
*ob
,
492 union packetdata packet
);
493 static void objbind_pack_modified_value(struct objbind
*ob
,
495 union packetdata packet
);
496 static void objbind_set_rowref(struct objbind
*ob
,
497 GtkTreeRowReference
*rr
);
498 static GtkTreeRowReference
*objbind_get_rowref(struct objbind
*ob
);
500 #define SPECHASH_TAG objbind
501 #define SPECHASH_INT_KEY_TYPE
502 #define SPECHASH_IDATA_TYPE struct objbind *
503 #define SPECHASH_IDATA_FREE objbind_destroy
504 #include "spechash.h"
507 /****************************************************************************
508 Extended property viewer declarations. This is a set of widgets used
509 for viewing and/or editing properties with complex values (e.g. arrays).
510 ****************************************************************************/
512 struct objprop
*objprop
;
513 struct propval
*pv_cached
;
515 GtkWidget
*panel_widget
;
516 GtkWidget
*panel_label
;
517 GtkWidget
*panel_button
;
518 GtkWidget
*panel_image
;
520 GtkWidget
*view_widget
;
521 GtkWidget
*view_label
;
524 GtkTextBuffer
*textbuf
;
527 static struct extviewer
*extviewer_new(struct objprop
*op
);
528 static struct objprop
*extviewer_get_objprop(struct extviewer
*ev
);
529 static GtkWidget
*extviewer_get_panel_widget(struct extviewer
*ev
);
530 static GtkWidget
*extviewer_get_view_widget(struct extviewer
*ev
);
531 static void extviewer_refresh_widgets(struct extviewer
*ev
,
533 static void extviewer_clear_widgets(struct extviewer
*ev
);
534 static void extviewer_panel_button_clicked(GtkButton
*button
,
536 static void extviewer_view_cell_toggled(GtkCellRendererToggle
*cell
,
539 static void extviewer_textbuf_changed(GtkTextBuffer
*textbuf
,
543 /****************************************************************************
544 Property page declarations.
545 ****************************************************************************/
546 struct property_page
{
547 enum editor_object_type objtype
;
550 GtkListStore
*object_store
;
551 GtkWidget
*object_view
;
552 GtkWidget
*extviewer_notebook
;
554 struct property_editor
*pe_parent
;
556 struct objprop_hash
*objprop_table
;
557 struct objbind_hash
*objbind_table
;
558 struct stored_tag_hash
*tag_table
;
560 struct objbind
*focused_objbind
;
563 static struct property_page
*
564 property_page_new(enum editor_object_type objtype
,
565 struct property_editor
*parent
);
566 static void property_page_setup_objprops(struct property_page
*pp
);
567 static const char *property_page_get_name(const struct property_page
*pp
);
568 static enum editor_object_type
569 property_page_get_objtype(const struct property_page
*pp
);
570 static void property_page_load_tiles(struct property_page
*pp
,
571 const struct tile_list
*tiles
);
572 static void property_page_add_objbinds_from_tile(struct property_page
*pp
,
573 const struct tile
*ptile
);
574 static int property_page_get_num_objbinds(const struct property_page
*pp
);
575 static void property_page_clear_objbinds(struct property_page
*pp
);
576 static void property_page_add_objbind(struct property_page
*pp
,
577 gpointer object_data
);
578 static void property_page_fill_widgets(struct property_page
*pp
);
579 static struct objbind
*
580 property_page_get_focused_objbind(struct property_page
*pp
);
581 static void property_page_set_focused_objbind(struct property_page
*pp
,
583 static struct objbind
*property_page_get_objbind(struct property_page
*pp
,
585 static void property_page_selection_changed(GtkTreeSelection
*sel
,
587 static gboolean
property_page_selection_func(GtkTreeSelection
*sel
,
590 gboolean currently_selected
,
592 static void property_page_quick_find_entry_changed(GtkWidget
*entry
,
594 static void property_page_change_value(struct property_page
*pp
,
597 static void property_page_send_values(struct property_page
*pp
);
598 static void property_page_reset_objbinds(struct property_page
*pp
);
599 static void property_page_destroy_objects(struct property_page
*pp
);
600 static void property_page_create_objects(struct property_page
*pp
,
601 struct tile_list
*hint_tiles
);
602 static union packetdata
property_page_new_packet(struct property_page
*pp
);
603 static void property_page_send_packet(struct property_page
*pp
,
604 union packetdata packet
);
605 static void property_page_free_packet(struct property_page
*pp
,
606 union packetdata packet
);
607 static void property_page_object_changed(struct property_page
*pp
,
610 static void property_page_object_created(struct property_page
*pp
,
611 int tag
, int object_id
);
612 static void property_page_add_extviewer(struct property_page
*pp
,
613 struct extviewer
*ev
);
614 static void property_page_show_extviewer(struct property_page
*pp
,
615 struct extviewer
*ev
);
616 static void property_page_store_creation_tag(struct property_page
*pp
,
618 static void property_page_remove_creation_tag(struct property_page
*pp
,
620 static bool property_page_tag_is_known(struct property_page
*pp
, int tag
);
621 static void property_page_clear_tags(struct property_page
*pp
);
622 static void property_page_apply_button_clicked(GtkButton
*button
,
624 static void property_page_refresh_button_clicked(GtkButton
*button
,
626 static void property_page_create_button_clicked(GtkButton
*button
,
628 static void property_page_destroy_button_clicked(GtkButton
*button
,
632 #define property_page_objprop_iterate(ARG_pp, NAME_op) \
633 TYPED_HASH_DATA_ITERATE(struct objprop *, (ARG_pp)->objprop_table, NAME_op)
634 #define property_page_objprop_iterate_end HASH_DATA_ITERATE_END
636 #define property_page_objbind_iterate(ARG_pp, NAME_ob) \
637 TYPED_HASH_DATA_ITERATE(struct objbind *, (ARG_pp)->objbind_table, NAME_ob)
638 #define property_page_objbind_iterate_end HASH_DATA_ITERATE_END
641 /****************************************************************************
642 Property editor declarations.
643 ****************************************************************************/
644 struct property_editor
{
648 struct property_page
*property_pages
[NUM_OBJTYPES
];
651 static struct property_editor
*property_editor_new(void);
652 static bool property_editor_add_page(struct property_editor
*pe
,
653 enum editor_object_type objtype
);
654 static struct property_page
*
655 property_editor_get_page(struct property_editor
*pe
,
656 enum editor_object_type objtype
);
658 static struct property_editor
*the_property_editor
;
661 /****************************************************************************
662 Returns the translated name for the given object type.
663 ****************************************************************************/
664 static const char *objtype_get_name(enum editor_object_type objtype
)
669 case OBJTYPE_STARTPOS
:
670 return _("Start Position");
678 return Q_("?play:Game");
683 log_error("%s() Unhandled request to get name of object type %d.",
684 __FUNCTION__
, objtype
);
688 /****************************************************************************
689 Returns the unique identifier value from the given object, assuming it
690 is of the 'objtype' object type. Valid IDs are always greater than or
692 ****************************************************************************/
693 static int objtype_get_id_from_object(enum editor_object_type objtype
,
698 return tile_index((struct tile
*) object
);
699 case OBJTYPE_STARTPOS
:
700 return startpos_number((struct startpos
*) object
);
702 return ((struct unit
*) object
)->id
;
704 return ((struct city
*) object
)->id
;
706 return player_number((struct player
*) object
);
713 log_error("%s(): Unhandled request to get object ID from object %p of "
714 "type %d (%s).", __FUNCTION__
, object
, objtype
,
715 objtype_get_name(objtype
));
719 /****************************************************************************
720 Get the object of type 'objtype' uniquely identified by 'id'.
721 ****************************************************************************/
722 static gpointer
objtype_get_object_from_id(enum editor_object_type objtype
,
727 return index_to_tile(&(wld
.map
), id
);
728 case OBJTYPE_STARTPOS
:
729 return map_startpos_by_number(id
);
731 return game_unit_by_number(id
);
733 return game_city_by_number(id
);
735 return player_by_number(id
);
742 log_error("%s(): Unhandled request to get object of type %d (%s) "
743 "with ID %d.", __FUNCTION__
, objtype
,
744 objtype_get_name(objtype
), id
);
748 /****************************************************************************
749 Returns TRUE if it does not make sense for the object of the given type to
750 be created and destroyed (e.g. tiles, game), as opposed to those that can
751 be (e.g. units, cities, players, etc.).
752 ****************************************************************************/
753 static bool objtype_is_conserved(enum editor_object_type objtype
)
759 case OBJTYPE_STARTPOS
:
768 log_error("%s(): Unhandled request for object type %d (%s)).",
769 __FUNCTION__
, objtype
, objtype_get_name(objtype
));
773 /****************************************************************************
774 Returns the untranslated name for the given value type.
775 ****************************************************************************/
776 static const char *valtype_get_name(enum value_types valtype
)
789 case VALTYPE_BUILT_ARRAY
:
790 return "struct built_status[B_LAST]";
791 case VALTYPE_INVENTIONS_ARRAY
:
792 return "bool[A_LAST]";
793 case VALTYPE_BV_SPECIAL
:
795 case VALTYPE_BV_ROADS
:
797 case VALTYPE_BV_BASES
:
801 case VALTYPE_NATION_HASH
:
802 return "struct nation_hash";
805 case VALTYPE_TILE_VISION_DATA
:
806 return "struct tile_vision_data";
809 log_error("%s(): unhandled value type %d.", __FUNCTION__
, valtype
);
813 /****************************************************************************
814 Convenience function to add a column to a GtkTreeView. Used for the
815 view widget creation in extviewer_new().
816 ****************************************************************************/
817 static void add_column(GtkWidget
*view
,
823 GCallback edit_callback
,
826 GtkCellRenderer
*cell
;
827 GtkTreeViewColumn
*col
;
828 const char *attr
= NULL
;
830 if (gtype
== G_TYPE_BOOLEAN
) {
831 cell
= gtk_cell_renderer_toggle_new();
832 gtk_cell_renderer_toggle_set_radio(GTK_CELL_RENDERER_TOGGLE(cell
),
835 g_signal_connect(cell
, "toggled", edit_callback
, userdata
);
838 } else if (gtype
== GDK_TYPE_PIXBUF
) {
839 cell
= gtk_cell_renderer_pixbuf_new();
842 cell
= gtk_cell_renderer_text_new();
844 g_object_set(cell
, "editable", TRUE
, NULL
);
845 g_signal_connect(cell
, "edited", edit_callback
, userdata
);
850 col
= gtk_tree_view_column_new_with_attributes(name
, cell
,
852 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), col
);
855 /****************************************************************************
856 Fill the supplied buffer with a short string representation of the given
857 value. Returned value is g_strdup'd and must be g_free'd.
858 ****************************************************************************/
859 static gchar
*propval_as_string(struct propval
*pv
)
863 fc_assert_ret_val(NULL
!= pv
, 0);
865 switch (pv
->valtype
) {
870 return g_strdup_printf("%d", pv
->data
.v_int
);
873 return g_strdup_printf("%s", pv
->data
.v_bool
? _("TRUE") : _("FALSE"));
876 return g_strdup_printf("%s", nation_adjective_translation(pv
->data
.v_nation
));
879 return g_strdup_printf("%s", government_name_translation(pv
->data
.v_gov
));
881 case VALTYPE_BUILT_ARRAY
:
883 int great_wonder_count
= 0, small_wonder_count
= 0, building_count
= 0;
886 improvement_iterate(pimprove
) {
887 id
= improvement_index(pimprove
);
888 if (pv
->data
.v_built
[id
].turn
< 0) {
891 if (is_great_wonder(pimprove
)) {
892 great_wonder_count
++;
893 } else if (is_small_wonder(pimprove
)) {
894 small_wonder_count
++;
898 } improvement_iterate_end
;
899 /* TRANS: "Number of buildings, number of small
900 * wonders (e.g. palace), number of great wonders." */
901 return g_strdup_printf(_("%db %ds %dW"),
902 building_count
, small_wonder_count
,
906 case VALTYPE_INVENTIONS_ARRAY
:
907 advance_index_iterate(A_FIRST
, tech
) {
908 if (pv
->data
.v_inventions
[tech
]) {
911 } advance_index_iterate_end
;
912 /* TRANS: "Number of technologies known". */
913 return g_strdup_printf(_("%d known"), count
);
915 case VALTYPE_BV_SPECIAL
:
916 extra_type_by_cause_iterate(EC_SPECIAL
, spe
) {
917 if (BV_ISSET(pv
->data
.v_bv_special
, spe
->data
.special_idx
)) {
920 } extra_type_by_cause_iterate_end
;
921 /* TRANS: "The number of terrain specials (e.g. hut,
922 * river, pollution, etc.) present on a tile." */
923 return g_strdup_printf(_("%d present"), count
);
925 case VALTYPE_BV_ROADS
:
926 extra_type_by_cause_iterate(EC_ROAD
, pextra
) {
927 struct road_type
*proad
= extra_road_get(pextra
);
929 if (BV_ISSET(pv
->data
.v_bv_roads
, road_number(proad
))) {
932 } extra_type_by_cause_iterate_end
;
933 return g_strdup_printf(_("%d present"), count
);
935 case VALTYPE_BV_BASES
:
936 extra_type_by_cause_iterate(EC_BASE
, pextra
) {
937 struct base_type
*pbase
= extra_base_get(pextra
);
939 if (BV_ISSET(pv
->data
.v_bv_bases
, base_number(pbase
))) {
942 } extra_type_by_cause_iterate_end
;
943 return g_strdup_printf(_("%d present"), count
);
945 case VALTYPE_NATION_HASH
:
946 count
= nation_hash_size(pv
->data
.v_nation_hash
);
948 return g_strdup(_("All nations"));
950 return g_strdup_printf(PL_("%d nation", "%d nations",
955 /* Assume it is a very long string. */
956 count
= strlen(pv
->data
.v_const_string
);
957 return g_strdup_printf(PL_("%d byte", "%d bytes", count
),
961 case VALTYPE_TILE_VISION_DATA
:
965 log_error("%s(): Unhandled value type %d for property value %p.",
966 __FUNCTION__
, pv
->valtype
, pv
);
970 /****************************************************************************
971 Convert the built_status information to a user viewable string.
972 Returned value is g_strdup'd and must be g_free'd.
973 ****************************************************************************/
974 static gchar
*built_status_to_string(struct built_status
*bs
)
978 turn_built
= bs
->turn
;
980 if (turn_built
== I_NEVER
) {
981 /* TRANS: Improvement never built. */
982 return g_strdup(_("(never)"));
983 } else if (turn_built
== I_DESTROYED
) {
984 /* TRANS: Improvement was destroyed. */
985 return g_strdup(_("(destroyed)"));
987 return g_strdup_printf("%d", turn_built
);
991 /****************************************************************************
992 Returns TRUE if a unit can be created at the given tile based on the
993 state of the editor (see editor_unit_virtual_create).
994 ****************************************************************************/
995 static bool can_create_unit_at_tile(struct tile
*ptile
)
999 struct player
*pplayer
;
1006 vunit
= editor_unit_virtual_create();
1011 pcity
= tile_city(ptile
);
1012 pplayer
= unit_owner(vunit
);
1014 ret
= (can_unit_exist_at_tile(vunit
, ptile
)
1015 && !is_non_allied_unit_tile(ptile
, pplayer
)
1017 || pplayers_allied(city_owner(pcity
),
1018 unit_owner(vunit
))));
1024 /****************************************************************************
1025 Return the next tag number in the sequence.
1026 ****************************************************************************/
1027 static int get_next_unique_tag(void)
1029 static int tag_series
= 0;
1035 /****************************************************************************
1036 Return a newly allocated deep copy of the given value.
1037 ****************************************************************************/
1038 static struct propval
*propval_copy(struct propval
*pv
)
1040 struct propval
*pv_copy
;
1047 pv_copy
= fc_calloc(1, sizeof(*pv
));
1048 pv_copy
->valtype
= pv
->valtype
;
1050 switch (pv
->valtype
) {
1054 pv_copy
->data
.v_int
= pv
->data
.v_int
;
1057 pv_copy
->data
.v_bool
= pv
->data
.v_bool
;
1059 case VALTYPE_STRING
:
1060 pv_copy
->data
.v_string
= fc_strdup(pv
->data
.v_string
);
1061 pv_copy
->must_free
= TRUE
;
1063 case VALTYPE_PIXBUF
:
1064 g_object_ref(pv
->data
.v_pixbuf
);
1065 pv_copy
->data
.v_pixbuf
= pv
->data
.v_pixbuf
;
1066 pv_copy
->must_free
= TRUE
;
1068 case VALTYPE_BUILT_ARRAY
:
1069 size
= B_LAST
* sizeof(struct built_status
);
1070 pv_copy
->data
.v_pointer
= fc_malloc(size
);
1071 memcpy(pv_copy
->data
.v_pointer
, pv
->data
.v_pointer
, size
);
1072 pv_copy
->must_free
= TRUE
;
1074 case VALTYPE_BV_SPECIAL
:
1075 pv_copy
->data
.v_bv_special
= pv
->data
.v_bv_special
;
1077 case VALTYPE_BV_ROADS
:
1078 pv_copy
->data
.v_bv_roads
= pv
->data
.v_bv_roads
;
1080 case VALTYPE_BV_BASES
:
1081 pv_copy
->data
.v_bv_bases
= pv
->data
.v_bv_bases
;
1083 case VALTYPE_NATION
:
1084 pv_copy
->data
.v_nation
= pv
->data
.v_nation
;
1087 pv_copy
->data
.v_gov
= pv
->data
.v_gov
;
1089 case VALTYPE_NATION_HASH
:
1090 pv_copy
->data
.v_nation_hash
1091 = nation_hash_copy(pv
->data
.v_nation_hash
);
1092 pv_copy
->must_free
= TRUE
;
1094 case VALTYPE_INVENTIONS_ARRAY
:
1095 size
= A_LAST
* sizeof(bool);
1096 pv_copy
->data
.v_pointer
= fc_malloc(size
);
1097 memcpy(pv_copy
->data
.v_pointer
, pv
->data
.v_pointer
, size
);
1098 pv_copy
->must_free
= TRUE
;
1100 case VALTYPE_TILE_VISION_DATA
:
1101 size
= sizeof(struct tile_vision_data
);
1102 pv_copy
->data
.v_tile_vision
= fc_malloc(size
);
1103 pv_copy
->data
.v_tile_vision
->tile_known
1104 = pv
->data
.v_tile_vision
->tile_known
;
1105 vision_layer_iterate(v
) {
1106 pv_copy
->data
.v_tile_vision
->tile_seen
[v
]
1107 = pv
->data
.v_tile_vision
->tile_seen
[v
];
1108 } vision_layer_iterate_end
;
1109 pv_copy
->must_free
= TRUE
;
1113 log_error("%s(): Unhandled value type %d for property value %p.",
1114 __FUNCTION__
, pv
->valtype
, pv
);
1115 pv_copy
->data
= pv
->data
;
1119 /****************************************************************************
1120 Free all allocated memory used by this property value, including calling
1121 the appropriate free function on the internal data according to its type.
1122 ****************************************************************************/
1123 static void propval_free(struct propval
*pv
)
1129 propval_free_data(pv
);
1133 /****************************************************************************
1134 Frees the internal data held by the propval, without freeing the propval
1136 ****************************************************************************/
1137 static void propval_free_data(struct propval
*pv
)
1139 if (!pv
|| !pv
->must_free
) {
1143 switch (pv
->valtype
) {
1147 case VALTYPE_BV_SPECIAL
:
1148 case VALTYPE_BV_ROADS
:
1149 case VALTYPE_BV_BASES
:
1150 case VALTYPE_NATION
:
1153 case VALTYPE_PIXBUF
:
1154 g_object_unref(pv
->data
.v_pixbuf
);
1156 case VALTYPE_STRING
:
1157 case VALTYPE_BUILT_ARRAY
:
1158 case VALTYPE_INVENTIONS_ARRAY
:
1159 case VALTYPE_TILE_VISION_DATA
:
1160 free(pv
->data
.v_pointer
);
1162 case VALTYPE_NATION_HASH
:
1163 nation_hash_destroy(pv
->data
.v_nation_hash
);
1167 log_error("%s(): Unhandled request to free data %p (type %s).",
1168 __FUNCTION__
, pv
->data
.v_pointer
, valtype_get_name(pv
->valtype
));
1171 /****************************************************************************
1172 Returns TRUE if the two values are equal, in a deep sense.
1173 ****************************************************************************/
1174 static bool propval_equal(struct propval
*pva
,
1175 struct propval
*pvb
)
1181 if (pva
->valtype
!= pvb
->valtype
) {
1185 switch (pva
->valtype
) {
1189 return pva
->data
.v_int
== pvb
->data
.v_int
;
1191 return pva
->data
.v_bool
== pvb
->data
.v_bool
;
1192 case VALTYPE_STRING
:
1193 if (pva
->data
.v_const_string
&& pvb
->data
.v_const_string
) {
1194 return 0 == strcmp(pva
->data
.v_const_string
,
1195 pvb
->data
.v_const_string
);
1197 return pva
->data
.v_const_string
== pvb
->data
.v_const_string
;
1198 case VALTYPE_PIXBUF
:
1199 return pva
->data
.v_pixbuf
== pvb
->data
.v_pixbuf
;
1200 case VALTYPE_BUILT_ARRAY
:
1201 if (pva
->data
.v_pointer
== pvb
->data
.v_pointer
) {
1203 } else if (!pva
->data
.v_pointer
|| !pvb
->data
.v_pointer
) {
1207 improvement_iterate(pimprove
) {
1209 id
= improvement_index(pimprove
);
1210 vatb
= pva
->data
.v_built
[id
].turn
;
1211 vbtb
= pvb
->data
.v_built
[id
].turn
;
1212 if (vatb
< 0 && vbtb
< 0) {
1218 } improvement_iterate_end
;
1220 case VALTYPE_INVENTIONS_ARRAY
:
1221 if (pva
->data
.v_pointer
== pvb
->data
.v_pointer
) {
1223 } else if (!pva
->data
.v_pointer
|| !pvb
->data
.v_pointer
) {
1227 advance_index_iterate(A_FIRST
, tech
) {
1228 if (pva
->data
.v_inventions
[tech
] != pvb
->data
.v_inventions
[tech
]) {
1231 } advance_index_iterate_end
;
1233 case VALTYPE_BV_SPECIAL
:
1234 return BV_ARE_EQUAL(pva
->data
.v_bv_special
, pvb
->data
.v_bv_special
);
1235 case VALTYPE_BV_ROADS
:
1236 return BV_ARE_EQUAL(pva
->data
.v_bv_roads
, pvb
->data
.v_bv_roads
);
1237 case VALTYPE_BV_BASES
:
1238 return BV_ARE_EQUAL(pva
->data
.v_bv_bases
, pvb
->data
.v_bv_bases
);
1239 case VALTYPE_NATION
:
1240 return pva
->data
.v_nation
== pvb
->data
.v_nation
;
1241 case VALTYPE_NATION_HASH
:
1242 return nation_hashs_are_equal(pva
->data
.v_nation_hash
,
1243 pvb
->data
.v_nation_hash
);
1245 return pva
->data
.v_gov
== pvb
->data
.v_gov
;
1246 case VALTYPE_TILE_VISION_DATA
:
1247 if (!BV_ARE_EQUAL(pva
->data
.v_tile_vision
->tile_known
,
1248 pvb
->data
.v_tile_vision
->tile_known
)) {
1251 vision_layer_iterate(v
) {
1252 if (!BV_ARE_EQUAL(pva
->data
.v_tile_vision
->tile_seen
[v
],
1253 pvb
->data
.v_tile_vision
->tile_seen
[v
])) {
1256 } vision_layer_iterate_end
;
1260 log_error("%s(): Unhandled value type %d for property values %p and %p.",
1261 __FUNCTION__
, pva
->valtype
, pva
, pvb
);
1262 return pva
->data
.v_pointer
== pvb
->data
.v_pointer
;
1265 /****************************************************************************
1266 Create a new "property state" record. It keeps track of the modified value
1267 of a property bound to an object.
1269 NB: Does NOT make a copy of 'pv'.
1270 ****************************************************************************/
1271 static struct propstate
*propstate_new(struct objprop
*op
,
1274 struct propstate
*ps
;
1280 ps
= fc_calloc(1, sizeof(*ps
));
1281 ps
->property_id
= objprop_get_id(op
);
1282 ps
->property_value
= pv
;
1287 /****************************************************************************
1288 Removes the stored value, freeing it if needed.
1289 ****************************************************************************/
1290 static void propstate_clear_value(struct propstate
*ps
)
1296 propval_free(ps
->property_value
);
1297 ps
->property_value
= NULL
;
1300 /****************************************************************************
1301 Free a property state and any associated resources.
1302 ****************************************************************************/
1303 static void propstate_destroy(struct propstate
*ps
)
1308 propstate_clear_value(ps
);
1312 /****************************************************************************
1313 Replace the stored property value with a new one. The old value will
1316 NB: Does NOT make a copy of 'pv'.
1317 ****************************************************************************/
1318 static void propstate_set_value(struct propstate
*ps
,
1324 propstate_clear_value(ps
);
1325 ps
->property_value
= pv
;
1328 /****************************************************************************
1329 Returns the stored value.
1331 NB: NOT a copy of the value.
1332 ****************************************************************************/
1333 static struct propval
*propstate_get_value(struct propstate
*ps
)
1338 return ps
->property_value
;
1341 /****************************************************************************
1342 Create a new object "bind". It serves to bind a set of object properties
1343 to an object instance.
1344 ****************************************************************************/
1345 static struct objbind
*objbind_new(enum editor_object_type objtype
,
1351 if (object
== NULL
) {
1355 id
= objtype_get_id_from_object(objtype
, object
);
1360 ob
= fc_calloc(1, sizeof(*ob
));
1362 ob
->objtype
= objtype
;
1363 ob
->propstate_table
= propstate_hash_new();
1368 /****************************************************************************
1369 Returns the bound object, if it still "exists". Returns NULL on error.
1370 ****************************************************************************/
1371 static gpointer
objbind_get_object(struct objbind
*ob
)
1378 id
= objbind_get_object_id(ob
);
1380 return objtype_get_object_from_id(ob
->objtype
, id
);
1383 /****************************************************************************
1384 Returns the ID of the bound object, or -1 if invalid.
1385 ****************************************************************************/
1386 static int objbind_get_object_id(struct objbind
*ob
)
1391 return ob
->object_id
;
1394 /****************************************************************************
1395 Sends a request to the server to have the bound object erased from
1396 existence. Only makes sense for object types for which the function
1397 objtype_is_conserved() returns FALSE.
1398 ****************************************************************************/
1399 static void objbind_request_destroy_object(struct objbind
*ob
)
1401 struct connection
*my_conn
= &client
.conn
;
1402 enum editor_object_type objtype
;
1409 objtype
= objbind_get_objtype(ob
);
1410 if (objtype_is_conserved(objtype
)) {
1414 id
= objbind_get_object_id(ob
);
1417 case OBJTYPE_STARTPOS
:
1418 dsend_packet_edit_startpos(my_conn
, id
, TRUE
, 0);
1421 dsend_packet_edit_unit_remove_by_id(my_conn
, id
);
1424 dsend_packet_edit_city_remove(my_conn
, id
);
1426 case OBJTYPE_PLAYER
:
1427 dsend_packet_edit_player_remove(my_conn
, id
);
1435 log_error("%s(): Unhandled request to destroy object %p (ID %d) of type "
1436 "%d (%s).", __FUNCTION__
, objbind_get_object(ob
), id
, objtype
,
1437 objtype_get_name(objtype
));
1440 /****************************************************************************
1441 Returns a newly allocated property value for the given object property
1442 on the object referenced by the given object bind, or NULL on failure.
1444 NB: You must call propval_free on the non-NULL return value when it
1446 ****************************************************************************/
1447 static struct propval
*objbind_get_value_from_object(struct objbind
*ob
,
1450 enum editor_object_type objtype
;
1451 enum object_property_ids propid
;
1458 objtype
= objbind_get_objtype(ob
);
1459 propid
= objprop_get_id(op
);
1461 pv
= fc_calloc(1, sizeof(*pv
));
1462 pv
->valtype
= objprop_get_valtype(op
);
1467 const struct tile
*ptile
= objbind_get_object(ob
);
1468 int tile_x
, tile_y
, nat_x
, nat_y
;
1470 if (NULL
== ptile
) {
1474 index_to_map_pos(&tile_x
, &tile_y
, tile_index(ptile
));
1475 index_to_native_pos(&nat_x
, &nat_y
, tile_index(ptile
));
1478 case OPID_TILE_IMAGE
:
1479 pv
->data
.v_pixbuf
= create_tile_pixbuf(ptile
);
1480 pv
->must_free
= TRUE
;
1482 #ifdef FREECIV_DEBUG
1483 case OPID_TILE_ADDRESS
:
1484 pv
->data
.v_string
= g_strdup_printf("%p", ptile
);
1485 pv
->must_free
= TRUE
;
1487 #endif /* FREECIV_DEBUG */
1488 case OPID_TILE_TERRAIN
:
1490 const struct terrain
*pterrain
= tile_terrain(ptile
);
1492 if (NULL
!= pterrain
) {
1493 pv
->data
.v_const_string
= terrain_name_translation(pterrain
);
1495 pv
->data
.v_const_string
= "";
1499 case OPID_TILE_RESOURCE
:
1501 const struct extra_type
*presource
= tile_resource(ptile
);
1503 if (NULL
!= presource
) {
1504 pv
->data
.v_const_string
= extra_name_translation(presource
);
1506 pv
->data
.v_const_string
= "";
1511 pv
->data
.v_string
= g_strdup_printf("(%d, %d)", tile_x
, tile_y
);
1512 pv
->must_free
= TRUE
;
1514 case OPID_TILE_INDEX
:
1515 pv
->data
.v_int
= tile_index(ptile
);
1518 pv
->data
.v_int
= tile_x
;
1521 pv
->data
.v_int
= tile_y
;
1523 case OPID_TILE_NAT_X
:
1524 pv
->data
.v_int
= nat_x
;
1526 case OPID_TILE_NAT_Y
:
1527 pv
->data
.v_int
= nat_y
;
1529 case OPID_TILE_CONTINENT
:
1530 pv
->data
.v_int
= ptile
->continent
;
1532 case OPID_TILE_SPECIALS
:
1533 BV_CLR_ALL(pv
->data
.v_bv_special
);
1534 extra_type_by_cause_iterate(EC_SPECIAL
, pextra
) {
1535 if (tile_has_extra(ptile
, pextra
)) {
1536 BV_SET(pv
->data
.v_bv_special
, pextra
->data
.special_idx
);
1538 } extra_type_by_cause_iterate_end
;
1540 case OPID_TILE_ROADS
:
1541 BV_CLR_ALL(pv
->data
.v_bv_roads
);
1542 extra_type_by_cause_iterate(EC_ROAD
, pextra
) {
1543 if (tile_has_extra(ptile
, pextra
)) {
1544 BV_SET(pv
->data
.v_bv_roads
, road_number(extra_road_get(pextra
)));
1546 } extra_type_by_cause_iterate_end
;
1548 case OPID_TILE_BASES
:
1549 BV_CLR_ALL(pv
->data
.v_bv_bases
);
1550 extra_type_by_cause_iterate(EC_BASE
, pextra
) {
1551 if (tile_has_extra(ptile
, pextra
)) {
1552 BV_SET(pv
->data
.v_bv_bases
, base_number(extra_base_get(pextra
)));
1554 } extra_type_by_cause_iterate_end
;
1556 case OPID_TILE_VISION
:
1557 pv
->data
.v_tile_vision
= fc_malloc(sizeof(struct tile_vision_data
));
1559 /* The server saves the known tiles and the player vision in special
1560 * bitvectors with the number of tiles as index. Here we want the
1561 * information for one tile. Thus, the data is transformed to
1562 * bitvectors with the number of player slots as index. */
1563 BV_CLR_ALL(pv
->data
.v_tile_vision
->tile_known
);
1564 players_iterate(pplayer
) {
1565 if (dbv_isset(&pplayer
->tile_known
, tile_index(ptile
))) {
1566 BV_SET(pv
->data
.v_tile_vision
->tile_known
,
1567 player_index(pplayer
));
1569 } players_iterate_end
;
1571 vision_layer_iterate(v
) {
1572 BV_CLR_ALL(pv
->data
.v_tile_vision
->tile_seen
[v
]);
1573 players_iterate(pplayer
) {
1574 if (fc_funcs
->player_tile_vision_get(ptile
, pplayer
, v
)) {
1575 BV_SET(pv
->data
.v_tile_vision
->tile_seen
[v
],
1576 player_index(pplayer
));
1578 } players_iterate_end
;
1579 } vision_layer_iterate_end
;
1580 pv
->must_free
= TRUE
;
1582 case OPID_TILE_LABEL
:
1583 if (ptile
->label
!= NULL
) {
1584 pv
->data
.v_const_string
= ptile
->label
;
1586 pv
->data
.v_const_string
= "";
1590 log_error("%s(): Unhandled request for value of property %d "
1591 "(%s) from object of type \"%s\".", __FUNCTION__
,
1592 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1598 case OBJTYPE_STARTPOS
:
1600 const struct startpos
*psp
= objbind_get_object(ob
);
1601 const struct tile
*ptile
;
1608 case OPID_STARTPOS_IMAGE
:
1609 ptile
= startpos_tile(psp
);
1610 pv
->data
.v_pixbuf
= create_tile_pixbuf(ptile
);
1611 pv
->must_free
= TRUE
;
1613 case OPID_STARTPOS_XY
:
1614 ptile
= startpos_tile(psp
);
1615 pv
->data
.v_string
= g_strdup_printf("(%d, %d)", TILE_XY(ptile
));
1616 pv
->must_free
= TRUE
;
1618 case OPID_STARTPOS_EXCLUDE
:
1619 pv
->data
.v_bool
= startpos_is_excluding(psp
);
1621 case OPID_STARTPOS_NATIONS
:
1622 pv
->data
.v_nation_hash
= nation_hash_copy(startpos_raw_nations(psp
));
1623 pv
->must_free
= TRUE
;
1626 log_error("%s(): Unhandled request for value of property %d "
1627 "(%s) from object of type \"%s\".", __FUNCTION__
,
1628 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1636 struct unit
*punit
= objbind_get_object(ob
);
1638 if (NULL
== punit
) {
1643 case OPID_UNIT_IMAGE
:
1644 pv
->data
.v_pixbuf
= create_unit_pixbuf(punit
);
1645 pv
->must_free
= TRUE
;
1647 #ifdef FREECIV_DEBUG
1648 case OPID_UNIT_ADDRESS
:
1649 pv
->data
.v_string
= g_strdup_printf("%p", punit
);
1650 pv
->must_free
= TRUE
;
1652 #endif /* FREECIV_DEBUG */
1655 const struct tile
*ptile
= unit_tile(punit
);
1657 pv
->data
.v_string
= g_strdup_printf("(%d, %d)", TILE_XY(ptile
));
1658 pv
->must_free
= TRUE
;
1662 pv
->data
.v_int
= punit
->id
;
1664 case OPID_UNIT_TYPE
:
1666 const struct unit_type
*putype
= unit_type_get(punit
);
1668 pv
->data
.v_const_string
= utype_name_translation(putype
);
1671 case OPID_UNIT_MOVES_LEFT
:
1672 pv
->data
.v_int
= punit
->moves_left
;
1674 case OPID_UNIT_FUEL
:
1675 pv
->data
.v_int
= punit
->fuel
;
1677 case OPID_UNIT_MOVED
:
1678 pv
->data
.v_bool
= punit
->moved
;
1680 case OPID_UNIT_DONE_MOVING
:
1681 pv
->data
.v_bool
= punit
->done_moving
;
1684 pv
->data
.v_int
= punit
->hp
;
1686 case OPID_UNIT_VETERAN
:
1687 pv
->data
.v_int
= punit
->veteran
;
1690 log_error("%s(): Unhandled request for value of property %d "
1691 "(%s) from object of type \"%s\".", __FUNCTION__
,
1692 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1700 const struct city
*pcity
= objbind_get_object(ob
);
1702 if (NULL
== pcity
) {
1707 case OPID_CITY_IMAGE
:
1708 pv
->data
.v_pixbuf
= create_city_pixbuf(pcity
);
1709 pv
->must_free
= TRUE
;
1711 #ifdef FREECIV_DEBUG
1712 case OPID_CITY_ADDRESS
:
1713 pv
->data
.v_string
= g_strdup_printf("%p", pcity
);
1714 pv
->must_free
= TRUE
;
1716 #endif /* FREECIV_DEBUG */
1719 const struct tile
*ptile
= city_tile(pcity
);
1721 pv
->data
.v_string
= g_strdup_printf("(%d, %d)", TILE_XY(ptile
));
1722 pv
->must_free
= TRUE
;
1726 pv
->data
.v_int
= pcity
->id
;
1728 case OPID_CITY_NAME
:
1729 pv
->data
.v_const_string
= pcity
->name
;
1731 case OPID_CITY_SIZE
:
1732 pv
->data
.v_int
= city_size_get(pcity
);
1734 case OPID_CITY_HISTORY
:
1735 pv
->data
.v_int
= pcity
->history
;
1737 case OPID_CITY_BUILDINGS
:
1738 pv
->data
.v_built
= fc_malloc(sizeof(pcity
->built
));
1739 memcpy(pv
->data
.v_built
, pcity
->built
, sizeof(pcity
->built
));
1740 pv
->must_free
= TRUE
;
1742 case OPID_CITY_FOOD_STOCK
:
1743 pv
->data
.v_int
= pcity
->food_stock
;
1745 case OPID_CITY_SHIELD_STOCK
:
1746 pv
->data
.v_int
= pcity
->shield_stock
;
1749 log_error("%s(): Unhandled request for value of property %d "
1750 "(%s) from object of type \"%s\".", __FUNCTION__
,
1751 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1757 case OBJTYPE_PLAYER
:
1759 const struct player
*pplayer
= objbind_get_object(ob
);
1760 const struct research
*presearch
;
1762 if (NULL
== pplayer
) {
1767 case OPID_PLAYER_NAME
:
1768 pv
->data
.v_const_string
= pplayer
->name
;
1770 case OPID_PLAYER_NATION
:
1771 pv
->data
.v_nation
= nation_of_player(pplayer
);
1773 case OPID_PLAYER_GOV
:
1774 pv
->data
.v_gov
= pplayer
->government
;
1776 case OPID_PLAYER_AGE
:
1777 pv
->data
.v_int
= pplayer
->turns_alive
;
1779 #ifdef FREECIV_DEBUG
1780 case OPID_PLAYER_ADDRESS
:
1781 pv
->data
.v_string
= g_strdup_printf("%p", pplayer
);
1782 pv
->must_free
= TRUE
;
1784 #endif /* FREECIV_DEBUG */
1785 case OPID_PLAYER_INVENTIONS
:
1786 presearch
= research_get(pplayer
);
1787 pv
->data
.v_inventions
= fc_calloc(A_LAST
, sizeof(bool));
1788 advance_index_iterate(A_FIRST
, tech
) {
1789 pv
->data
.v_inventions
[tech
]
1790 = TECH_KNOWN
== research_invention_state(presearch
, tech
);
1791 } advance_index_iterate_end
;
1792 pv
->must_free
= TRUE
;
1794 case OPID_PLAYER_SCENARIO_RESERVED
:
1795 pv
->data
.v_bool
= player_has_flag(pplayer
, PLRF_SCENARIO_RESERVED
);
1797 case OPID_PLAYER_SCIENCE
:
1798 presearch
= research_get(pplayer
);
1799 pv
->data
.v_int
= presearch
->bulbs_researched
;
1801 case OPID_PLAYER_GOLD
:
1802 pv
->data
.v_int
= pplayer
->economic
.gold
;
1805 log_error("%s(): Unhandled request for value of property %d "
1806 "(%s) from object of type \"%s\".", __FUNCTION__
,
1807 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1815 const struct civ_game
*pgame
= objbind_get_object(ob
);
1817 if (NULL
== pgame
) {
1822 case OPID_GAME_YEAR
:
1823 pv
->data
.v_int
= pgame
->info
.year
;
1825 case OPID_GAME_SCENARIO
:
1826 pv
->data
.v_bool
= pgame
->scenario
.is_scenario
;
1828 case OPID_GAME_SCENARIO_NAME
:
1829 pv
->data
.v_const_string
= pgame
->scenario
.name
;
1831 case OPID_GAME_SCENARIO_AUTHORS
:
1832 pv
->data
.v_const_string
= pgame
->scenario
.authors
;
1834 case OPID_GAME_SCENARIO_DESC
:
1835 pv
->data
.v_const_string
= pgame
->scenario_desc
.description
;
1837 case OPID_GAME_SCENARIO_RANDSTATE
:
1838 pv
->data
.v_bool
= pgame
->scenario
.save_random
;
1840 case OPID_GAME_SCENARIO_PLAYERS
:
1841 pv
->data
.v_bool
= pgame
->scenario
.players
;
1843 case OPID_GAME_STARTPOS_NATIONS
:
1844 pv
->data
.v_bool
= pgame
->scenario
.startpos_nations
;
1846 case OPID_GAME_PREVENT_CITIES
:
1847 pv
->data
.v_bool
= pgame
->scenario
.prevent_new_cities
;
1849 case OPID_GAME_LAKE_FLOODING
:
1850 pv
->data
.v_bool
= pgame
->scenario
.lake_flooding
;
1852 case OPID_GAME_RULESET_LOCKED
:
1853 pv
->data
.v_bool
= pgame
->scenario
.ruleset_locked
;
1856 log_error("%s(): Unhandled request for value of property %d "
1857 "(%s) from object of type \"%s\".", __FUNCTION__
,
1858 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1868 log_error("%s(): Unhandled request for object type \"%s\" (nb %d).",
1869 __FUNCTION__
, objtype_get_name(objtype
), objtype
);
1878 /****************************************************************************
1879 If applicable, sets the allowed range values of the given object property
1880 as applied to the bound object. Returns TRUE if values were set.
1881 ****************************************************************************/
1882 static bool objbind_get_allowed_value_span(struct objbind
*ob
,
1889 enum editor_object_type objtype
;
1890 enum object_property_ids propid
;
1893 /* Fill the values with something. */
1904 if (NULL
!= pstep
) {
1909 if (NULL
!= pbig_step
) {
1919 propid
= objprop_get_id(op
);
1920 objtype
= objbind_get_objtype(ob
);
1924 case OBJTYPE_STARTPOS
:
1925 log_error("%s(): Unhandled request for value range of property %d (%s) "
1926 "from object of type \"%s\".", __FUNCTION__
,
1927 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1932 const struct unit
*punit
= objbind_get_object(ob
);
1933 const struct unit_type
*putype
;
1935 if (NULL
== punit
) {
1939 putype
= unit_type_get(punit
);
1942 case OPID_UNIT_MOVES_LEFT
:
1944 *pmax
= 65535; /* packets.def MOVEFRAGS */
1948 case OPID_UNIT_FUEL
:
1950 *pmax
= utype_fuel(putype
);
1960 case OPID_UNIT_VETERAN
:
1962 *pmax
= utype_veteran_levels(putype
) - 1;
1970 log_error("%s(): Unhandled request for value range of property %d (%s) "
1971 "from object of type \"%s\".", __FUNCTION__
,
1972 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
1977 const struct city
*pcity
= objbind_get_object(ob
);
1979 if (NULL
== pcity
) {
1984 case OPID_CITY_SIZE
:
1986 *pmax
= MAX_CITY_SIZE
;
1990 case OPID_CITY_HISTORY
:
1996 case OPID_CITY_FOOD_STOCK
:
1998 *pmax
= city_granary_size(city_size_get(pcity
));
2002 case OPID_CITY_SHIELD_STOCK
:
2004 *pmax
= USHRT_MAX
; /* Limited to uint16 by city info packet. */
2012 log_error("%s(): Unhandled request for value range of property %d (%s) "
2013 "from object of type \"%s\".", __FUNCTION__
,
2014 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2017 case OBJTYPE_PLAYER
:
2019 case OPID_PLAYER_SCIENCE
:
2021 *pmax
= 1000000; /* Arbitrary. */
2025 case OPID_PLAYER_GOLD
:
2027 *pmax
= 1000000; /* Arbitrary. */
2034 log_error("%s(): Unhandled request for value range of property %d (%s) "
2035 "from object of type \"%s\".", __FUNCTION__
,
2036 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2041 case OPID_GAME_YEAR
:
2050 log_error("%s(): Unhandled request for value range of property %d (%s) "
2051 "from object of type \"%s\".", __FUNCTION__
,
2052 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2059 log_error("%s(): Unhandled request for object type \"%s\" (nb %d).",
2060 __FUNCTION__
, objtype_get_name(objtype
), objtype
);
2064 /****************************************************************************
2065 Remove a stored modified value, if it exists.
2066 ****************************************************************************/
2067 static void objbind_clear_modified_value(struct objbind
*ob
,
2070 if (!ob
|| !op
|| !ob
->propstate_table
) {
2074 propstate_hash_remove(ob
->propstate_table
, objprop_get_id(op
));
2077 /****************************************************************************
2078 Returns TRUE if a stored modified property value exists for this bound
2079 object for the given property.
2080 ****************************************************************************/
2081 static bool objbind_property_is_modified(struct objbind
*ob
,
2088 if (objprop_is_readonly(op
)) {
2092 return propstate_hash_lookup(ob
->propstate_table
,
2093 objprop_get_id(op
), NULL
);
2096 /****************************************************************************
2097 Returns TRUE if there are any stored modified values of any of the
2098 properties of the bound object.
2099 ****************************************************************************/
2100 static bool objbind_has_modified_properties(struct objbind
*ob
)
2106 return (0 < propstate_hash_size(ob
->propstate_table
));
2109 /****************************************************************************
2110 Deletes all stored modified property values.
2111 ****************************************************************************/
2112 static void objbind_clear_all_modified_values(struct objbind
*ob
)
2117 propstate_hash_clear(ob
->propstate_table
);
2120 /****************************************************************************
2121 Store a modified property value, but only if it is different from the
2122 current value. Always makes a copy of the given value when storing.
2123 ****************************************************************************/
2124 static void objbind_set_modified_value(struct objbind
*ob
,
2128 struct propstate
*ps
;
2130 struct propval
*pv_old
, *pv_copy
;
2131 enum object_property_ids propid
;
2137 propid
= objprop_get_id(op
);
2139 pv_old
= objbind_get_value_from_object(ob
, op
);
2144 equal
= propval_equal(pv
, pv_old
);
2145 propval_free(pv_old
);
2148 objbind_clear_modified_value(ob
, op
);
2152 pv_copy
= propval_copy(pv
);
2154 if (propstate_hash_lookup(ob
->propstate_table
, propid
, &ps
)) {
2155 propstate_set_value(ps
, pv_copy
);
2157 ps
= propstate_new(op
, pv_copy
);
2158 propstate_hash_insert(ob
->propstate_table
, propid
, ps
);
2162 /****************************************************************************
2163 Retrieve the stored property value for the bound object, or NULL if none
2166 NB: Does NOT return a copy.
2167 ****************************************************************************/
2168 static struct propval
*objbind_get_modified_value(struct objbind
*ob
,
2171 struct propstate
*ps
;
2177 if (propstate_hash_lookup(ob
->propstate_table
, objprop_get_id(op
), &ps
)) {
2178 return propstate_get_value(ps
);
2184 /****************************************************************************
2185 Destroy the object bind and free any resources it might have been using.
2186 ****************************************************************************/
2187 static void objbind_destroy(struct objbind
*ob
)
2192 if (ob
->propstate_table
) {
2193 propstate_hash_destroy(ob
->propstate_table
);
2194 ob
->propstate_table
= NULL
;
2197 gtk_tree_row_reference_free(ob
->rowref
);
2203 /****************************************************************************
2204 Returns the object type of the bound object.
2205 ****************************************************************************/
2206 static enum editor_object_type
objbind_get_objtype(const struct objbind
*ob
)
2209 return NUM_OBJTYPES
;
2214 /****************************************************************************
2215 Bind the object in the given objbind to the properties in the page.
2216 ****************************************************************************/
2217 static void objbind_bind_properties(struct objbind
*ob
,
2218 struct property_page
*pp
)
2223 ob
->parent_property_page
= pp
;
2226 /****************************************************************************
2227 Fill the packet with the bound object's current state.
2229 NB: This must be updated if the packet_edit_<object> definitions change.
2230 ****************************************************************************/
2231 static void objbind_pack_current_values(struct objbind
*ob
,
2232 union packetdata pd
)
2234 enum editor_object_type objtype
;
2236 if (!ob
|| !pd
.pointers
.v_pointer1
) {
2240 objtype
= objbind_get_objtype(ob
);
2245 struct packet_edit_tile
*packet
= pd
.tile
;
2246 const struct tile
*ptile
= objbind_get_object(ob
);
2248 if (NULL
== ptile
) {
2252 packet
->tile
= tile_index(ptile
);
2253 packet
->extras
= *tile_extras(ptile
);
2254 /* TODO: Set more packet fields. */
2258 case OBJTYPE_STARTPOS
:
2260 struct packet_edit_startpos_full
*packet
= pd
.startpos
;
2261 const struct startpos
*psp
= objbind_get_object(ob
);
2264 startpos_pack(psp
, packet
);
2271 struct packet_edit_unit
*packet
= pd
.unit
;
2272 const struct unit
*punit
= objbind_get_object(ob
);
2274 if (NULL
== punit
) {
2278 packet
->id
= punit
->id
;
2279 packet
->moves_left
= punit
->moves_left
;
2280 packet
->fuel
= punit
->fuel
;
2281 packet
->moved
= punit
->moved
;
2282 packet
->done_moving
= punit
->done_moving
;
2283 packet
->hp
= punit
->hp
;
2284 packet
->veteran
= punit
->veteran
;
2285 /* TODO: Set more packet fields. */
2291 struct packet_edit_city
*packet
= pd
.city
;
2292 const struct city
*pcity
= objbind_get_object(ob
);
2295 if (NULL
== pcity
) {
2299 packet
->id
= pcity
->id
;
2300 sz_strlcpy(packet
->name
, pcity
->name
);
2301 packet
->size
= city_size_get(pcity
);
2302 packet
->history
= pcity
->history
;
2303 for (i
= 0; i
< B_LAST
; i
++) {
2304 packet
->built
[i
] = pcity
->built
[i
].turn
;
2306 packet
->food_stock
= pcity
->food_stock
;
2307 packet
->shield_stock
= pcity
->shield_stock
;
2308 /* TODO: Set more packet fields. */
2312 case OBJTYPE_PLAYER
:
2314 struct packet_edit_player
*packet
= pd
.player
;
2315 const struct player
*pplayer
= objbind_get_object(ob
);
2316 const struct nation_type
*pnation
;
2317 const struct research
*presearch
;
2319 if (NULL
== pplayer
) {
2323 packet
->id
= player_number(pplayer
);
2324 sz_strlcpy(packet
->name
, pplayer
->name
);
2325 pnation
= nation_of_player(pplayer
);
2326 packet
->nation
= nation_index(pnation
);
2327 presearch
= research_get(pplayer
);
2328 advance_index_iterate(A_FIRST
, tech
) {
2329 packet
->inventions
[tech
]
2330 = TECH_KNOWN
== research_invention_state(presearch
, tech
);
2331 } advance_index_iterate_end
;
2332 packet
->gold
= pplayer
->economic
.gold
;
2333 packet
->government
= government_index(pplayer
->government
);
2334 /* TODO: Set more packet fields. */
2340 struct packet_edit_game
*packet
= pd
.game
.game
;
2341 const struct civ_game
*pgame
= objbind_get_object(ob
);
2343 if (NULL
== pgame
) {
2347 packet
->year
= pgame
->info
.year
;
2348 packet
->scenario
= pgame
->scenario
.is_scenario
;
2349 sz_strlcpy(packet
->scenario_name
, pgame
->scenario
.name
);
2350 sz_strlcpy(packet
->scenario_authors
, pgame
->scenario
.authors
);
2351 sz_strlcpy(pd
.game
.desc
->scenario_desc
, pgame
->scenario_desc
.description
);
2352 packet
->scenario_random
= pgame
->scenario
.save_random
;
2353 packet
->scenario_players
= pgame
->scenario
.players
;
2354 packet
->startpos_nations
= pgame
->scenario
.startpos_nations
;
2355 packet
->prevent_new_cities
= pgame
->scenario
.prevent_new_cities
;
2356 packet
->lake_flooding
= pgame
->scenario
.lake_flooding
;
2364 log_error("%s(): Unhandled object type %s (nb %d).", __FUNCTION__
,
2365 objtype_get_name(objtype
), objtype
);
2368 /****************************************************************************
2369 Package the modified property value into the supplied packet.
2370 ****************************************************************************/
2371 static void objbind_pack_modified_value(struct objbind
*ob
,
2373 union packetdata pd
)
2376 enum editor_object_type objtype
;
2377 enum object_property_ids propid
;
2379 if (!op
|| !ob
|| !pd
.pointers
.v_pointer1
) {
2383 if (NULL
== objbind_get_object(ob
)) {
2387 if (objprop_is_readonly(op
) || !objbind_property_is_modified(ob
, op
)) {
2391 pv
= objbind_get_modified_value(ob
, op
);
2396 objtype
= objbind_get_objtype(ob
);
2397 propid
= objprop_get_id(op
);
2402 struct packet_edit_tile
*packet
= pd
.tile
;
2405 case OPID_TILE_SPECIALS
:
2406 extra_type_by_cause_iterate(EC_SPECIAL
, pextra
) {
2407 if (BV_ISSET(pv
->data
.v_bv_special
, pextra
->data
.special_idx
)) {
2408 BV_SET(packet
->extras
, pextra
->data
.special_idx
);
2410 BV_CLR(packet
->extras
, pextra
->data
.special_idx
);
2412 } extra_type_by_cause_iterate_end
;
2414 case OPID_TILE_ROADS
:
2415 extra_type_by_cause_iterate(EC_ROAD
, pextra
) {
2416 int ridx
= road_number(extra_road_get(pextra
));
2418 if (BV_ISSET(pv
->data
.v_bv_roads
, ridx
)) {
2419 BV_SET(packet
->extras
, extra_index(pextra
));
2421 BV_CLR(packet
->extras
, extra_index(pextra
));
2423 } extra_type_by_cause_iterate_end
;
2425 case OPID_TILE_BASES
:
2426 extra_type_by_cause_iterate(EC_BASE
, pextra
) {
2427 int bidx
= base_number(extra_base_get(pextra
));
2429 if (BV_ISSET(pv
->data
.v_bv_bases
, bidx
)) {
2430 BV_SET(packet
->extras
, extra_index(pextra
));
2432 BV_CLR(packet
->extras
, extra_index(pextra
));
2434 } extra_type_by_cause_iterate_end
;
2436 case OPID_TILE_LABEL
:
2437 sz_strlcpy(packet
->label
, pv
->data
.v_string
);
2443 log_error("%s(): Unhandled request to pack value of property "
2444 "%d (%s) from object of type \"%s\".", __FUNCTION__
,
2445 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2448 case OBJTYPE_STARTPOS
:
2450 struct packet_edit_startpos_full
*packet
= pd
.startpos
;
2453 case OPID_STARTPOS_EXCLUDE
:
2454 packet
->exclude
= pv
->data
.v_bool
;
2456 case OPID_STARTPOS_NATIONS
:
2457 BV_CLR_ALL(packet
->nations
);
2458 nation_hash_iterate(pv
->data
.v_nation_hash
, pnation
) {
2459 BV_SET(packet
->nations
, nation_number(pnation
));
2460 } nation_hash_iterate_end
;
2466 log_error("%s(): Unhandled request to pack value of property "
2467 "%d (%s) from object of type \"%s\".", __FUNCTION__
,
2468 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2473 struct packet_edit_unit
*packet
= pd
.unit
;
2476 case OPID_UNIT_MOVES_LEFT
:
2477 packet
->moves_left
= pv
->data
.v_int
;
2479 case OPID_UNIT_FUEL
:
2480 packet
->fuel
= pv
->data
.v_int
;
2482 case OPID_UNIT_MOVED
:
2483 packet
->moved
= pv
->data
.v_bool
;
2485 case OPID_UNIT_DONE_MOVING
:
2486 packet
->done_moving
= pv
->data
.v_bool
;
2489 packet
->hp
= pv
->data
.v_int
;
2491 case OPID_UNIT_VETERAN
:
2492 packet
->veteran
= pv
->data
.v_int
;
2498 log_error("%s(): Unhandled request to pack value of property "
2499 "%d (%s) from object of type \"%s\".", __FUNCTION__
,
2500 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2505 struct packet_edit_city
*packet
= pd
.city
;
2508 case OPID_CITY_NAME
:
2509 sz_strlcpy(packet
->name
, pv
->data
.v_string
);
2511 case OPID_CITY_SIZE
:
2512 packet
->size
= pv
->data
.v_int
;
2514 case OPID_CITY_HISTORY
:
2515 packet
->history
= pv
->data
.v_int
;
2517 case OPID_CITY_FOOD_STOCK
:
2518 packet
->food_stock
= pv
->data
.v_int
;
2520 case OPID_CITY_SHIELD_STOCK
:
2521 packet
->shield_stock
= pv
->data
.v_int
;
2523 case OPID_CITY_BUILDINGS
:
2527 for (i
= 0; i
< B_LAST
; i
++) {
2528 packet
->built
[i
] = pv
->data
.v_built
[i
].turn
;
2536 log_error("%s(): Unhandled request to pack value of property "
2537 "%d (%s) from object of type \"%s\".", __FUNCTION__
,
2538 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2541 case OBJTYPE_PLAYER
:
2543 struct packet_edit_player
*packet
= pd
.player
;
2546 case OPID_PLAYER_NAME
:
2547 sz_strlcpy(packet
->name
, pv
->data
.v_string
);
2549 case OPID_PLAYER_NATION
:
2550 packet
->nation
= nation_index(pv
->data
.v_nation
);
2552 case OPID_PLAYER_GOV
:
2553 packet
->government
= government_index(pv
->data
.v_gov
);
2555 case OPID_PLAYER_INVENTIONS
:
2556 advance_index_iterate(A_FIRST
, tech
) {
2557 packet
->inventions
[tech
] = pv
->data
.v_inventions
[tech
];
2558 } advance_index_iterate_end
;
2560 case OPID_PLAYER_SCENARIO_RESERVED
:
2561 packet
->scenario_reserved
= pv
->data
.v_bool
;
2563 case OPID_PLAYER_SCIENCE
:
2564 packet
->bulbs_researched
= pv
->data
.v_int
;
2566 case OPID_PLAYER_GOLD
:
2567 packet
->gold
= pv
->data
.v_int
;
2573 log_error("%s(): Unhandled request to pack value of property "
2574 "%d (%s) from object of type \"%s\".", __FUNCTION__
,
2575 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2580 struct packet_edit_game
*packet
= pd
.game
.game
;
2583 case OPID_GAME_YEAR
:
2584 packet
->year
= pv
->data
.v_int
;
2586 case OPID_GAME_SCENARIO
:
2587 packet
->scenario
= pv
->data
.v_bool
;
2589 case OPID_GAME_SCENARIO_NAME
:
2590 sz_strlcpy(packet
->scenario_name
, pv
->data
.v_const_string
);
2592 case OPID_GAME_SCENARIO_AUTHORS
:
2593 sz_strlcpy(packet
->scenario_authors
, pv
->data
.v_const_string
);
2595 case OPID_GAME_SCENARIO_DESC
:
2596 sz_strlcpy(pd
.game
.desc
->scenario_desc
, pv
->data
.v_const_string
);
2598 case OPID_GAME_SCENARIO_RANDSTATE
:
2599 packet
->scenario_random
= pv
->data
.v_bool
;
2601 case OPID_GAME_SCENARIO_PLAYERS
:
2602 packet
->scenario_players
= pv
->data
.v_bool
;
2604 case OPID_GAME_STARTPOS_NATIONS
:
2605 packet
->startpos_nations
= pv
->data
.v_bool
;
2607 case OPID_GAME_PREVENT_CITIES
:
2608 packet
->prevent_new_cities
= pv
->data
.v_bool
;
2610 case OPID_GAME_LAKE_FLOODING
:
2611 packet
->lake_flooding
= pv
->data
.v_bool
;
2613 case OPID_GAME_RULESET_LOCKED
:
2614 packet
->ruleset_locked
= pv
->data
.v_bool
;
2620 log_error("%s(): Unhandled request to pack value of property "
2621 "%d (%s) from object of type \"%s\".", __FUNCTION__
,
2622 propid
, objprop_get_name(op
), objtype_get_name(objtype
));
2629 log_error("%s(): Unhandled request for object type \"%s\" (nb %d).",
2630 __FUNCTION__
, objtype_get_name(objtype
), objtype
);
2634 /****************************************************************************
2635 Sets the row reference in a GtkTreeModel of this objbind.
2636 ****************************************************************************/
2637 static void objbind_set_rowref(struct objbind
*ob
,
2638 GtkTreeRowReference
*rr
)
2646 /****************************************************************************
2647 Returns the row reference of this objbind, or NULL if not applicable.
2648 ****************************************************************************/
2649 static GtkTreeRowReference
*objbind_get_rowref(struct objbind
*ob
)
2657 /****************************************************************************
2658 Returns the unique property identifier for this object property.
2659 ****************************************************************************/
2660 static int objprop_get_id(const struct objprop
*op
)
2668 /****************************************************************************
2669 Returns the GType that this object property renders as in a GtkTreeView.
2670 Returning G_TYPE_NONE indicates that this property cannot be viewed
2673 NB: This must correspond to the actions in property_page_set_store_value.
2674 ****************************************************************************/
2675 static GType
objprop_get_gtype(const struct objprop
*op
)
2677 fc_assert_ret_val(NULL
!= op
, G_TYPE_NONE
);
2679 switch (op
->valtype
) {
2681 case VALTYPE_TILE_VISION_DATA
:
2686 /* We want to show it as translated string, not as untranslated G_TYPE_BOOLEAN */
2687 return G_TYPE_STRING
;
2688 case VALTYPE_STRING
:
2689 case VALTYPE_BUILT_ARRAY
:
2690 case VALTYPE_INVENTIONS_ARRAY
:
2691 case VALTYPE_BV_SPECIAL
:
2692 case VALTYPE_BV_ROADS
:
2693 case VALTYPE_BV_BASES
:
2694 case VALTYPE_NATION_HASH
:
2695 return G_TYPE_STRING
;
2696 case VALTYPE_PIXBUF
:
2697 case VALTYPE_NATION
:
2699 return GDK_TYPE_PIXBUF
;
2701 log_error("%s(): Unhandled value type %d.", __FUNCTION__
, op
->valtype
);
2705 /****************************************************************************
2706 Returns the value type of this property value (one of enum value_types).
2707 ****************************************************************************/
2708 static enum value_types
objprop_get_valtype(const struct objprop
*op
)
2711 return VALTYPE_NONE
;
2716 /****************************************************************************
2717 Returns TRUE if this object property can be viewed in a GtkTreeView.
2718 ****************************************************************************/
2719 static bool objprop_show_in_listview(const struct objprop
*op
)
2724 return op
->flags
& OPF_IN_LISTVIEW
;
2727 /****************************************************************************
2728 Returns TRUE if this object property can create and use a property widget.
2729 ****************************************************************************/
2730 static bool objprop_has_widget(const struct objprop
*op
)
2735 return op
->flags
& OPF_HAS_WIDGET
;
2738 /****************************************************************************
2739 Returns a the string corresponding to the attribute type name required
2740 for gtk_tree_view_column_new_with_attributes according to this objprop's
2741 GType value. May return NULL if it does not make sense for this
2743 ****************************************************************************/
2744 static const char *objprop_get_attribute_type_string(const struct objprop
*op
)
2752 gtype
= objprop_get_gtype(op
);
2753 if (gtype
== G_TYPE_INT
|| gtype
== G_TYPE_STRING
2754 || gtype
== G_TYPE_BOOLEAN
) {
2756 } else if (gtype
== GDK_TYPE_PIXBUF
) {
2763 /****************************************************************************
2764 Store the column id of the list store that this object property can be
2765 viewed in. This should generally only be changed set once, when the
2766 object property's associated list view is created.
2767 NB: This is the column id in the model, not the view.
2768 ****************************************************************************/
2769 static void objprop_set_column_id(struct objprop
*op
, int col_id
)
2774 op
->column_id
= col_id
;
2777 /****************************************************************************
2778 Returns the column id in the list store for this object property.
2779 May return -1 if not applicable or if not yet set.
2780 NB: This is the column id in the model, not the view.
2781 ****************************************************************************/
2782 static int objprop_get_column_id(const struct objprop
*op
)
2787 return op
->column_id
;
2790 /****************************************************************************
2791 Sets the view column reference for later convenience.
2792 ****************************************************************************/
2793 static void objprop_set_treeview_column(struct objprop
*op
,
2794 GtkTreeViewColumn
*col
)
2799 op
->view_column
= col
;
2802 /****************************************************************************
2803 Returns the previously stored tree view column reference, or NULL if none
2805 ****************************************************************************/
2806 static GtkTreeViewColumn
*objprop_get_treeview_column(const struct objprop
*op
)
2811 return op
->view_column
;
2814 /****************************************************************************
2815 Returns the string name of this object property.
2816 ****************************************************************************/
2817 static const char *objprop_get_name(const struct objprop
*op
)
2825 /****************************************************************************
2826 Return a description (translated) of the property.
2827 ****************************************************************************/
2828 static const char *objprop_get_tooltip(const struct objprop
*op
)
2836 /****************************************************************************
2837 Create and return a cell renderer corresponding to this object property,
2838 suitable to be used with a tree view. May return NULL if this object
2839 property cannot exist in a list store.
2840 ****************************************************************************/
2841 static GtkCellRenderer
*objprop_create_cell_renderer(const struct objprop
*op
)
2843 GtkCellRenderer
*cell
= NULL
;
2846 gtype
= objprop_get_gtype(op
);
2848 if (gtype
== G_TYPE_INT
|| gtype
== G_TYPE_STRING
2849 || gtype
== G_TYPE_BOOLEAN
) {
2850 cell
= gtk_cell_renderer_text_new();
2851 } else if (gtype
== GDK_TYPE_PIXBUF
) {
2852 cell
= gtk_cell_renderer_pixbuf_new();
2858 /****************************************************************************
2859 Return TRUE if the given object property can be sorted (i.e. in the list
2861 ****************************************************************************/
2862 static bool objprop_is_sortable(const struct objprop
*op
)
2868 gtype
= objprop_get_gtype(op
);
2869 return gtype
== G_TYPE_INT
|| gtype
== G_TYPE_STRING
;
2872 /****************************************************************************
2873 Return TRUE if the given object property cannot be edited (i.e. it is
2874 displayed information only).
2875 ****************************************************************************/
2876 static bool objprop_is_readonly(const struct objprop
*op
)
2881 return !(op
->flags
& OPF_EDITABLE
);
2884 /****************************************************************************
2885 Callback for entry widget 'changed' signal.
2886 ****************************************************************************/
2887 static void objprop_widget_entry_changed(GtkEntry
*entry
, gpointer userdata
)
2890 struct property_page
*pp
;
2891 struct propval value
= {{0,}, VALTYPE_STRING
, FALSE
};
2894 pp
= objprop_get_property_page(op
);
2895 value
.data
.v_const_string
= gtk_entry_get_text(entry
);
2897 property_page_change_value(pp
, op
, &value
);
2900 /****************************************************************************
2901 Callback for spin button widget 'value-changed' signal.
2902 ****************************************************************************/
2903 static void objprop_widget_spin_button_changed(GtkSpinButton
*spin
,
2907 struct property_page
*pp
;
2908 struct propval value
= {{0,}, VALTYPE_INT
, FALSE
};
2911 pp
= objprop_get_property_page(op
);
2912 value
.data
.v_int
= gtk_spin_button_get_value_as_int(spin
);
2914 property_page_change_value(pp
, op
, &value
);
2917 /****************************************************************************
2918 Callback for toggle type button widget 'toggled' signal.
2919 ****************************************************************************/
2920 static void objprop_widget_toggle_button_changed(GtkToggleButton
*button
,
2924 struct property_page
*pp
;
2925 struct propval value
= {{0,}, VALTYPE_BOOL
, FALSE
};
2928 pp
= objprop_get_property_page(op
);
2929 value
.data
.v_bool
= gtk_toggle_button_get_active(button
);
2931 property_page_change_value(pp
, op
, &value
);
2934 /****************************************************************************
2935 Create the gtk widget used to edit or display this object property.
2936 ****************************************************************************/
2937 static void objprop_setup_widget(struct objprop
*op
)
2939 GtkWidget
*ebox
, *hbox
, *hbox2
, *label
, *image
, *entry
, *spin
, *button
;
2940 struct extviewer
*ev
= NULL
;
2941 enum object_property_ids propid
;
2947 if (!objprop_has_widget(op
)) {
2951 ebox
= gtk_event_box_new();
2954 hbox
= gtk_grid_new();
2955 gtk_grid_set_column_spacing(GTK_GRID(hbox
), 4);
2957 gtk_container_add(GTK_CONTAINER(ebox
), hbox
);
2959 label
= gtk_label_new(objprop_get_name(op
));
2960 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
2961 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
2962 gtk_container_add(GTK_CONTAINER(hbox
), label
);
2963 objprop_set_child_widget(op
, "name-label", label
);
2965 propid
= objprop_get_id(op
);
2968 case OPID_TILE_INDEX
:
2971 case OPID_TILE_NAT_X
:
2972 case OPID_TILE_NAT_Y
:
2973 case OPID_TILE_CONTINENT
:
2974 case OPID_TILE_TERRAIN
:
2975 case OPID_TILE_RESOURCE
:
2977 case OPID_STARTPOS_XY
:
2980 case OPID_UNIT_TYPE
:
2983 case OPID_PLAYER_AGE
:
2984 #ifdef FREECIV_DEBUG
2985 case OPID_TILE_ADDRESS
:
2986 case OPID_UNIT_ADDRESS
:
2987 case OPID_CITY_ADDRESS
:
2988 case OPID_PLAYER_ADDRESS
:
2989 #endif /* FREECIV_DEBUG */
2990 label
= gtk_label_new(NULL
);
2991 gtk_widget_set_hexpand(label
, TRUE
);
2992 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
2993 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
2994 gtk_container_add(GTK_CONTAINER(hbox
), label
);
2995 objprop_set_child_widget(op
, "value-label", label
);
2998 case OPID_TILE_IMAGE
:
2999 case OPID_STARTPOS_IMAGE
:
3000 case OPID_UNIT_IMAGE
:
3001 case OPID_CITY_IMAGE
:
3002 image
= gtk_image_new();
3003 gtk_widget_set_hexpand(image
, TRUE
);
3004 gtk_widget_set_halign(image
, GTK_ALIGN_START
);
3005 gtk_widget_set_valign(image
, GTK_ALIGN_CENTER
);
3006 gtk_container_add(GTK_CONTAINER(hbox
), image
);
3007 objprop_set_child_widget(op
, "image", image
);
3010 case OPID_CITY_NAME
:
3011 case OPID_PLAYER_NAME
:
3012 case OPID_GAME_SCENARIO_NAME
:
3013 case OPID_TILE_LABEL
:
3014 entry
= gtk_entry_new();
3015 gtk_widget_set_hexpand(entry
, TRUE
);
3016 gtk_widget_set_halign(entry
, GTK_ALIGN_END
);
3017 gtk_entry_set_width_chars(GTK_ENTRY(entry
), 8);
3018 g_signal_connect(entry
, "changed",
3019 G_CALLBACK(objprop_widget_entry_changed
), op
);
3020 gtk_container_add(GTK_CONTAINER(hbox
), entry
);
3021 objprop_set_child_widget(op
, "entry", entry
);
3024 case OPID_UNIT_MOVES_LEFT
:
3025 case OPID_CITY_SIZE
:
3026 case OPID_CITY_HISTORY
:
3027 case OPID_CITY_SHIELD_STOCK
:
3028 case OPID_PLAYER_SCIENCE
:
3029 case OPID_PLAYER_GOLD
:
3030 case OPID_GAME_YEAR
:
3031 spin
= gtk_spin_button_new_with_range(0.0, 100.0, 1.0);
3032 gtk_widget_set_hexpand(spin
, TRUE
);
3033 gtk_widget_set_halign(spin
, GTK_ALIGN_END
);
3034 g_signal_connect(spin
, "value-changed",
3035 G_CALLBACK(objprop_widget_spin_button_changed
), op
);
3036 gtk_container_add(GTK_CONTAINER(hbox
), spin
);
3037 objprop_set_child_widget(op
, "spin", spin
);
3040 case OPID_UNIT_FUEL
:
3042 case OPID_UNIT_VETERAN
:
3043 case OPID_CITY_FOOD_STOCK
:
3044 hbox2
= gtk_grid_new();
3045 gtk_widget_set_hexpand(hbox2
, TRUE
);
3046 gtk_widget_set_halign(hbox2
, GTK_ALIGN_END
);
3047 gtk_grid_set_column_spacing(GTK_GRID(hbox2
), 4);
3048 gtk_container_add(GTK_CONTAINER(hbox
), hbox2
);
3049 spin
= gtk_spin_button_new_with_range(0.0, 100.0, 1.0);
3050 g_signal_connect(spin
, "value-changed",
3051 G_CALLBACK(objprop_widget_spin_button_changed
), op
);
3052 gtk_container_add(GTK_CONTAINER(hbox2
), spin
);
3053 objprop_set_child_widget(op
, "spin", spin
);
3054 label
= gtk_label_new(NULL
);
3055 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
3056 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
3057 gtk_container_add(GTK_CONTAINER(hbox2
), label
);
3058 objprop_set_child_widget(op
, "max-value-label", label
);
3061 case OPID_TILE_SPECIALS
:
3062 case OPID_TILE_ROADS
:
3063 case OPID_TILE_BASES
:
3064 case OPID_TILE_VISION
:
3065 case OPID_STARTPOS_NATIONS
:
3066 case OPID_CITY_BUILDINGS
:
3067 case OPID_PLAYER_NATION
:
3068 case OPID_PLAYER_GOV
:
3069 case OPID_PLAYER_INVENTIONS
:
3070 case OPID_GAME_SCENARIO_AUTHORS
:
3071 case OPID_GAME_SCENARIO_DESC
:
3072 ev
= extviewer_new(op
);
3073 objprop_set_extviewer(op
, ev
);
3074 gtk_widget_set_hexpand(extviewer_get_panel_widget(ev
), TRUE
);
3075 gtk_widget_set_halign(extviewer_get_panel_widget(ev
), GTK_ALIGN_END
);
3076 gtk_container_add(GTK_CONTAINER(hbox
), extviewer_get_panel_widget(ev
));
3077 property_page_add_extviewer(objprop_get_property_page(op
), ev
);
3080 case OPID_STARTPOS_EXCLUDE
:
3081 case OPID_UNIT_MOVED
:
3082 case OPID_UNIT_DONE_MOVING
:
3083 case OPID_GAME_SCENARIO
:
3084 case OPID_GAME_SCENARIO_RANDSTATE
:
3085 case OPID_GAME_SCENARIO_PLAYERS
:
3086 case OPID_GAME_STARTPOS_NATIONS
:
3087 case OPID_GAME_PREVENT_CITIES
:
3088 case OPID_GAME_LAKE_FLOODING
:
3089 case OPID_GAME_RULESET_LOCKED
:
3090 case OPID_PLAYER_SCENARIO_RESERVED
:
3091 button
= gtk_check_button_new();
3092 gtk_widget_set_hexpand(button
, TRUE
);
3093 gtk_widget_set_halign(button
, GTK_ALIGN_END
);
3094 g_signal_connect(button
, "toggled",
3095 G_CALLBACK(objprop_widget_toggle_button_changed
), op
);
3096 gtk_container_add(GTK_CONTAINER(hbox
), button
);
3097 objprop_set_child_widget(op
, "checkbutton", button
);
3101 log_error("%s(): Unhandled request to create widget for property %d (%s).",
3102 __FUNCTION__
, propid
, objprop_get_name(op
));
3105 /****************************************************************************
3106 Refresh the display/edit widget corresponding to this object property
3107 according to the value of the bound object. If a stored modified value
3108 exists, then check it against the object's current value and remove it
3111 If 'ob' is NULL, then clear the widget.
3112 ****************************************************************************/
3113 static void objprop_refresh_widget(struct objprop
*op
,
3116 GtkWidget
*w
, *label
, *image
, *entry
, *spin
, *button
;
3117 struct extviewer
*ev
;
3120 enum object_property_ids propid
;
3121 double min
, max
, step
, big_step
;
3124 if (!op
|| !objprop_has_widget(op
)) {
3128 w
= objprop_get_widget(op
);
3133 propid
= objprop_get_id(op
);
3135 /* NB: We must take care to propval_free the return value of
3136 * objbind_get_value_from_object, since it always makes a
3137 * copy, but to NOT free the result of objbind_get_modified_value
3138 * since it returns its own stored value. */
3139 pv
= objbind_get_value_from_object(ob
, op
);
3140 modified
= objbind_property_is_modified(ob
, op
);
3142 if (pv
&& modified
) {
3143 struct propval
*pv_mod
;
3145 pv_mod
= objbind_get_modified_value(ob
, op
);
3147 if (propval_equal(pv
, pv_mod
)) {
3148 objbind_clear_modified_value(ob
, op
);
3161 case OPID_TILE_IMAGE
:
3162 case OPID_STARTPOS_IMAGE
:
3163 case OPID_UNIT_IMAGE
:
3164 case OPID_CITY_IMAGE
:
3165 image
= objprop_get_child_widget(op
, "image");
3167 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), pv
->data
.v_pixbuf
);
3169 gtk_image_set_from_pixbuf(GTK_IMAGE(image
), NULL
);
3174 case OPID_TILE_TERRAIN
:
3175 case OPID_TILE_RESOURCE
:
3176 case OPID_STARTPOS_XY
:
3178 case OPID_UNIT_TYPE
:
3180 #ifdef FREECIV_DEBUG
3181 case OPID_TILE_ADDRESS
:
3182 case OPID_UNIT_ADDRESS
:
3183 case OPID_CITY_ADDRESS
:
3184 case OPID_PLAYER_ADDRESS
:
3185 #endif /* FREECIV_DEBUG */
3186 label
= objprop_get_child_widget(op
, "value-label");
3188 gtk_label_set_text(GTK_LABEL(label
), pv
->data
.v_string
);
3190 gtk_label_set_text(GTK_LABEL(label
), NULL
);
3194 case OPID_TILE_INDEX
:
3197 case OPID_TILE_NAT_X
:
3198 case OPID_TILE_NAT_Y
:
3199 case OPID_TILE_CONTINENT
:
3202 case OPID_PLAYER_AGE
:
3203 label
= objprop_get_child_widget(op
, "value-label");
3207 fc_snprintf(agebuf
, sizeof(agebuf
), "%d", pv
->data
.v_int
);
3208 gtk_label_set_text(GTK_LABEL(label
), agebuf
);
3210 gtk_label_set_text(GTK_LABEL(label
), NULL
);
3214 case OPID_CITY_NAME
:
3215 case OPID_PLAYER_NAME
:
3216 case OPID_GAME_SCENARIO_NAME
:
3217 case OPID_TILE_LABEL
:
3218 entry
= objprop_get_child_widget(op
, "entry");
3220 gtk_entry_set_text(GTK_ENTRY(entry
), pv
->data
.v_string
);
3222 gtk_entry_set_text(GTK_ENTRY(entry
), "");
3224 gtk_widget_set_sensitive(entry
, pv
!= NULL
);
3227 case OPID_UNIT_MOVES_LEFT
:
3228 case OPID_CITY_SIZE
:
3229 case OPID_CITY_HISTORY
:
3230 case OPID_CITY_SHIELD_STOCK
:
3231 case OPID_PLAYER_SCIENCE
:
3232 case OPID_PLAYER_GOLD
:
3233 case OPID_GAME_YEAR
:
3234 spin
= objprop_get_child_widget(op
, "spin");
3236 disable_gobject_callback(G_OBJECT(spin
),
3237 G_CALLBACK(objprop_widget_spin_button_changed
));
3238 if (objbind_get_allowed_value_span(ob
, op
, &min
, &max
,
3239 &step
, &big_step
)) {
3240 gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin
), min
, max
);
3241 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(spin
),
3244 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin
), pv
->data
.v_int
);
3245 enable_gobject_callback(G_OBJECT(spin
),
3246 G_CALLBACK(objprop_widget_spin_button_changed
));
3248 gtk_widget_set_sensitive(spin
, pv
!= NULL
);
3251 case OPID_UNIT_FUEL
:
3253 case OPID_UNIT_VETERAN
:
3254 case OPID_CITY_FOOD_STOCK
:
3255 spin
= objprop_get_child_widget(op
, "spin");
3256 label
= objprop_get_child_widget(op
, "max-value-label");
3258 disable_gobject_callback(G_OBJECT(spin
),
3259 G_CALLBACK(objprop_widget_spin_button_changed
));
3260 if (objbind_get_allowed_value_span(ob
, op
, &min
, &max
,
3261 &step
, &big_step
)) {
3262 gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin
), min
, max
);
3263 gtk_spin_button_set_increments(GTK_SPIN_BUTTON(spin
),
3265 fc_snprintf(buf
, sizeof(buf
), "/%d", (int) max
);
3266 gtk_label_set_text(GTK_LABEL(label
), buf
);
3268 gtk_label_set_text(GTK_LABEL(label
), NULL
);
3270 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin
), pv
->data
.v_int
);
3271 enable_gobject_callback(G_OBJECT(spin
),
3272 G_CALLBACK(objprop_widget_spin_button_changed
));
3274 gtk_label_set_text(GTK_LABEL(label
), NULL
);
3276 gtk_widget_set_sensitive(spin
, pv
!= NULL
);
3279 case OPID_TILE_SPECIALS
:
3280 case OPID_TILE_ROADS
:
3281 case OPID_TILE_BASES
:
3282 case OPID_TILE_VISION
:
3283 case OPID_STARTPOS_NATIONS
:
3284 case OPID_CITY_BUILDINGS
:
3285 case OPID_PLAYER_NATION
:
3286 case OPID_PLAYER_GOV
:
3287 case OPID_PLAYER_INVENTIONS
:
3288 case OPID_GAME_SCENARIO_AUTHORS
:
3289 case OPID_GAME_SCENARIO_DESC
:
3290 ev
= objprop_get_extviewer(op
);
3292 extviewer_refresh_widgets(ev
, pv
);
3294 extviewer_clear_widgets(ev
);
3298 case OPID_STARTPOS_EXCLUDE
:
3299 case OPID_UNIT_MOVED
:
3300 case OPID_UNIT_DONE_MOVING
:
3301 case OPID_GAME_SCENARIO
:
3302 case OPID_GAME_SCENARIO_RANDSTATE
:
3303 case OPID_GAME_SCENARIO_PLAYERS
:
3304 case OPID_GAME_STARTPOS_NATIONS
:
3305 case OPID_GAME_PREVENT_CITIES
:
3306 case OPID_GAME_LAKE_FLOODING
:
3307 case OPID_GAME_RULESET_LOCKED
:
3308 case OPID_PLAYER_SCENARIO_RESERVED
:
3309 button
= objprop_get_child_widget(op
, "checkbutton");
3310 disable_gobject_callback(G_OBJECT(button
),
3311 G_CALLBACK(objprop_widget_toggle_button_changed
));
3313 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
),
3316 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button
), FALSE
);
3318 enable_gobject_callback(G_OBJECT(button
),
3319 G_CALLBACK(objprop_widget_toggle_button_changed
));
3320 gtk_widget_set_sensitive(button
, pv
!= NULL
);
3328 label
= objprop_get_child_widget(op
, "name-label");
3330 const char *name
= objprop_get_name(op
);
3334 fc_snprintf(namebuf
, sizeof(namebuf
),
3335 "<span foreground=\"red\">%s</span>", name
);
3336 gtk_label_set_markup(GTK_LABEL(label
), namebuf
);
3338 gtk_label_set_text(GTK_LABEL(label
), name
);
3343 /****************************************************************************
3344 Returns the gtk widget used to display or edit this property, or NULL
3346 ****************************************************************************/
3347 static GtkWidget
*objprop_get_widget(struct objprop
*op
)
3353 objprop_setup_widget(op
);
3358 /****************************************************************************
3359 Stores the given widget under the given name. This function will have no
3360 effect if objprop_get_widget does not return a valid GtkWidget instance.
3361 ****************************************************************************/
3362 static void objprop_set_child_widget(struct objprop
*op
,
3363 const char *widget_name
,
3368 if (!op
|| !widget_name
|| !widget
) {
3372 w
= objprop_get_widget(op
);
3374 log_error("Cannot store child widget %p under name "
3375 "\"%s\" using objprop_set_child_widget for object "
3376 "property %d (%s) because objprop_get_widget does "
3377 "not return a valid widget.",
3378 widget
, widget_name
, objprop_get_id(op
), objprop_get_name(op
));
3382 g_object_set_data(G_OBJECT(w
), widget_name
, widget
);
3385 /****************************************************************************
3386 Retrieves the widget stored under the given name, or returns NULL and
3387 logs an error message if not found.
3388 ****************************************************************************/
3389 static GtkWidget
*objprop_get_child_widget(struct objprop
*op
,
3390 const char *widget_name
)
3392 GtkWidget
*w
, *child
;
3394 if (!op
|| !widget_name
) {
3398 w
= objprop_get_widget(op
);
3400 log_error("Cannot retrieve child widget under name "
3401 "\"%s\" using objprop_get_child_widget for object "
3402 "property %d (%s) because objprop_get_widget does "
3403 "not return a valid widget.",
3404 widget_name
, objprop_get_id(op
), objprop_get_name(op
));
3408 child
= g_object_get_data(G_OBJECT(w
), widget_name
);
3410 log_error("Child widget \"%s\" not found for object "
3411 "property %d (%s) via objprop_get_child_widget.",
3412 widget_name
, objprop_get_id(op
), objprop_get_name(op
));
3419 /****************************************************************************
3420 Store the extviewer struct for later retrieval.
3421 ****************************************************************************/
3422 static void objprop_set_extviewer(struct objprop
*op
,
3423 struct extviewer
*ev
)
3431 /****************************************************************************
3432 Return the stored extviewer, or NULL if none exists.
3433 ****************************************************************************/
3434 static struct extviewer
*objprop_get_extviewer(struct objprop
*op
)
3439 return op
->extviewer
;
3442 /****************************************************************************
3443 Get the property page in which this property is installed.
3444 ****************************************************************************/
3445 static struct property_page
*objprop_get_property_page(const struct objprop
*op
)
3450 return op
->parent_page
;
3453 /****************************************************************************
3454 Create a new object property.
3455 ****************************************************************************/
3456 static struct objprop
*objprop_new(int id
,
3458 const char *tooltip
,
3459 enum object_property_flags flags
,
3460 enum value_types valtype
,
3461 struct property_page
*parent
)
3465 op
= fc_calloc(1, sizeof(*op
));
3468 op
->tooltip
= tooltip
;
3470 op
->valtype
= valtype
;
3472 op
->parent_page
= parent
;
3477 /****************************************************************************
3478 Create "extended property viewer". This is used for viewing/editing
3479 complex property values (e.g. arrays, bitvectors, etc.).
3480 ****************************************************************************/
3481 static struct extviewer
*extviewer_new(struct objprop
*op
)
3483 struct extviewer
*ev
;
3484 GtkWidget
*hbox
, *vbox
, *label
, *button
, *scrollwin
, *image
;
3485 GtkWidget
*view
= NULL
;
3486 GtkTreeSelection
*sel
;
3487 GtkListStore
*store
= NULL
;
3488 GtkTextBuffer
*textbuf
= NULL
;
3490 enum object_property_ids propid
;
3497 ev
= fc_calloc(1, sizeof(*ev
));
3500 propid
= objprop_get_id(op
);
3503 /* Create the panel widget. */
3506 case OPID_TILE_SPECIALS
:
3507 case OPID_TILE_ROADS
:
3508 case OPID_TILE_BASES
:
3509 case OPID_STARTPOS_NATIONS
:
3510 case OPID_CITY_BUILDINGS
:
3511 case OPID_PLAYER_INVENTIONS
:
3512 case OPID_GAME_SCENARIO_AUTHORS
:
3513 case OPID_GAME_SCENARIO_DESC
:
3514 hbox
= gtk_grid_new();
3515 gtk_grid_set_column_spacing(GTK_GRID(hbox
), 4);
3516 ev
->panel_widget
= hbox
;
3518 label
= gtk_label_new(NULL
);
3519 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
3520 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
3521 gtk_container_add(GTK_CONTAINER(hbox
), label
);
3522 ev
->panel_label
= label
;
3525 case OPID_PLAYER_NATION
:
3526 case OPID_PLAYER_GOV
:
3527 vbox
= gtk_grid_new();
3528 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox
),
3529 GTK_ORIENTATION_VERTICAL
);
3530 gtk_grid_set_row_spacing(GTK_GRID(vbox
), 4);
3531 ev
->panel_widget
= vbox
;
3533 label
= gtk_label_new(NULL
);
3534 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
3535 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
3536 gtk_container_add(GTK_CONTAINER(vbox
), label
);
3537 ev
->panel_label
= label
;
3539 hbox
= gtk_grid_new();
3540 gtk_grid_set_column_spacing(GTK_GRID(hbox
), 4);
3541 gtk_container_add(GTK_CONTAINER(vbox
), hbox
);
3543 image
= gtk_image_new();
3544 gtk_widget_set_halign(image
, GTK_ALIGN_START
);
3545 gtk_widget_set_valign(image
, GTK_ALIGN_CENTER
);
3546 gtk_container_add(GTK_CONTAINER(hbox
), image
);
3547 ev
->panel_image
= image
;
3550 case OPID_TILE_VISION
:
3551 hbox
= gtk_grid_new();
3552 gtk_grid_set_column_spacing(GTK_GRID(hbox
), 4);
3553 ev
->panel_widget
= hbox
;
3557 log_error("Unhandled request to create panel widget "
3558 "for property %d (%s) in extviewer_new().",
3559 propid
, objprop_get_name(op
));
3560 hbox
= gtk_grid_new();
3561 gtk_grid_set_column_spacing(GTK_GRID(hbox
), 4);
3562 ev
->panel_widget
= hbox
;
3566 if (objprop_is_readonly(op
)) {
3567 button
= gtk_button_new_with_label(Q_("?verb:View"));
3569 button
= gtk_button_new_with_label(_("Edit"));
3571 g_signal_connect(button
, "clicked",
3572 G_CALLBACK(extviewer_panel_button_clicked
), ev
);
3573 gtk_container_add(GTK_CONTAINER(hbox
), button
);
3574 ev
->panel_button
= button
;
3577 /* Create the data store. */
3580 case OPID_TILE_SPECIALS
:
3581 case OPID_TILE_ROADS
:
3582 case OPID_TILE_BASES
:
3583 case OPID_PLAYER_INVENTIONS
:
3584 store
= gtk_list_store_new(3, G_TYPE_BOOLEAN
, G_TYPE_INT
,
3587 case OPID_TILE_VISION
:
3588 num_cols
= 3 + 1 + V_COUNT
;
3589 gtypes
= fc_malloc(num_cols
* sizeof(GType
));
3590 gtypes
[0] = G_TYPE_INT
; /* player number */
3591 gtypes
[1] = GDK_TYPE_PIXBUF
; /* player flag */
3592 gtypes
[2] = G_TYPE_STRING
; /* player name */
3593 gtypes
[3] = G_TYPE_BOOLEAN
; /* tile_known */
3594 vision_layer_iterate(v
) {
3595 gtypes
[4 + v
] = G_TYPE_BOOLEAN
; /* tile_seen[v] */
3596 } vision_layer_iterate_end
;
3597 store
= gtk_list_store_newv(num_cols
, gtypes
);
3600 case OPID_CITY_BUILDINGS
:
3601 store
= gtk_list_store_new(4, G_TYPE_BOOLEAN
, G_TYPE_INT
,
3602 G_TYPE_STRING
, G_TYPE_STRING
);
3604 case OPID_STARTPOS_NATIONS
:
3605 case OPID_PLAYER_NATION
:
3606 case OPID_PLAYER_GOV
:
3607 store
= gtk_list_store_new(4, G_TYPE_BOOLEAN
, G_TYPE_INT
,
3608 GDK_TYPE_PIXBUF
, G_TYPE_STRING
);
3610 case OPID_GAME_SCENARIO_AUTHORS
:
3611 case OPID_GAME_SCENARIO_DESC
:
3612 textbuf
= gtk_text_buffer_new(NULL
);
3615 log_error("Unhandled request to create data store "
3616 "for property %d (%s) in extviewer_new().",
3617 propid
, objprop_get_name(op
));
3622 ev
->textbuf
= textbuf
;
3624 /* Create the view widget. */
3626 vbox
= gtk_grid_new();
3627 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox
),
3628 GTK_ORIENTATION_VERTICAL
);
3629 gtk_grid_set_row_spacing(GTK_GRID(vbox
), 4);
3630 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
3631 ev
->view_widget
= vbox
;
3633 label
= gtk_label_new(objprop_get_name(op
));
3634 gtk_widget_set_halign(label
, GTK_ALIGN_START
);
3635 gtk_widget_set_valign(label
, GTK_ALIGN_CENTER
);
3636 gtk_container_add(GTK_CONTAINER(vbox
), label
);
3637 ev
->view_label
= label
;
3639 if (store
|| textbuf
) {
3640 scrollwin
= gtk_scrolled_window_new(NULL
, NULL
);
3641 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin
),
3642 GTK_SHADOW_ETCHED_IN
);
3643 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin
),
3644 GTK_POLICY_AUTOMATIC
,
3645 GTK_POLICY_AUTOMATIC
);
3646 gtk_container_add(GTK_CONTAINER(vbox
), scrollwin
);
3649 view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(store
));
3650 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view
), TRUE
);
3651 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
3652 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
3654 const bool editable
= !objprop_is_readonly(op
);
3655 view
= gtk_text_view_new_with_buffer(textbuf
);
3656 gtk_text_view_set_editable(GTK_TEXT_VIEW(view
), editable
);
3657 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view
), editable
);
3659 gtk_widget_set_hexpand(view
, TRUE
);
3660 gtk_widget_set_vexpand(view
, TRUE
);
3662 gtk_container_add(GTK_CONTAINER(scrollwin
), view
);
3667 case OPID_TILE_SPECIALS
:
3668 case OPID_TILE_ROADS
:
3669 case OPID_TILE_BASES
:
3670 /* TRANS: As in "this tile special is present". */
3671 add_column(view
, 0, _("Present"), G_TYPE_BOOLEAN
, TRUE
, FALSE
,
3672 G_CALLBACK(extviewer_view_cell_toggled
), ev
);
3673 add_column(view
, 1, _("ID"), G_TYPE_INT
,
3674 FALSE
, FALSE
, NULL
, NULL
);
3675 add_column(view
, 2, _("Name"), G_TYPE_STRING
,
3676 FALSE
, FALSE
, NULL
, NULL
);
3679 case OPID_TILE_VISION
:
3680 add_column(view
, 0, _("ID"), G_TYPE_INT
,
3681 FALSE
, FALSE
, NULL
, NULL
);
3682 add_column(view
, 1, _("Nation"), GDK_TYPE_PIXBUF
,
3683 FALSE
, FALSE
, NULL
, NULL
);
3684 add_column(view
, 2, _("Name"), G_TYPE_STRING
,
3685 FALSE
, FALSE
, NULL
, NULL
);
3686 add_column(view
, 3, _("Known"), G_TYPE_BOOLEAN
,
3687 FALSE
, FALSE
, NULL
, NULL
);
3688 vision_layer_iterate(v
) {
3689 add_column(view
, 4 + v
, vision_layer_get_name(v
),
3690 G_TYPE_BOOLEAN
, FALSE
, FALSE
, NULL
, NULL
);
3691 } vision_layer_iterate_end
;
3694 case OPID_CITY_BUILDINGS
:
3695 /* TRANS: As in "this building is present". */
3696 add_column(view
, 0, _("Present"), G_TYPE_BOOLEAN
, TRUE
, FALSE
,
3697 G_CALLBACK(extviewer_view_cell_toggled
), ev
);
3698 add_column(view
, 1, _("ID"), G_TYPE_INT
,
3699 FALSE
, FALSE
, NULL
, NULL
);
3700 add_column(view
, 2, _("Name"), G_TYPE_STRING
,
3701 FALSE
, FALSE
, NULL
, NULL
);
3702 /* TRANS: As in "the turn when this building was built". */
3703 add_column(view
, 3, _("Turn Built"), G_TYPE_STRING
,
3704 FALSE
, FALSE
, NULL
, NULL
);
3707 case OPID_STARTPOS_NATIONS
:
3708 /* TRANS: As in "the player has set this nation". */
3709 add_column(view
, 0, _("Set"), G_TYPE_BOOLEAN
, TRUE
, FALSE
,
3710 G_CALLBACK(extviewer_view_cell_toggled
), ev
);
3711 add_column(view
, 1, _("ID"), G_TYPE_INT
,
3712 FALSE
, FALSE
, NULL
, NULL
);
3713 add_column(view
, 2, _("Flag"), GDK_TYPE_PIXBUF
,
3714 FALSE
, FALSE
, NULL
, NULL
);
3715 add_column(view
, 3, _("Name"), G_TYPE_STRING
,
3716 FALSE
, FALSE
, NULL
, NULL
);
3719 case OPID_PLAYER_NATION
:
3720 case OPID_PLAYER_GOV
:
3721 /* TRANS: As in "the player has set this nation". */
3722 add_column(view
, 0, _("Set"), G_TYPE_BOOLEAN
, TRUE
, TRUE
,
3723 G_CALLBACK(extviewer_view_cell_toggled
), ev
);
3724 add_column(view
, 1, _("ID"), G_TYPE_INT
,
3725 FALSE
, FALSE
, NULL
, NULL
);
3727 propid
== OPID_PLAYER_GOV
? _("Icon") : _("Flag"),
3729 FALSE
, FALSE
, NULL
, NULL
);
3730 add_column(view
, 3, _("Name"), G_TYPE_STRING
,
3731 FALSE
, FALSE
, NULL
, NULL
);
3734 case OPID_PLAYER_INVENTIONS
:
3735 /* TRANS: As in "this invention is known". */
3736 add_column(view
, 0, _("Known"), G_TYPE_BOOLEAN
, TRUE
, FALSE
,
3737 G_CALLBACK(extviewer_view_cell_toggled
), ev
);
3738 add_column(view
, 1, _("ID"), G_TYPE_INT
,
3739 FALSE
, FALSE
, NULL
, NULL
);
3740 add_column(view
, 2, _("Name"), G_TYPE_STRING
,
3741 FALSE
, FALSE
, NULL
, NULL
);
3744 case OPID_GAME_SCENARIO_AUTHORS
:
3745 case OPID_GAME_SCENARIO_DESC
:
3746 g_signal_connect(textbuf
, "changed",
3747 G_CALLBACK(extviewer_textbuf_changed
), ev
);
3751 log_error("Unhandled request to configure view widget "
3752 "for property %d (%s) in extviewer_new().",
3753 propid
, objprop_get_name(op
));
3757 gtk_widget_show_all(ev
->panel_widget
);
3758 gtk_widget_show_all(ev
->view_widget
);
3763 /****************************************************************************
3764 Returns the object property that is displayed by this extviewer.
3765 ****************************************************************************/
3766 static struct objprop
*extviewer_get_objprop(struct extviewer
*ev
)
3774 /****************************************************************************
3775 Returns the "panel widget" for this extviewer, i.e. the widget the
3776 is to be placed into the properties panel.
3777 ****************************************************************************/
3778 static GtkWidget
*extviewer_get_panel_widget(struct extviewer
*ev
)
3783 return ev
->panel_widget
;
3786 /****************************************************************************
3787 Returns the "view widget" for this extviewer, i.e. the widget the
3788 is to be used for viewing/editing a complex property.
3789 ****************************************************************************/
3790 static GtkWidget
*extviewer_get_view_widget(struct extviewer
*ev
)
3795 return ev
->view_widget
;
3798 /****************************************************************************
3799 Set the widgets in the extended property viewer to display the given value.
3800 ****************************************************************************/
3801 static void extviewer_refresh_widgets(struct extviewer
*ev
,
3805 enum object_property_ids propid
;
3810 GtkListStore
*store
;
3811 GtkTextBuffer
*textbuf
;
3819 op
= extviewer_get_objprop(ev
);
3820 propid
= objprop_get_id(op
);
3822 if (propval_equal(pv
, ev
->pv_cached
)) {
3825 propval_free(ev
->pv_cached
);
3826 ev
->pv_cached
= propval_copy(pv
);
3828 textbuf
= ev
->textbuf
;
3831 /* NB: Remember to have -1 as the last argument to
3832 * gtk_list_store_set() and to use the correct column
3833 * number when inserting data. :) */
3836 case OPID_TILE_SPECIALS
:
3837 gtk_list_store_clear(store
);
3838 extra_type_by_cause_iterate(EC_SPECIAL
, spe
) {
3839 id
= spe
->data
.special_idx
;
3840 name
= extra_name_translation(spe
);
3841 present
= BV_ISSET(pv
->data
.v_bv_special
, id
);
3842 gtk_list_store_append(store
, &iter
);
3843 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
, 2, name
, -1);
3844 } extra_type_by_cause_iterate_end
;
3845 buf
= propval_as_string(pv
);
3846 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
3850 case OPID_TILE_ROADS
:
3851 gtk_list_store_clear(store
);
3852 extra_type_by_cause_iterate(EC_ROAD
, pextra
) {
3853 struct road_type
*proad
= extra_road_get(pextra
);
3855 id
= road_number(proad
);
3856 name
= extra_name_translation(pextra
);
3857 present
= BV_ISSET(pv
->data
.v_bv_roads
, id
);
3858 gtk_list_store_append(store
, &iter
);
3859 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
, 2, name
, -1);
3860 } extra_type_by_cause_iterate_end
;
3861 buf
= propval_as_string(pv
);
3862 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
3866 case OPID_TILE_BASES
:
3867 gtk_list_store_clear(store
);
3868 extra_type_by_cause_iterate(EC_BASE
, pextra
) {
3869 struct base_type
*pbase
= extra_base_get(pextra
);
3871 id
= base_number(pbase
);
3872 name
= extra_name_translation(pextra
);
3873 present
= BV_ISSET(pv
->data
.v_bv_bases
, id
);
3874 gtk_list_store_append(store
, &iter
);
3875 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
, 2, name
, -1);
3876 } extra_type_by_cause_iterate_end
;
3877 buf
= propval_as_string(pv
);
3878 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
3882 case OPID_TILE_VISION
:
3883 gtk_list_store_clear(store
);
3884 player_slots_iterate(pslot
) {
3885 id
= player_slot_index(pslot
);
3886 if (player_slot_is_used(pslot
)) {
3887 struct player
*pplayer
= player_slot_get_player(pslot
);
3888 name
= player_name(pplayer
);
3889 pixbuf
= get_flag(pplayer
->nation
);
3894 gtk_list_store_append(store
, &iter
);
3895 gtk_list_store_set(store
, &iter
, 0, id
, 2, name
, -1);
3897 gtk_list_store_set(store
, &iter
, 1, pixbuf
, -1);
3898 g_object_unref(pixbuf
);
3901 present
= BV_ISSET(pv
->data
.v_tile_vision
->tile_known
, id
);
3902 gtk_list_store_set(store
, &iter
, 3, present
, -1);
3903 vision_layer_iterate(v
) {
3904 present
= BV_ISSET(pv
->data
.v_tile_vision
->tile_seen
[v
], id
);
3905 gtk_list_store_set(store
, &iter
, 4 + v
, present
, -1);
3906 } vision_layer_iterate_end
;
3907 } player_slots_iterate_end
;
3910 case OPID_STARTPOS_NATIONS
:
3911 gtk_list_store_clear(store
);
3912 gtk_list_store_append(store
, &iter
);
3913 all
= (0 == nation_hash_size(pv
->data
.v_nation_hash
));
3914 gtk_list_store_set(store
, &iter
, 0, all
, 1, -1, 3,
3915 _("All nations"), -1);
3916 nations_iterate(pnation
) {
3917 if (client_nation_is_in_current_set(pnation
)
3918 && is_nation_playable(pnation
)) {
3919 present
= (!all
&& nation_hash_lookup(pv
->data
.v_nation_hash
,
3921 id
= nation_number(pnation
);
3922 pixbuf
= get_flag(pnation
);
3923 name
= nation_adjective_translation(pnation
);
3924 gtk_list_store_append(store
, &iter
);
3925 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
,
3926 2, pixbuf
, 3, name
, -1);
3928 g_object_unref(pixbuf
);
3931 } nations_iterate_end
;
3932 buf
= propval_as_string(pv
);
3933 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
3937 case OPID_CITY_BUILDINGS
:
3938 gtk_list_store_clear(store
);
3939 improvement_iterate(pimprove
) {
3940 if (is_special_improvement(pimprove
)) {
3943 id
= improvement_index(pimprove
);
3944 name
= improvement_name_translation(pimprove
);
3945 turn_built
= pv
->data
.v_built
[id
].turn
;
3946 present
= turn_built
>= 0;
3947 buf
= built_status_to_string(&pv
->data
.v_built
[id
]);
3948 gtk_list_store_append(store
, &iter
);
3949 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
, 2, name
,
3952 } improvement_iterate_end
;
3953 buf
= propval_as_string(pv
);
3954 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
3958 case OPID_PLAYER_NATION
:
3960 enum barbarian_type barbarian_type
=
3961 nation_barbarian_type(pv
->data
.v_nation
);
3963 gtk_list_store_clear(store
);
3964 nations_iterate(pnation
) {
3965 if (client_nation_is_in_current_set(pnation
)
3966 && nation_barbarian_type(pnation
) == barbarian_type
3967 && (barbarian_type
!= NOT_A_BARBARIAN
3968 || is_nation_playable(pnation
))) {
3969 present
= (pnation
== pv
->data
.v_nation
);
3970 id
= nation_index(pnation
);
3971 pixbuf
= get_flag(pnation
);
3972 name
= nation_adjective_translation(pnation
);
3973 gtk_list_store_append(store
, &iter
);
3974 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
,
3975 2, pixbuf
, 3, name
, -1);
3977 g_object_unref(pixbuf
);
3980 } nations_iterate_end
;
3981 gtk_label_set_text(GTK_LABEL(ev
->panel_label
),
3982 nation_adjective_translation(pv
->data
.v_nation
));
3983 pixbuf
= get_flag(pv
->data
.v_nation
);
3984 gtk_image_set_from_pixbuf(GTK_IMAGE(ev
->panel_image
), pixbuf
);
3986 g_object_unref(pixbuf
);
3991 case OPID_PLAYER_GOV
:
3993 gtk_list_store_clear(store
);
3994 governments_iterate(pgov
) {
3995 present
= (pgov
== pv
->data
.v_gov
);
3996 id
= government_index(pgov
);
3997 pixbuf
= sprite_get_pixbuf(get_government_sprite(tileset
, pgov
));
3998 name
= government_name_translation(pgov
);
3999 gtk_list_store_append(store
, &iter
);
4000 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
,
4001 2, pixbuf
, 3, name
, -1);
4003 g_object_unref(pixbuf
);
4005 } governments_iterate_end
;
4006 gtk_label_set_text(GTK_LABEL(ev
->panel_label
),
4007 government_name_translation(pv
->data
.v_gov
));
4008 pixbuf
= sprite_get_pixbuf(get_government_sprite(tileset
, pv
->data
.v_gov
));
4009 gtk_image_set_from_pixbuf(GTK_IMAGE(ev
->panel_image
), pixbuf
);
4011 g_object_unref(pixbuf
);
4016 case OPID_PLAYER_INVENTIONS
:
4017 gtk_list_store_clear(store
);
4018 advance_iterate(A_FIRST
, padvance
) {
4019 id
= advance_index(padvance
);
4020 present
= pv
->data
.v_inventions
[id
];
4021 name
= advance_name_translation(padvance
);
4022 gtk_list_store_append(store
, &iter
);
4023 gtk_list_store_set(store
, &iter
, 0, present
, 1, id
, 2, name
, -1);
4024 } advance_iterate_end
;
4025 buf
= propval_as_string(pv
);
4026 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4030 case OPID_GAME_SCENARIO_AUTHORS
:
4031 case OPID_GAME_SCENARIO_DESC
:
4032 disable_gobject_callback(G_OBJECT(ev
->textbuf
),
4033 G_CALLBACK(extviewer_textbuf_changed
));
4035 GtkTextIter start
, end
;
4038 /* Don't re-set content if unchanged, to avoid moving cursor */
4039 gtk_text_buffer_get_bounds(textbuf
, &start
, &end
);
4040 oldtext
= gtk_text_buffer_get_text(textbuf
, &start
, &end
, TRUE
);
4041 if (strcmp(oldtext
, pv
->data
.v_const_string
) != 0) {
4042 gtk_text_buffer_set_text(textbuf
, pv
->data
.v_const_string
, -1);
4045 enable_gobject_callback(G_OBJECT(ev
->textbuf
),
4046 G_CALLBACK(extviewer_textbuf_changed
));
4047 gtk_widget_set_sensitive(ev
->view_widget
, TRUE
);
4048 buf
= propval_as_string(pv
);
4049 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4054 log_error("Unhandled request to refresh widgets "
4055 "extviewer_refresh_widgets() for objprop id=%d "
4056 "name \"%s\".", propid
, objprop_get_name(op
));
4061 /****************************************************************************
4062 Clear the display widgets.
4063 ****************************************************************************/
4064 static void extviewer_clear_widgets(struct extviewer
*ev
)
4067 enum object_property_ids propid
;
4073 op
= extviewer_get_objprop(ev
);
4074 propid
= objprop_get_id(op
);
4076 propval_free(ev
->pv_cached
);
4077 ev
->pv_cached
= NULL
;
4079 if (ev
->panel_label
!= NULL
) {
4080 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), NULL
);
4084 case OPID_TILE_SPECIALS
:
4085 case OPID_TILE_ROADS
:
4086 case OPID_TILE_BASES
:
4087 case OPID_TILE_VISION
:
4088 case OPID_STARTPOS_NATIONS
:
4089 case OPID_CITY_BUILDINGS
:
4090 case OPID_PLAYER_INVENTIONS
:
4091 gtk_list_store_clear(ev
->store
);
4093 case OPID_PLAYER_NATION
:
4094 case OPID_PLAYER_GOV
:
4095 gtk_list_store_clear(ev
->store
);
4096 gtk_image_set_from_pixbuf(GTK_IMAGE(ev
->panel_image
), NULL
);
4098 case OPID_GAME_SCENARIO_AUTHORS
:
4099 case OPID_GAME_SCENARIO_DESC
:
4100 disable_gobject_callback(G_OBJECT(ev
->textbuf
),
4101 G_CALLBACK(extviewer_textbuf_changed
));
4102 gtk_text_buffer_set_text(ev
->textbuf
, "", -1);
4103 enable_gobject_callback(G_OBJECT(ev
->textbuf
),
4104 G_CALLBACK(extviewer_textbuf_changed
));
4105 gtk_widget_set_sensitive(ev
->view_widget
, FALSE
);
4108 log_error("Unhandled request to clear widgets "
4109 "in extviewer_clear_widgets() for objprop id=%d "
4110 "name \"%s\".", propid
, objprop_get_name(op
));
4115 /****************************************************************************
4116 Handle the extviewer's panel button click. This should set the property
4117 page to display the view widget for this complex property.
4118 ****************************************************************************/
4119 static void extviewer_panel_button_clicked(GtkButton
*button
,
4122 struct extviewer
*ev
;
4123 struct property_page
*pp
;
4131 op
= extviewer_get_objprop(ev
);
4132 pp
= objprop_get_property_page(op
);
4133 property_page_show_extviewer(pp
, ev
);
4136 /****************************************************************************
4137 Handle toggling of a boolean cell value in the extviewer's tree view.
4138 ****************************************************************************/
4139 static void extviewer_view_cell_toggled(GtkCellRendererToggle
*cell
,
4143 struct extviewer
*ev
;
4145 struct property_page
*pp
;
4146 enum object_property_ids propid
;
4147 GtkTreeModel
*model
;
4149 int id
, old_id
, turn_built
;
4151 bool active
, present
;
4153 GdkPixbuf
*pixbuf
= NULL
;
4165 op
= extviewer_get_objprop(ev
);
4166 propid
= objprop_get_id(op
);
4167 active
= gtk_cell_renderer_toggle_get_active(cell
);
4168 pp
= objprop_get_property_page(op
);
4170 model
= GTK_TREE_MODEL(ev
->store
);
4171 if (!gtk_tree_model_get_iter_from_string(model
, &iter
, path
)) {
4179 case OPID_TILE_SPECIALS
:
4180 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4181 if (id
< 0 || id
>= extra_type_list_size(extra_type_list_by_cause(EC_SPECIAL
))) {
4185 BV_SET(pv
->data
.v_bv_special
, id
);
4187 BV_CLR(pv
->data
.v_bv_special
, id
);
4189 gtk_list_store_set(ev
->store
, &iter
, 0, present
, -1);
4190 buf
= propval_as_string(pv
);
4191 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4195 case OPID_TILE_ROADS
:
4196 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4197 if (!(0 <= id
&& id
< road_count())) {
4201 BV_SET(pv
->data
.v_bv_roads
, id
);
4203 BV_CLR(pv
->data
.v_bv_roads
, id
);
4205 gtk_list_store_set(ev
->store
, &iter
, 0, present
, -1);
4206 buf
= propval_as_string(pv
);
4207 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4211 case OPID_TILE_BASES
:
4212 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4213 if (!(0 <= id
&& id
< base_count())) {
4217 BV_SET(pv
->data
.v_bv_bases
, id
);
4219 BV_CLR(pv
->data
.v_bv_bases
, id
);
4221 gtk_list_store_set(ev
->store
, &iter
, 0, present
, -1);
4222 buf
= propval_as_string(pv
);
4223 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4227 case OPID_STARTPOS_NATIONS
:
4228 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4229 if (-1 > id
&& id
>= nation_count()) {
4234 gtk_list_store_set(ev
->store
, &iter
, 0, present
, -1);
4235 gtk_tree_model_get_iter_first(model
, &iter
);
4237 while (gtk_tree_model_iter_next(model
, &iter
)) {
4238 gtk_list_store_set(ev
->store
, &iter
, 0, FALSE
, -1);
4240 nation_hash_clear(pv
->data
.v_nation_hash
);
4242 const struct nation_type
*pnation
;
4245 gtk_tree_model_iter_next(model
, &iter
);
4246 gtk_tree_model_get(model
, &iter
, 0, &id2
, -1);
4247 gtk_list_store_set(ev
->store
, &iter
, 0, TRUE
, -1);
4248 pnation
= nation_by_number(id2
);
4249 nation_hash_insert(pv
->data
.v_nation_hash
, pnation
, NULL
);
4252 const struct nation_type
*pnation
;
4255 gtk_list_store_set(ev
->store
, &iter
, 0, present
, -1);
4256 pnation
= nation_by_number(id
);
4258 nation_hash_insert(pv
->data
.v_nation_hash
, pnation
, NULL
);
4260 nation_hash_remove(pv
->data
.v_nation_hash
, pnation
);
4262 gtk_tree_model_get_iter_first(model
, &iter
);
4263 all
= (0 == nation_hash_size(pv
->data
.v_nation_hash
));
4264 gtk_list_store_set(ev
->store
, &iter
, 0, all
, -1);
4266 buf
= propval_as_string(pv
);
4267 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4271 case OPID_CITY_BUILDINGS
:
4272 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4273 if (!(0 <= id
&& id
< B_LAST
)) {
4276 turn_built
= present
? game
.info
.turn
: I_NEVER
;
4277 pv
->data
.v_built
[id
].turn
= turn_built
;
4278 buf
= built_status_to_string(&pv
->data
.v_built
[id
]);
4279 gtk_list_store_set(ev
->store
, &iter
, 0, present
, 3, buf
, -1);
4281 buf
= propval_as_string(pv
);
4282 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4286 case OPID_PLAYER_NATION
:
4287 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4288 if (!(0 <= id
&& id
< nation_count()) || !present
) {
4291 old_id
= nation_index(pv
->data
.v_nation
);
4292 pv
->data
.v_nation
= nation_by_number(id
);
4293 gtk_list_store_set(ev
->store
, &iter
, 0, TRUE
, -1);
4294 gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, old_id
);
4295 gtk_list_store_set(ev
->store
, &iter
, 0, FALSE
, -1);
4296 gtk_label_set_text(GTK_LABEL(ev
->panel_label
),
4297 nation_adjective_translation(pv
->data
.v_nation
));
4298 pixbuf
= get_flag(pv
->data
.v_nation
);
4299 gtk_image_set_from_pixbuf(GTK_IMAGE(ev
->panel_image
), pixbuf
);
4301 g_object_unref(pixbuf
);
4305 case OPID_PLAYER_GOV
:
4306 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4307 if (!(0 <= id
&& id
< government_count()) || !present
) {
4310 old_id
= government_index(pv
->data
.v_gov
);
4311 pv
->data
.v_gov
= government_by_number(id
);
4312 gtk_list_store_set(ev
->store
, &iter
, 0, TRUE
, -1);
4313 gtk_tree_model_iter_nth_child(model
, &iter
, NULL
, old_id
);
4314 gtk_list_store_set(ev
->store
, &iter
, 0, FALSE
, -1);
4315 gtk_label_set_text(GTK_LABEL(ev
->panel_label
),
4316 government_name_translation(pv
->data
.v_gov
));
4317 pixbuf
= sprite_get_pixbuf(get_government_sprite(tileset
, pv
->data
.v_gov
));
4318 gtk_image_set_from_pixbuf(GTK_IMAGE(ev
->panel_image
), pixbuf
);
4320 g_object_unref(pixbuf
);
4324 case OPID_PLAYER_INVENTIONS
:
4325 gtk_tree_model_get(model
, &iter
, 1, &id
, -1);
4326 if (!(A_FIRST
<= id
&& id
< advance_count())) {
4329 pv
->data
.v_inventions
[id
] = present
;
4330 gtk_list_store_set(ev
->store
, &iter
, 0, present
, -1);
4331 buf
= propval_as_string(pv
);
4332 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4337 log_error("Unhandled widget toggled signal in "
4338 "extviewer_view_cell_toggled() for objprop id=%d "
4339 "name \"%s\".", propid
, objprop_get_name(op
));
4344 property_page_change_value(pp
, op
, pv
);
4347 /****************************************************************************
4348 Handle a change in the extviewer's text buffer.
4349 ****************************************************************************/
4350 static void extviewer_textbuf_changed(GtkTextBuffer
*textbuf
,
4353 struct extviewer
*ev
;
4355 struct property_page
*pp
;
4356 enum object_property_ids propid
;
4357 struct propval value
= {{0,}, VALTYPE_STRING
, FALSE
}, *pv
;
4358 GtkTextIter start
, end
;
4367 op
= extviewer_get_objprop(ev
);
4368 propid
= objprop_get_id(op
);
4369 pp
= objprop_get_property_page(op
);
4371 gtk_text_buffer_get_start_iter(textbuf
, &start
);
4372 gtk_text_buffer_get_end_iter(textbuf
, &end
);
4373 text
= gtk_text_buffer_get_text(textbuf
, &start
, &end
, FALSE
);
4374 value
.data
.v_const_string
= text
;
4378 case OPID_GAME_SCENARIO_AUTHORS
:
4379 case OPID_GAME_SCENARIO_DESC
:
4380 buf
= propval_as_string(pv
);
4381 gtk_label_set_text(GTK_LABEL(ev
->panel_label
), buf
);
4385 log_error("Unhandled widget modified signal in "
4386 "extviewer_textbuf_changed() for objprop id=%d "
4387 "name \"%s\".", propid
, objprop_get_name(op
));
4392 property_page_change_value(pp
, op
, pv
);
4396 /****************************************************************************
4397 Install all object properties that this page type can view/edit.
4398 ****************************************************************************/
4399 static void property_page_setup_objprops(struct property_page
*pp
)
4401 #define ADDPROP(ARG_id, ARG_name, ARG_tooltip, ARG_flags, ARG_valtype) do { \
4402 struct objprop *MY_op = objprop_new(ARG_id, ARG_name, ARG_tooltip, \
4403 ARG_flags, ARG_valtype, pp); \
4404 objprop_hash_insert(pp->objprop_table, MY_op->id, MY_op); \
4407 switch (property_page_get_objtype(pp
)) {
4409 ADDPROP(OPID_TILE_IMAGE
, _("Image"), NULL
,
4410 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_PIXBUF
);
4411 ADDPROP(OPID_TILE_TERRAIN
, _("Terrain"), NULL
,
4412 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_STRING
);
4413 ADDPROP(OPID_TILE_RESOURCE
, _("Resource"), NULL
,
4414 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_STRING
);
4415 ADDPROP(OPID_TILE_INDEX
, _("Index"), NULL
,
4416 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4417 ADDPROP(OPID_TILE_X
, Q_("?coordinate:X"), NULL
,
4418 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4419 ADDPROP(OPID_TILE_Y
, Q_("?coordinate:Y"), NULL
,
4420 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4421 /* TRANS: The coordinate X in native coordinates.
4422 * The freeciv coordinate system is described in doc/HACKING. */
4423 ADDPROP(OPID_TILE_NAT_X
, _("NAT X"), NULL
,
4424 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4425 /* TRANS: The coordinate Y in native coordinates.
4426 * The freeciv coordinate system is described in doc/HACKING. */
4427 ADDPROP(OPID_TILE_NAT_Y
, _("NAT Y"), NULL
,
4428 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4429 ADDPROP(OPID_TILE_CONTINENT
, _("Continent"), NULL
,
4430 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4431 ADDPROP(OPID_TILE_XY
, Q_("?coordinates:X,Y"), NULL
,
4432 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_STRING
);
4433 ADDPROP(OPID_TILE_SPECIALS
, _("Specials"), NULL
,
4434 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4435 VALTYPE_BV_SPECIAL
);
4436 ADDPROP(OPID_TILE_ROADS
, _("Roads"), NULL
,
4437 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4439 ADDPROP(OPID_TILE_BASES
, _("Bases"), NULL
,
4440 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4442 #ifdef FREECIV_DEBUG
4443 ADDPROP(OPID_TILE_ADDRESS
, _("Address"), NULL
,
4444 OPF_HAS_WIDGET
, VALTYPE_STRING
);
4445 #endif /* FREECIV_DEBUG */
4447 /* Disabled entirely for now as server is not sending other
4448 * players' vision information anyway. */
4449 ADDPROP(OPID_TILE_VISION
, _("Vision"), NULL
,
4450 OPF_HAS_WIDGET
, VALTYPE_TILE_VISION_DATA
);
4452 /* TRANS: Tile property "Label" label in editor */
4453 ADDPROP(OPID_TILE_LABEL
, Q_("?property:Label"), NULL
,
4454 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_STRING
);
4457 case OBJTYPE_STARTPOS
:
4458 ADDPROP(OPID_STARTPOS_IMAGE
, _("Image"), NULL
,
4459 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_PIXBUF
);
4460 ADDPROP(OPID_STARTPOS_XY
, Q_("?coordinates:X,Y"), NULL
,
4461 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_STRING
);
4462 ADDPROP(OPID_STARTPOS_EXCLUDE
, _("Exclude Nations"), NULL
,
4463 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4464 ADDPROP(OPID_STARTPOS_NATIONS
, _("Nations"), NULL
,
4465 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4466 VALTYPE_NATION_HASH
);
4470 ADDPROP(OPID_UNIT_IMAGE
, _("Image"), NULL
,
4471 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_PIXBUF
);
4472 #ifdef FREECIV_DEBUG
4473 ADDPROP(OPID_UNIT_ADDRESS
, _("Address"), NULL
,
4474 OPF_HAS_WIDGET
, VALTYPE_STRING
);
4475 #endif /* FREECIV_DEBUG */
4476 ADDPROP(OPID_UNIT_TYPE
, _("Type"), NULL
,
4477 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_STRING
);
4478 ADDPROP(OPID_UNIT_ID
, _("ID"), NULL
,
4479 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4480 ADDPROP(OPID_UNIT_XY
, Q_("?coordinates:X,Y"), NULL
,
4481 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_STRING
);
4482 ADDPROP(OPID_UNIT_MOVES_LEFT
, _("Moves Left"), NULL
,
4483 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4484 ADDPROP(OPID_UNIT_FUEL
, _("Fuel"), NULL
,
4485 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4486 ADDPROP(OPID_UNIT_MOVED
, _("Moved"), NULL
,
4487 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4488 ADDPROP(OPID_UNIT_DONE_MOVING
, _("Done Moving"), NULL
,
4489 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4490 /* TRANS: HP = Hit Points of a unit. */
4491 ADDPROP(OPID_UNIT_HP
, _("HP"), NULL
,
4492 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4493 ADDPROP(OPID_UNIT_VETERAN
, _("Veteran"), NULL
,
4494 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4498 ADDPROP(OPID_CITY_IMAGE
, _("Image"), NULL
,
4499 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_PIXBUF
);
4500 ADDPROP(OPID_CITY_NAME
, _("Name"), NULL
,
4501 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_STRING
);
4502 #ifdef FREECIV_DEBUG
4503 ADDPROP(OPID_CITY_ADDRESS
, _("Address"), NULL
,
4504 OPF_HAS_WIDGET
, VALTYPE_STRING
);
4505 #endif /* FREECIV_DEBUG */
4506 ADDPROP(OPID_CITY_ID
, _("ID"), NULL
,
4507 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_INT
);
4508 ADDPROP(OPID_CITY_XY
, Q_("?coordinates:X,Y"), NULL
,
4509 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
, VALTYPE_STRING
);
4510 ADDPROP(OPID_CITY_SIZE
, _("Size"), NULL
,
4511 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4512 ADDPROP(OPID_CITY_HISTORY
, _("History"), NULL
,
4513 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4514 ADDPROP(OPID_CITY_BUILDINGS
, _("Buildings"), NULL
,
4515 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4516 VALTYPE_BUILT_ARRAY
);
4517 ADDPROP(OPID_CITY_FOOD_STOCK
, _("Food Stock"), NULL
,
4518 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4519 ADDPROP(OPID_CITY_SHIELD_STOCK
, _("Shield Stock"), NULL
,
4520 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4523 case OBJTYPE_PLAYER
:
4524 ADDPROP(OPID_PLAYER_NAME
, _("Name"), NULL
,
4525 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4527 #ifdef FREECIV_DEBUG
4528 ADDPROP(OPID_PLAYER_ADDRESS
, _("Address"), NULL
,
4529 OPF_HAS_WIDGET
, VALTYPE_STRING
);
4530 #endif /* FREECIV_DEBUG */
4531 ADDPROP(OPID_PLAYER_NATION
, _("Nation"), NULL
,
4532 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4534 ADDPROP(OPID_PLAYER_GOV
, _("Government"), NULL
,
4535 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_GOV
);
4536 ADDPROP(OPID_PLAYER_AGE
, _("Age"), NULL
,
4537 OPF_HAS_WIDGET
, VALTYPE_INT
);
4538 ADDPROP(OPID_PLAYER_INVENTIONS
, _("Inventions"), NULL
,
4539 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4540 VALTYPE_INVENTIONS_ARRAY
);
4541 ADDPROP(OPID_PLAYER_SCENARIO_RESERVED
, _("Reserved"), NULL
,
4542 OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4543 ADDPROP(OPID_PLAYER_SCIENCE
, _("Science"), NULL
,
4544 OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_INT
);
4545 ADDPROP(OPID_PLAYER_GOLD
, _("Gold"), NULL
,
4546 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4551 ADDPROP(OPID_GAME_YEAR
, _("Year"), NULL
,
4552 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4554 ADDPROP(OPID_GAME_SCENARIO
, _("Scenario"), NULL
,
4555 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4557 ADDPROP(OPID_GAME_SCENARIO_NAME
, _("Scenario Name"), NULL
,
4558 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4560 ADDPROP(OPID_GAME_SCENARIO_AUTHORS
,
4561 _("Scenario Authors"), NULL
,
4562 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4564 ADDPROP(OPID_GAME_SCENARIO_DESC
,
4565 _("Scenario Description"), NULL
,
4566 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
,
4568 ADDPROP(OPID_GAME_SCENARIO_RANDSTATE
,
4569 _("Save Random Number State"), NULL
,
4570 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4571 ADDPROP(OPID_GAME_SCENARIO_PLAYERS
,
4572 _("Save Players"), NULL
,
4573 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4574 ADDPROP(OPID_GAME_STARTPOS_NATIONS
,
4575 _("Nation Start Positions"), NULL
,
4576 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4577 ADDPROP(OPID_GAME_PREVENT_CITIES
,
4578 _("Prevent New Cities"), NULL
,
4579 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4580 ADDPROP(OPID_GAME_LAKE_FLOODING
,
4581 _("Saltwater Flooding Lakes"), NULL
,
4582 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4583 ADDPROP(OPID_GAME_RULESET_LOCKED
,
4584 _("Lock to current Ruleset"), NULL
,
4585 OPF_IN_LISTVIEW
| OPF_HAS_WIDGET
| OPF_EDITABLE
, VALTYPE_BOOL
);
4592 log_error("%s(): Unhandled page object type %s (nb %d).", __FUNCTION__
,
4593 objtype_get_name(property_page_get_objtype(pp
)),
4594 property_page_get_objtype(pp
));
4598 /****************************************************************************
4599 Callback for when a property page's listview's selection changes.
4600 ****************************************************************************/
4601 static void property_page_selection_changed(GtkTreeSelection
*sel
,
4604 struct property_page
*pp
;
4605 struct objbind
*ob
= NULL
;
4612 if (gtk_tree_selection_count_selected_rows(sel
) < 1) {
4613 property_page_set_focused_objbind(pp
, NULL
);
4616 ob
= property_page_get_focused_objbind(pp
);
4617 property_page_objprop_iterate(pp
, op
) {
4618 objprop_refresh_widget(op
, ob
);
4619 } property_page_objprop_iterate_end
;
4622 /****************************************************************************
4623 Monitor which rows are to be selected, so we know which objbind to display
4624 in the properties panel.
4625 ****************************************************************************/
4626 static gboolean
property_page_selection_func(GtkTreeSelection
*sel
,
4627 GtkTreeModel
*model
,
4628 GtkTreePath
*sel_path
,
4629 gboolean currently_selected
,
4632 struct property_page
*pp
;
4633 struct objbind
*ob
= NULL
, *old_ob
;
4637 if (!pp
|| !sel_path
) {
4641 if (!gtk_tree_model_get_iter(model
, &iter
, sel_path
)) {
4645 old_ob
= property_page_get_focused_objbind(pp
);
4646 gtk_tree_model_get(model
, &iter
, 0, &ob
, -1);
4647 if (currently_selected
) {
4651 struct objbind
*new_ob
= NULL
;
4653 rows
= gtk_tree_selection_get_selected_rows(sel
, NULL
);
4654 for (p
= rows
; p
!= NULL
; p
= p
->next
) {
4656 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
4657 struct objbind
*test_ob
= NULL
;
4658 gtk_tree_model_get(model
, &iter
, 0, &test_ob
, -1);
4659 if (test_ob
== ob
) {
4666 g_list_foreach(rows
, (GFunc
) gtk_tree_path_free
, NULL
);
4669 property_page_set_focused_objbind(pp
, new_ob
);
4672 property_page_set_focused_objbind(pp
, ob
);
4678 /****************************************************************************
4679 Callback to handle text changing in the quick find entry widget.
4680 ****************************************************************************/
4681 static void property_page_quick_find_entry_changed(GtkWidget
*entry
,
4684 struct property_page
*pp
;
4687 GtkTreeViewColumn
*col
;
4688 struct property_filter
*pf
;
4692 text
= gtk_entry_get_text(GTK_ENTRY(entry
));
4693 pf
= property_filter_new(text
);
4695 property_page_objprop_iterate(pp
, op
) {
4696 if (!objprop_has_widget(op
)
4697 && !objprop_show_in_listview(op
)) {
4700 matched
= property_filter_match(pf
, op
);
4701 w
= objprop_get_widget(op
);
4702 if (objprop_has_widget(op
) && w
!= NULL
) {
4709 col
= objprop_get_treeview_column(op
);
4710 if (objprop_show_in_listview(op
) && col
!= NULL
) {
4711 gtk_tree_view_column_set_visible(col
, matched
);
4713 } property_page_objprop_iterate_end
;
4715 property_filter_free(pf
);
4718 /****************************************************************************
4719 Create and return a property page of the given object type.
4720 Returns NULL if the page could not be created.
4721 ****************************************************************************/
4722 static struct property_page
*
4723 property_page_new(enum editor_object_type objtype
,
4724 struct property_editor
*pe
)
4726 struct property_page
*pp
;
4727 GtkWidget
*vbox
, *vbox2
, *hbox
, *hbox2
, *paned
, *frame
, *w
;
4728 GtkWidget
*scrollwin
, *view
, *label
, *entry
, *notebook
;
4729 GtkWidget
*button
, *hsep
, *image
;
4730 GtkTreeSelection
*sel
;
4731 GtkCellRenderer
*cell
;
4732 GtkTreeViewColumn
*col
;
4733 GtkSizeGroup
*sizegroup
;
4734 int num_columns
= 0;
4737 const char *attr_type_str
, *name
, *tooltip
;
4740 if (!(0 <= objtype
&& objtype
< NUM_OBJTYPES
)) {
4744 pp
= fc_calloc(1, sizeof(struct property_page
));
4745 pp
->objtype
= objtype
;
4748 sizegroup
= gtk_size_group_new(GTK_SIZE_GROUP_BOTH
);
4750 pp
->objprop_table
= objprop_hash_new();
4751 property_page_setup_objprops(pp
);
4753 pp
->objbind_table
= objbind_hash_new();
4755 pp
->tag_table
= stored_tag_hash_new();
4757 property_page_objprop_iterate(pp
, op
) {
4758 if (objprop_show_in_listview(op
)) {
4761 } property_page_objprop_iterate_end
;
4763 /* Column zero in the store holds an objbind
4764 * pointer and is never displayed. */
4766 gtype_array
= fc_malloc(num_columns
* sizeof(GType
));
4767 gtype_array
[0] = G_TYPE_POINTER
;
4769 property_page_objprop_iterate(pp
, op
) {
4770 if (objprop_show_in_listview(op
)) {
4771 gtype_array
[col_id
] = objprop_get_gtype(op
);
4772 objprop_set_column_id(op
, col_id
);
4775 } property_page_objprop_iterate_end
;
4777 pp
->object_store
= gtk_list_store_newv(num_columns
, gtype_array
);
4780 paned
= gtk_paned_new(GTK_ORIENTATION_HORIZONTAL
);
4781 gtk_paned_set_position(GTK_PANED(paned
), 256);
4784 /* Left side object list view. */
4786 vbox
= gtk_grid_new();
4787 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox
),
4788 GTK_ORIENTATION_VERTICAL
);
4789 gtk_grid_set_row_spacing(GTK_GRID(vbox
), 4);
4790 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
4791 gtk_paned_pack1(GTK_PANED(paned
), vbox
, TRUE
, TRUE
);
4793 scrollwin
= gtk_scrolled_window_new(NULL
, NULL
);
4794 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin
),
4795 GTK_SHADOW_ETCHED_IN
);
4796 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin
),
4797 GTK_POLICY_AUTOMATIC
,
4798 GTK_POLICY_AUTOMATIC
);
4799 gtk_container_add(GTK_CONTAINER(vbox
), scrollwin
);
4801 view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(pp
->object_store
));
4802 gtk_widget_set_hexpand(view
, TRUE
);
4803 gtk_widget_set_vexpand(view
, TRUE
);
4804 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view
), TRUE
);
4806 property_page_objprop_iterate(pp
, op
) {
4807 if (!objprop_show_in_listview(op
)) {
4811 attr_type_str
= objprop_get_attribute_type_string(op
);
4812 if (!attr_type_str
) {
4815 col_id
= objprop_get_column_id(op
);
4819 name
= objprop_get_name(op
);
4823 cell
= objprop_create_cell_renderer(op
);
4828 col
= gtk_tree_view_column_new_with_attributes(name
, cell
,
4829 attr_type_str
, col_id
,
4832 gtk_tree_view_column_set_sizing(col
, GTK_TREE_VIEW_COLUMN_GROW_ONLY
);
4833 gtk_tree_view_column_set_resizable(col
, TRUE
);
4834 gtk_tree_view_column_set_reorderable(col
, TRUE
);
4835 if (objprop_is_sortable(op
)) {
4836 gtk_tree_view_column_set_clickable(col
, TRUE
);
4837 gtk_tree_view_column_set_sort_column_id(col
, col_id
);
4839 gtk_tree_view_column_set_clickable(col
, FALSE
);
4841 gtk_tree_view_append_column(GTK_TREE_VIEW(view
), col
);
4842 objprop_set_treeview_column(op
, col
);
4844 } property_page_objprop_iterate_end
;
4846 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
4847 gtk_tree_selection_set_mode(sel
, GTK_SELECTION_MULTIPLE
);
4848 g_signal_connect(sel
, "changed",
4849 G_CALLBACK(property_page_selection_changed
), pp
);
4850 gtk_tree_selection_set_select_function(sel
,
4851 property_page_selection_func
, pp
, NULL
);
4853 gtk_container_add(GTK_CONTAINER(scrollwin
), view
);
4854 pp
->object_view
= view
;
4856 if (!objtype_is_conserved(objtype
)) {
4857 hbox
= gtk_grid_new();
4858 gtk_grid_set_column_spacing(GTK_GRID(hbox
), 4);
4859 gtk_container_add(GTK_CONTAINER(vbox
), hbox
);
4861 button
= gtk_button_new();
4862 image
= gtk_image_new_from_stock(GTK_STOCK_ADD
, GTK_ICON_SIZE_BUTTON
);
4863 gtk_button_set_image(GTK_BUTTON(button
), image
);
4864 gtk_button_set_label(GTK_BUTTON(button
), _("Create"));
4865 gtk_size_group_add_widget(sizegroup
, button
);
4866 gtk_widget_set_tooltip_text(button
,
4867 _("Pressing this button will create a new object of the "
4868 "same type as the current property page and add it to "
4869 "the page. The specific type and count of the objects "
4870 "is taken from the editor tool state. So for example, "
4871 "the \"tool value\" of the unit tool and its \"count\" "
4872 "parameter affect unit creation."));
4873 g_signal_connect(button
, "clicked",
4874 G_CALLBACK(property_page_create_button_clicked
), pp
);
4875 gtk_container_add(GTK_CONTAINER(hbox
), button
);
4877 button
= gtk_button_new();
4878 image
= gtk_image_new_from_stock(GTK_STOCK_REMOVE
,
4879 GTK_ICON_SIZE_BUTTON
);
4880 gtk_button_set_image(GTK_BUTTON(button
), image
);
4881 gtk_button_set_label(GTK_BUTTON(button
), _("Destroy"));
4882 gtk_size_group_add_widget(sizegroup
, button
);
4883 gtk_widget_set_tooltip_text(button
,
4884 _("Pressing this button will send a request to the server "
4885 "to destroy (i.e. erase) the objects selected in the object "
4887 g_signal_connect(button
, "clicked",
4888 G_CALLBACK(property_page_destroy_button_clicked
), pp
);
4889 gtk_container_add(GTK_CONTAINER(hbox
), button
);
4892 /* Right side properties panel. */
4894 hbox
= gtk_grid_new();
4895 gtk_grid_set_column_spacing(GTK_GRID(hbox
), 4);
4896 gtk_paned_pack2(GTK_PANED(paned
), hbox
, TRUE
, TRUE
);
4898 vbox
= gtk_grid_new();
4899 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox
),
4900 GTK_ORIENTATION_VERTICAL
);
4901 gtk_grid_set_row_spacing(GTK_GRID(vbox
), 4);
4902 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 4);
4903 gtk_container_add(GTK_CONTAINER(hbox
), vbox
);
4905 /* Extended property viewer to the right of the properties panel.
4906 * This needs to be created before property widgets, since some
4907 * might try to append themselves to this notebook. */
4909 vbox2
= gtk_grid_new();
4910 gtk_widget_set_hexpand(vbox2
, TRUE
);
4911 gtk_widget_set_vexpand(vbox2
, TRUE
);
4912 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2
),
4913 GTK_ORIENTATION_VERTICAL
);
4914 gtk_grid_set_row_spacing(GTK_GRID(vbox2
), 4);
4915 gtk_container_add(GTK_CONTAINER(hbox
), vbox2
);
4917 notebook
= gtk_notebook_new();
4918 gtk_widget_set_vexpand(notebook
, TRUE
);
4919 gtk_widget_set_size_request(notebook
, 256, -1);
4920 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook
), FALSE
);
4921 gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook
), FALSE
);
4922 gtk_container_add(GTK_CONTAINER(vbox2
), notebook
);
4923 pp
->extviewer_notebook
= notebook
;
4925 hsep
= gtk_separator_new(GTK_ORIENTATION_HORIZONTAL
);
4926 gtk_container_add(GTK_CONTAINER(vbox2
), hsep
);
4928 hbox2
= gtk_grid_new();
4929 gtk_container_set_border_width(GTK_CONTAINER(hbox2
), 4);
4930 gtk_container_add(GTK_CONTAINER(vbox2
), hbox2
);
4932 button
= gtk_button_new_from_stock(GTK_STOCK_CLOSE
);
4933 gtk_size_group_add_widget(sizegroup
, button
);
4934 g_signal_connect_swapped(button
, "clicked",
4935 G_CALLBACK(gtk_widget_hide_on_delete
), pe
->widget
);
4936 gtk_container_add(GTK_CONTAINER(hbox2
), button
);
4938 /* Now create the properties panel. */
4940 /* TRANS: %s is a type of object that can be edited, such as "Tile",
4941 * "Unit", "Start Position", etc. */
4942 title
= g_strdup_printf(_("%s Properties"),
4943 objtype_get_name(objtype
));
4944 frame
= gtk_frame_new(title
);
4946 gtk_widget_set_size_request(frame
, 256, -1);
4947 gtk_container_add(GTK_CONTAINER(vbox
), frame
);
4949 scrollwin
= gtk_scrolled_window_new(NULL
, NULL
);
4950 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin
),
4952 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin
),
4953 GTK_POLICY_AUTOMATIC
,
4954 GTK_POLICY_AUTOMATIC
);
4955 gtk_container_add(GTK_CONTAINER(frame
), scrollwin
);
4957 vbox2
= gtk_grid_new();
4958 gtk_widget_set_vexpand(vbox2
, TRUE
);
4959 gtk_orientable_set_orientation(GTK_ORIENTABLE(vbox2
),
4960 GTK_ORIENTATION_VERTICAL
);
4961 gtk_grid_set_row_spacing(GTK_GRID(vbox2
), 4);
4962 gtk_container_set_border_width(GTK_CONTAINER(vbox2
), 4);
4963 gtk_container_add(GTK_CONTAINER(scrollwin
), vbox2
);
4965 property_page_objprop_iterate(pp
, op
) {
4966 if (!objprop_has_widget(op
)) {
4969 w
= objprop_get_widget(op
);
4973 gtk_container_add(GTK_CONTAINER(vbox2
), w
);
4974 tooltip
= objprop_get_tooltip(op
);
4975 if (NULL
!= tooltip
) {
4976 gtk_widget_set_tooltip_text(w
, tooltip
);
4978 } property_page_objprop_iterate_end
;
4980 hbox2
= gtk_grid_new();
4981 gtk_widget_set_margin_top(hbox2
, 4);
4982 gtk_widget_set_margin_bottom(hbox2
, 4);
4983 gtk_grid_set_column_spacing(GTK_GRID(hbox2
), 4);
4984 gtk_container_add(GTK_CONTAINER(vbox
), hbox2
);
4986 label
= gtk_label_new(_("Filter:"));
4987 gtk_container_add(GTK_CONTAINER(hbox2
), label
);
4989 entry
= gtk_entry_new();
4990 gtk_widget_set_tooltip_text(entry
,
4991 _("Enter a filter string to limit which properties are shown. "
4992 "The filter is one or more text patterns separated by | "
4993 "(\"or\") or & (\"and\"). The symbol & has higher precedence "
4994 "than |. A pattern may also be negated by prefixing it with !."));
4995 g_signal_connect(entry
, "changed",
4996 G_CALLBACK(property_page_quick_find_entry_changed
), pp
);
4997 gtk_container_add(GTK_CONTAINER(hbox2
), entry
);
4999 hbox2
= gtk_grid_new();
5000 gtk_grid_set_column_spacing(GTK_GRID(hbox2
), 4);
5001 gtk_container_add(GTK_CONTAINER(vbox
), hbox2
);
5003 button
= gtk_button_new_from_stock(GTK_STOCK_REFRESH
);
5004 gtk_size_group_add_widget(sizegroup
, button
);
5005 gtk_widget_set_tooltip_text(button
,
5006 _("Pressing this button will reset all modified properties of "
5007 "the selected objects to their current values (the values "
5008 "they have on the server)."));
5009 g_signal_connect(button
, "clicked",
5010 G_CALLBACK(property_page_refresh_button_clicked
), pp
);
5011 gtk_container_add(GTK_CONTAINER(hbox2
), button
);
5013 button
= gtk_button_new_from_stock(GTK_STOCK_APPLY
);
5014 gtk_size_group_add_widget(sizegroup
, button
);
5015 gtk_widget_set_tooltip_text(button
,
5016 _("Pressing this button will send all modified properties of "
5017 "the objects selected in the object list to the server. "
5018 "Modified properties' names are shown in red in the properties "
5020 g_signal_connect(button
, "clicked",
5021 G_CALLBACK(property_page_apply_button_clicked
), pp
);
5022 gtk_container_add(GTK_CONTAINER(hbox2
), button
);
5027 /****************************************************************************
5028 Returns the translated name of the property page's object type.
5029 ****************************************************************************/
5030 static const char *property_page_get_name(const struct property_page
*pp
)
5035 return objtype_get_name(property_page_get_objtype(pp
));
5038 /****************************************************************************
5039 Returns the object type for this property page, or -1 if none.
5040 ****************************************************************************/
5041 static enum editor_object_type
5042 property_page_get_objtype(const struct property_page
*pp
)
5050 /****************************************************************************
5051 Create a pixbuf containing an image of the given tile. The image will
5052 only be of the layers containing terrains, resources and specials.
5054 May return NULL on error or bad input.
5055 NB: You must call g_object_unref on the non-NULL return value when you
5057 ****************************************************************************/
5058 static GdkPixbuf
*create_tile_pixbuf(const struct tile
*ptile
)
5060 return create_pixbuf_from_layers(ptile
, NULL
, NULL
, LAYER_CATEGORY_TILE
);
5063 /****************************************************************************
5064 Create a pixbuf containing an image of the given unit.
5066 May return NULL on error or bad input.
5067 NB: You must call g_object_unref on the non-NULL return value when you
5069 ****************************************************************************/
5070 static GdkPixbuf
*create_unit_pixbuf(const struct unit
*punit
)
5072 return create_pixbuf_from_layers(NULL
, punit
, NULL
, LAYER_CATEGORY_UNIT
);
5075 /****************************************************************************
5076 Create a pixbuf containing an image of the given city.
5078 May return NULL on error or bad input.
5079 NB: You must call g_object_unref on the non-NULL return value when you
5081 ****************************************************************************/
5082 static GdkPixbuf
*create_city_pixbuf(const struct city
*pcity
)
5084 return create_pixbuf_from_layers(city_tile(pcity
), NULL
, pcity
,
5085 LAYER_CATEGORY_CITY
);
5088 /****************************************************************************
5089 Create a pixbuf containing an image of the given tile, unit or city
5090 restricted to the layer category 'cat'.
5092 May return NULL on error or bad input.
5093 NB: You must call g_object_unref on the non-NULL return value when you
5095 ****************************************************************************/
5096 static GdkPixbuf
*create_pixbuf_from_layers(const struct tile
*ptile
,
5097 const struct unit
*punit
,
5098 const struct city
*pcity
,
5099 enum layer_category category
)
5101 struct canvas canvas
= FC_STATIC_CANVAS_INIT
;
5102 int h
, fh
, fw
, canvas_x
, canvas_y
;
5106 fw
= tileset_full_tile_width(tileset
);
5107 fh
= tileset_full_tile_height(tileset
);
5108 h
= tileset_tile_height(tileset
);
5110 canvas
.surface
= cairo_image_surface_create(CAIRO_FORMAT_ARGB32
, fw
, fh
);
5112 cr
= cairo_create(canvas
.surface
);
5113 cairo_set_operator(cr
, CAIRO_OPERATOR_CLEAR
);
5120 canvas_y
+= (fh
- h
);
5122 mapview_layer_iterate(layer
) {
5123 if (tileset_layer_in_category(layer
, category
)) {
5124 put_one_element(&canvas
, 1.0, layer
,
5125 ptile
, NULL
, NULL
, punit
, pcity
,
5126 canvas_x
, canvas_y
, NULL
, NULL
);
5128 } mapview_layer_iterate_end
5129 pixbuf
= surface_get_pixbuf(canvas
.surface
, fw
, fh
);
5130 cairo_surface_destroy(canvas
.surface
);
5135 /****************************************************************************
5136 Remove all object binds (i.e. objects listed) in the property page.
5137 ****************************************************************************/
5138 static void property_page_clear_objbinds(struct property_page
*pp
)
5144 gtk_list_store_clear(pp
->object_store
);
5145 objbind_hash_clear(pp
->objbind_table
);
5146 property_page_set_focused_objbind(pp
, NULL
);
5149 /****************************************************************************
5150 Create a new object bind to the given object and register it with the
5151 given property page.
5152 ****************************************************************************/
5153 static void property_page_add_objbind(struct property_page
*pp
,
5154 gpointer object_data
)
5157 enum editor_object_type objtype
;
5164 objtype
= property_page_get_objtype(pp
);
5165 id
= objtype_get_id_from_object(objtype
, object_data
);
5170 if (objbind_hash_lookup(pp
->objbind_table
, id
, NULL
)) {
5171 /* Object already exists. */
5175 ob
= objbind_new(objtype
, object_data
);
5180 objbind_bind_properties(ob
, pp
);
5182 objbind_hash_insert(pp
->objbind_table
, ob
->object_id
, ob
);
5185 /****************************************************************************
5186 Create zero or more object binds from the objects on the given tile to
5187 the properties contained in the given property page.
5188 ****************************************************************************/
5189 static void property_page_add_objbinds_from_tile(struct property_page
*pp
,
5190 const struct tile
*ptile
)
5193 if (!pp
|| !ptile
) {
5197 switch (property_page_get_objtype(pp
)) {
5199 property_page_add_objbind(pp
, (gpointer
) ptile
);
5202 case OBJTYPE_STARTPOS
:
5204 struct startpos
*psp
= map_startpos_get(ptile
);
5207 property_page_add_objbind(pp
, map_startpos_get(ptile
));
5213 unit_list_iterate(ptile
->units
, punit
) {
5214 property_page_add_objbind(pp
, punit
);
5215 } unit_list_iterate_end
;
5219 if (tile_city(ptile
)) {
5220 property_page_add_objbind(pp
, tile_city(ptile
));
5224 case OBJTYPE_PLAYER
:
5232 log_error("%s(): Unhandled page object type %s (nb %d).", __FUNCTION__
,
5233 objtype_get_name(property_page_get_objtype(pp
)),
5234 property_page_get_objtype(pp
));
5237 /****************************************************************************
5238 Set the column value in the list store of the property page.
5239 Returns TRUE if data was enetered into the store.
5241 NB: This must match the conversion in objprop_get_gtype.
5242 ****************************************************************************/
5243 static bool property_page_set_store_value(struct property_page
*pp
,
5250 enum value_types valtype
;
5252 GdkPixbuf
*pixbuf
= NULL
;
5253 GtkListStore
*store
;
5256 if (!pp
|| !pp
->object_store
|| !op
|| !ob
) {
5260 if (!objprop_show_in_listview(op
)) {
5264 col_id
= objprop_get_column_id(op
);
5269 pv
= objbind_get_value_from_object(ob
, op
);
5274 valtype
= objprop_get_valtype(op
);
5275 store
= pp
->object_store
;
5281 gtk_list_store_set(store
, iter
, col_id
, pv
->data
.v_int
, -1);
5284 /* Set as translated string, not as untranslated G_TYPE_BOOLEAN */
5285 gtk_list_store_set(store
, iter
, col_id
, propval_as_string(pv
), -1);
5287 case VALTYPE_STRING
:
5288 if (fc_strlcpy(buf
, pv
->data
.v_string
, 28) >= 28) {
5289 sz_strlcat(buf
, "...");
5291 for (p
= buf
; *p
; p
++) {
5292 if (*p
== '\n' || *p
== '\t' || *p
== '\r') {
5296 gtk_list_store_set(store
, iter
, col_id
, buf
, -1);
5298 case VALTYPE_PIXBUF
:
5299 gtk_list_store_set(store
, iter
, col_id
, pv
->data
.v_pixbuf
, -1);
5301 case VALTYPE_BUILT_ARRAY
:
5302 case VALTYPE_INVENTIONS_ARRAY
:
5303 case VALTYPE_BV_SPECIAL
:
5304 case VALTYPE_BV_ROADS
:
5305 case VALTYPE_BV_BASES
:
5306 case VALTYPE_NATION_HASH
:
5307 buf2
= propval_as_string(pv
);
5308 gtk_list_store_set(store
, iter
, col_id
, buf2
, -1);
5311 case VALTYPE_NATION
:
5312 pixbuf
= get_flag(pv
->data
.v_nation
);
5313 gtk_list_store_set(store
, iter
, col_id
, pixbuf
, -1);
5315 g_object_unref(pixbuf
);
5319 pixbuf
= sprite_get_pixbuf(get_government_sprite(tileset
, pv
->data
.v_gov
));
5320 gtk_list_store_set(store
, iter
, col_id
, pixbuf
, -1);
5322 g_object_unref(pixbuf
);
5325 case VALTYPE_TILE_VISION_DATA
:
5334 /****************************************************************************
5335 Inserts any objbinds owned by this proprety page into the page's list
5336 store if they are not there already and refreshes all property widgets.
5337 ****************************************************************************/
5338 static void property_page_fill_widgets(struct property_page
*pp
)
5340 struct objbind
*focused
;
5342 if (!pp
|| !pp
->objbind_table
) {
5346 if (pp
->object_store
) {
5348 GtkTreeRowReference
*rr
;
5349 GtkTreeModel
*model
;
5352 model
= GTK_TREE_MODEL(pp
->object_store
);
5354 property_page_objbind_iterate(pp
, ob
) {
5355 if (objbind_get_rowref(ob
)) {
5358 gtk_list_store_append(pp
->object_store
, &iter
);
5359 gtk_list_store_set(pp
->object_store
, &iter
, 0, ob
, -1);
5360 path
= gtk_tree_model_get_path(model
, &iter
);
5361 rr
= gtk_tree_row_reference_new(model
, path
);
5362 gtk_tree_path_free(path
);
5363 objbind_set_rowref(ob
, rr
);
5365 property_page_objprop_iterate(pp
, op
) {
5366 property_page_set_store_value(pp
, op
, ob
, &iter
);
5367 } property_page_objprop_iterate_end
;
5368 } property_page_objbind_iterate_end
;
5370 if (gtk_tree_model_get_iter_first(model
, &iter
)) {
5371 GtkTreeSelection
*sel
;
5372 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pp
->object_view
));
5373 gtk_tree_selection_select_iter(sel
, &iter
);
5377 focused
= property_page_get_focused_objbind(pp
);
5378 property_page_objprop_iterate(pp
, op
) {
5379 objprop_refresh_widget(op
, focused
);
5380 } property_page_objprop_iterate_end
;
5383 /****************************************************************************
5384 Get the objbind corresponding to the object that is currently in view
5385 (i.e. in the information/properties panels) or NULL if none.
5386 ****************************************************************************/
5387 static struct objbind
*property_page_get_focused_objbind(struct property_page
*pp
)
5392 return pp
->focused_objbind
;
5395 /****************************************************************************
5396 Set the objbind that should be shown in the properties panel. Does not
5397 refresh property widgets.
5398 ****************************************************************************/
5399 static void property_page_set_focused_objbind(struct property_page
*pp
,
5405 pp
->focused_objbind
= ob
;
5408 /****************************************************************************
5409 Returns the objbind whose object corresponds to the given id, or NULL
5410 if no such objbind exists.
5411 ****************************************************************************/
5412 static struct objbind
*property_page_get_objbind(struct property_page
*pp
,
5417 if (!pp
|| !pp
->objbind_table
) {
5421 objbind_hash_lookup(pp
->objbind_table
, object_id
, &ob
);
5425 /****************************************************************************
5426 Removes all of the current objbinds and extracts new ones from the
5427 supplied list of tiles.
5428 ****************************************************************************/
5429 static void property_page_load_tiles(struct property_page
*pp
,
5430 const struct tile_list
*tiles
)
5432 if (!pp
|| !tiles
) {
5436 tile_list_iterate(tiles
, ptile
) {
5437 property_page_add_objbinds_from_tile(pp
, ptile
);
5438 } tile_list_iterate_end
;
5440 property_page_fill_widgets(pp
);
5443 /****************************************************************************
5444 Return the number of current bound objects to this property page.
5445 ****************************************************************************/
5446 static int property_page_get_num_objbinds(const struct property_page
*pp
)
5448 if (!pp
|| !pp
->objbind_table
) {
5451 return objbind_hash_size(pp
->objbind_table
);
5454 /****************************************************************************
5455 Called when a user sets a new value for the given property via the GUI.
5456 Refreshes the properties widget if anything changes.
5457 ****************************************************************************/
5458 static void property_page_change_value(struct property_page
*pp
,
5462 GtkTreeSelection
*sel
;
5463 GtkTreeModel
*model
;
5469 if (!pp
|| !op
|| !pp
->object_view
) {
5473 if (objprop_is_readonly(op
)) {
5477 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pp
->object_view
));
5478 rows
= gtk_tree_selection_get_selected_rows(sel
, &model
);
5480 for (p
= rows
; p
!= NULL
; p
= p
->next
) {
5482 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
5483 gtk_tree_model_get(model
, &iter
, 0, &ob
, -1);
5484 objbind_set_modified_value(ob
, op
, pv
);
5486 gtk_tree_path_free(path
);
5490 ob
= property_page_get_focused_objbind(pp
);
5491 objprop_refresh_widget(op
, ob
);
5494 /****************************************************************************
5495 Send all modified values of all selected properties.
5496 ****************************************************************************/
5497 static void property_page_send_values(struct property_page
*pp
)
5499 GtkTreeSelection
*sel
;
5500 GtkTreeModel
*model
;
5505 union packetdata packet
;
5506 struct connection
*my_conn
= &client
.conn
;
5508 if (!pp
|| !pp
->object_view
) {
5512 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pp
->object_view
));
5513 if (gtk_tree_selection_count_selected_rows(sel
) < 1) {
5517 packet
= property_page_new_packet(pp
);
5518 if (!packet
.pointers
.v_pointer1
) {
5522 rows
= gtk_tree_selection_get_selected_rows(sel
, &model
);
5523 connection_do_buffer(my_conn
);
5524 for (p
= rows
; p
!= NULL
; p
= p
->next
) {
5526 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
5527 gtk_tree_model_get(model
, &iter
, 0, &ob
, -1);
5528 if (objbind_has_modified_properties(ob
)) {
5529 objbind_pack_current_values(ob
, packet
);
5530 property_page_objprop_iterate(pp
, op
) {
5531 if (objprop_is_readonly(op
)) {
5534 objbind_pack_modified_value(ob
, op
, packet
);
5535 } property_page_objprop_iterate_end
;
5536 property_page_send_packet(pp
, packet
);
5539 gtk_tree_path_free(path
);
5541 connection_do_unbuffer(my_conn
);
5544 property_page_free_packet(pp
, packet
);
5547 /****************************************************************************
5548 Returns pointer to a packet suitable for this page's object type. Result
5549 should be freed using property_page_free_packet when no longer needed.
5550 ****************************************************************************/
5551 static union packetdata
property_page_new_packet(struct property_page
*pp
)
5553 union packetdata packet
;
5555 packet
.pointers
.v_pointer2
= NULL
;
5558 packet
.pointers
.v_pointer1
= NULL
;
5562 switch (property_page_get_objtype(pp
)) {
5564 packet
.tile
= fc_calloc(1, sizeof(*packet
.tile
));
5566 case OBJTYPE_STARTPOS
:
5567 packet
.startpos
= fc_calloc(1, sizeof(*packet
.startpos
));
5570 packet
.unit
= fc_calloc(1, sizeof(*packet
.unit
));
5573 packet
.city
= fc_calloc(1, sizeof(*packet
.city
));
5575 case OBJTYPE_PLAYER
:
5576 packet
.player
= fc_calloc(1, sizeof(*packet
.player
));
5579 packet
.game
.game
= fc_calloc(1, sizeof(*packet
.game
.game
));
5580 packet
.game
.desc
= fc_calloc(1, sizeof(*packet
.game
.desc
));
5589 /****************************************************************************
5590 Sends the given packet.
5591 ****************************************************************************/
5592 static void property_page_send_packet(struct property_page
*pp
,
5593 union packetdata packet
)
5595 struct connection
*my_conn
= &client
.conn
;
5597 if (!pp
|| !packet
.pointers
.v_pointer1
) {
5601 switch (property_page_get_objtype(pp
)) {
5603 send_packet_edit_tile(my_conn
, packet
.tile
);
5605 case OBJTYPE_STARTPOS
:
5606 send_packet_edit_startpos_full(my_conn
, packet
.startpos
);
5609 send_packet_edit_unit(my_conn
, packet
.unit
);
5612 send_packet_edit_city(my_conn
, packet
.city
);
5614 case OBJTYPE_PLAYER
:
5615 send_packet_edit_player(my_conn
, packet
.player
);
5618 send_packet_edit_game(my_conn
, packet
.game
.game
);
5619 send_packet_edit_scenario_desc(my_conn
, packet
.game
.desc
);
5625 log_error("%s(): Unhandled object type %s (nb %d).",
5626 __FUNCTION__
, objtype_get_name(property_page_get_objtype(pp
)),
5627 property_page_get_objtype(pp
));
5630 /****************************************************************************
5631 Free any resources being used by the packet.
5632 ****************************************************************************/
5633 static void property_page_free_packet(struct property_page
*pp
,
5634 union packetdata packet
)
5636 if (!packet
.pointers
.v_pointer1
) {
5640 free(packet
.pointers
.v_pointer1
);
5641 packet
.pointers
.v_pointer1
= NULL
;
5643 if (packet
.pointers
.v_pointer2
!= NULL
) {
5644 free(packet
.pointers
.v_pointer2
);
5645 packet
.pointers
.v_pointer2
= NULL
;
5649 /****************************************************************************
5650 Reload the displayed values of all properties for the selected bound
5651 objects. Hence, deletes all their stored modified values.
5652 ****************************************************************************/
5653 static void property_page_reset_objbinds(struct property_page
*pp
)
5655 GtkTreeSelection
*sel
;
5656 GtkTreeModel
*model
;
5662 if (!pp
|| !pp
->object_view
) {
5666 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pp
->object_view
));
5667 if (gtk_tree_selection_count_selected_rows(sel
) < 1) {
5671 rows
= gtk_tree_selection_get_selected_rows(sel
, &model
);
5672 for (p
= rows
; p
!= NULL
; p
= p
->next
) {
5674 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
5675 gtk_tree_model_get(model
, &iter
, 0, &ob
, -1);
5676 objbind_clear_all_modified_values(ob
);
5677 property_page_objprop_iterate(pp
, op
) {
5678 property_page_set_store_value(pp
, op
, ob
, &iter
);
5679 } property_page_objprop_iterate_end
;
5681 gtk_tree_path_free(path
);
5685 ob
= property_page_get_focused_objbind(pp
);
5686 property_page_objprop_iterate(pp
, op
) {
5687 objprop_refresh_widget(op
, ob
);
5688 } property_page_objprop_iterate_end
;
5691 /****************************************************************************
5692 Destroy all selected objects in the current property page.
5693 ****************************************************************************/
5694 static void property_page_destroy_objects(struct property_page
*pp
)
5696 GtkTreeSelection
*sel
;
5697 GtkTreeModel
*model
;
5702 struct connection
*my_conn
= &client
.conn
;
5704 if (!pp
|| !pp
->object_view
) {
5708 sel
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pp
->object_view
));
5709 if (gtk_tree_selection_count_selected_rows(sel
) < 1) {
5713 rows
= gtk_tree_selection_get_selected_rows(sel
, &model
);
5714 connection_do_buffer(my_conn
);
5715 for (p
= rows
; p
!= NULL
; p
= p
->next
) {
5717 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
5718 gtk_tree_model_get(model
, &iter
, 0, &ob
, -1);
5719 objbind_request_destroy_object(ob
);
5721 gtk_tree_path_free(path
);
5723 connection_do_unbuffer(my_conn
);
5727 /****************************************************************************
5728 Create objects corresponding to the type of this property page. Parameters
5729 such as the type, count, size and player owner are taken from the current
5730 editor state. The 'hint_tiles' argument is a list of tiles where the
5731 objects could be created.
5732 ****************************************************************************/
5733 static void property_page_create_objects(struct property_page
*pp
,
5734 struct tile_list
*hint_tiles
)
5736 enum editor_object_type objtype
;
5737 int apno
, value
, count
, size
;
5739 struct connection
*my_conn
= &client
.conn
;
5740 struct tile
*ptile
= NULL
;
5741 struct player
*pplayer
;
5747 objtype
= property_page_get_objtype(pp
);
5748 if (objtype_is_conserved(objtype
)) {
5752 tag
= get_next_unique_tag();
5756 case OBJTYPE_STARTPOS
:
5758 tile_list_iterate(hint_tiles
, atile
) {
5759 if (NULL
== map_startpos_get(atile
)) {
5763 } tile_list_iterate_end
;
5766 if (NULL
== ptile
) {
5767 ptile
= get_center_tile_mapcanvas();
5770 if (NULL
== ptile
) {
5774 dsend_packet_edit_startpos(my_conn
, tile_index(ptile
), FALSE
, tag
);
5779 tile_list_iterate(hint_tiles
, atile
) {
5780 if (can_create_unit_at_tile(atile
)) {
5784 } tile_list_iterate_end
;
5789 property_page_objbind_iterate(pp
, ob
) {
5790 punit
= objbind_get_object(ob
);
5791 if (punit
&& can_create_unit_at_tile(unit_tile(punit
))) {
5792 ptile
= unit_tile(punit
);
5795 } property_page_objbind_iterate_end
;
5799 ptile
= get_center_tile_mapcanvas();
5806 apno
= editor_tool_get_applied_player(ETT_UNIT
);
5807 count
= editor_tool_get_count(ETT_UNIT
);
5808 value
= editor_tool_get_value(ETT_UNIT
);
5809 dsend_packet_edit_unit_create(my_conn
, apno
, tile_index(ptile
),
5814 apno
= editor_tool_get_applied_player(ETT_CITY
);
5815 pplayer
= player_by_number(apno
);
5816 if (pplayer
&& hint_tiles
) {
5817 tile_list_iterate(hint_tiles
, atile
) {
5818 if (!is_enemy_unit_tile(atile
, pplayer
)
5819 && city_can_be_built_here(atile
, NULL
)) {
5823 } tile_list_iterate_end
;
5827 ptile
= get_center_tile_mapcanvas();
5834 size
= editor_tool_get_size(ETT_CITY
);
5835 dsend_packet_edit_city_create(my_conn
, apno
, tile_index(ptile
),
5839 case OBJTYPE_PLAYER
:
5840 dsend_packet_edit_player_create(my_conn
, tag
);
5849 property_page_store_creation_tag(pp
, tag
, count
);
5852 /****************************************************************************
5853 Update objbinds and widgets according to how the object given by
5854 'object_id' has changed. If the object no longer exists then the
5855 objbind is removed from the property page.
5856 ****************************************************************************/
5857 static void property_page_object_changed(struct property_page
*pp
,
5862 GtkTreeRowReference
*rr
;
5864 ob
= property_page_get_objbind(pp
, object_id
);
5869 rr
= objbind_get_rowref(ob
);
5870 if (rr
&& gtk_tree_row_reference_valid(rr
)) {
5873 GtkTreeModel
*model
;
5875 model
= GTK_TREE_MODEL(pp
->object_store
);
5876 path
= gtk_tree_row_reference_get_path(rr
);
5878 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
5880 gtk_list_store_remove(pp
->object_store
, &iter
);
5882 property_page_objprop_iterate(pp
, op
) {
5883 property_page_set_store_value(pp
, op
, ob
, &iter
);
5884 } property_page_objprop_iterate_end
;
5888 gtk_tree_path_free(path
);
5892 objbind_hash_remove(pp
->objbind_table
, object_id
);
5896 if (ob
== property_page_get_focused_objbind(pp
)) {
5897 property_page_objprop_iterate(pp
, op
) {
5898 objprop_refresh_widget(op
, ob
);
5899 } property_page_objprop_iterate_end
;
5903 /****************************************************************************
5904 Handle a notification of object creation sent back from the server. If
5905 this is something we previously requested, then 'tag' should be found in
5906 the tag table. In this case we create a new objbind for the object given
5907 by 'object_id' and add it to this page.
5908 ****************************************************************************/
5909 static void property_page_object_created(struct property_page
*pp
,
5910 int tag
, int object_id
)
5913 enum editor_object_type objtype
;
5915 if (!property_page_tag_is_known(pp
, tag
)) {
5918 property_page_remove_creation_tag(pp
, tag
);
5920 objtype
= property_page_get_objtype(pp
);
5921 object
= objtype_get_object_from_id(objtype
, object_id
);
5927 property_page_add_objbind(pp
, object
);
5928 property_page_fill_widgets(pp
);
5931 /****************************************************************************
5932 Add the extviewer's view widget to the property page so that it can
5933 be shown in the extended property view panel.
5934 ****************************************************************************/
5935 static void property_page_add_extviewer(struct property_page
*pp
,
5936 struct extviewer
*ev
)
5944 w
= extviewer_get_view_widget(ev
);
5948 gtk_notebook_append_page(GTK_NOTEBOOK(pp
->extviewer_notebook
), w
, NULL
);
5951 /****************************************************************************
5952 Make the given extended property viewer's view widget visible in the
5954 ****************************************************************************/
5955 static void property_page_show_extviewer(struct property_page
*pp
,
5956 struct extviewer
*ev
)
5959 GtkNotebook
*notebook
;
5966 w
= extviewer_get_view_widget(ev
);
5971 notebook
= GTK_NOTEBOOK(pp
->extviewer_notebook
);
5972 page
= gtk_notebook_page_num(notebook
, w
);
5973 gtk_notebook_set_current_page(notebook
, page
);
5976 /****************************************************************************
5977 Store the given object creation tag so that when the server notifies
5978 us about it we know what to do, up to 'count' times.
5979 ****************************************************************************/
5980 static void property_page_store_creation_tag(struct property_page
*pp
,
5983 if (!pp
|| !pp
->tag_table
) {
5987 if (stored_tag_hash_lookup(pp
->tag_table
, tag
, NULL
)) {
5988 log_error("Attempted to insert object creation tag %d "
5989 "twice into tag table for property page %p (%d %s).",
5990 tag
, pp
, property_page_get_objtype(pp
),
5991 property_page_get_name(pp
));
5995 stored_tag_hash_insert(pp
->tag_table
, tag
, count
);
5998 /****************************************************************************
5999 Decrease the tag count and remove the object creation tag if it is no
6001 ****************************************************************************/
6002 static void property_page_remove_creation_tag(struct property_page
*pp
,
6007 if (!pp
|| !pp
->tag_table
) {
6011 if (stored_tag_hash_lookup(pp
->tag_table
, tag
, &count
)) {
6013 stored_tag_hash_remove(pp
->tag_table
, tag
);
6018 /****************************************************************************
6019 Check if the given tag is one that we previously stored.
6020 ****************************************************************************/
6021 static bool property_page_tag_is_known(struct property_page
*pp
, int tag
)
6023 if (!pp
|| !pp
->tag_table
) {
6026 return stored_tag_hash_lookup(pp
->tag_table
, tag
, NULL
);
6029 /****************************************************************************
6030 Remove all tags in the tag table.
6031 ****************************************************************************/
6032 static void property_page_clear_tags(struct property_page
*pp
)
6034 if (!pp
|| !pp
->tag_table
) {
6037 stored_tag_hash_clear(pp
->tag_table
);
6040 /****************************************************************************
6041 Handles the 'clicked' signal for the "Apply" button in the property page.
6042 ****************************************************************************/
6043 static void property_page_apply_button_clicked(GtkButton
*button
,
6046 struct property_page
*pp
= userdata
;
6047 property_page_send_values(pp
);
6050 /****************************************************************************
6051 Handles the 'clicked' signal for the "Refresh" button in the
6053 ****************************************************************************/
6054 static void property_page_refresh_button_clicked(GtkButton
*button
,
6057 struct property_page
*pp
= userdata
;
6058 property_page_reset_objbinds(pp
);
6061 /****************************************************************************
6062 Handle a request to create a new object in the property page.
6063 ****************************************************************************/
6064 static void property_page_create_button_clicked(GtkButton
*button
,
6067 struct property_page
*pp
= userdata
, *tile_pp
;
6068 struct tile_list
*tiles
= NULL
;
6075 tile_pp
= property_editor_get_page(pp
->pe_parent
, OBJTYPE_TILE
);
6076 tiles
= tile_list_new();
6078 property_page_objbind_iterate(tile_pp
, ob
) {
6079 ptile
= objbind_get_object(ob
);
6081 tile_list_append(tiles
, ptile
);
6083 } property_page_objbind_iterate_end
;
6085 property_page_create_objects(pp
, tiles
);
6086 tile_list_destroy(tiles
);
6089 /****************************************************************************
6090 Handle a click on the "destroy" button.
6091 ****************************************************************************/
6092 static void property_page_destroy_button_clicked(GtkButton
*button
,
6095 struct property_page
*pp
= userdata
;
6096 property_page_destroy_objects(pp
);
6099 /****************************************************************************
6100 Create and add a property page for the given object type
6101 to the property editor. Returns TRUE if successful.
6102 ****************************************************************************/
6103 static bool property_editor_add_page(struct property_editor
*pe
,
6104 enum editor_object_type objtype
)
6106 struct property_page
*pp
;
6110 if (!pe
|| !pe
->notebook
) {
6114 if (!(0 <= objtype
&& objtype
< NUM_OBJTYPES
)) {
6118 pp
= property_page_new(objtype
, pe
);
6123 name
= property_page_get_name(pp
);
6124 label
= gtk_label_new(name
);
6125 gtk_notebook_append_page(GTK_NOTEBOOK(pe
->notebook
),
6128 pe
->property_pages
[objtype
] = pp
;
6133 /****************************************************************************
6134 Returns the property page for the given object type.
6135 ****************************************************************************/
6136 static struct property_page
*
6137 property_editor_get_page(struct property_editor
*pe
,
6138 enum editor_object_type objtype
)
6140 if (!pe
|| !(0 <= objtype
&& objtype
< NUM_OBJTYPES
)) {
6144 return pe
->property_pages
[objtype
];
6147 /****************************************************************************
6148 Create and return the property editor widget bundle.
6149 ****************************************************************************/
6150 static struct property_editor
*property_editor_new(void)
6152 struct property_editor
*pe
;
6153 GtkWidget
*win
, *notebook
, *vbox
;
6154 enum editor_object_type objtype
;
6156 pe
= fc_calloc(1, sizeof(*pe
));
6158 /* The property editor dialog window. */
6160 win
= gtk_window_new(GTK_WINDOW_TOPLEVEL
);
6161 gtk_window_set_title(GTK_WINDOW(win
), _("Property Editor"));
6162 gtk_window_set_resizable(GTK_WINDOW(win
), TRUE
);
6163 gtk_window_set_default_size(GTK_WINDOW(win
), 780, 560);
6164 gtk_window_set_position(GTK_WINDOW(win
), GTK_WIN_POS_CENTER_ON_PARENT
);
6165 gtk_window_set_transient_for(GTK_WINDOW(win
), GTK_WINDOW(toplevel
));
6166 gtk_window_set_destroy_with_parent(GTK_WINDOW(win
), TRUE
);
6167 gtk_window_set_type_hint(GTK_WINDOW(win
), GDK_WINDOW_TYPE_HINT_DIALOG
);
6168 gtk_container_set_border_width(GTK_CONTAINER(win
), 4);
6169 g_signal_connect(win
, "delete-event",
6170 G_CALLBACK(gtk_widget_hide_on_delete
), NULL
);
6173 vbox
= gtk_grid_new();
6174 gtk_container_add(GTK_CONTAINER(win
), vbox
);
6176 /* Property pages. */
6178 notebook
= gtk_notebook_new();
6179 gtk_notebook_set_show_tabs(GTK_NOTEBOOK(notebook
), TRUE
);
6180 gtk_container_add(GTK_CONTAINER(vbox
), notebook
);
6181 pe
->notebook
= notebook
;
6183 for (objtype
= 0; objtype
< NUM_OBJTYPES
; objtype
++) {
6184 property_editor_add_page(pe
, objtype
);
6190 /****************************************************************************
6191 Get the property editor for the client's GUI.
6192 ****************************************************************************/
6193 struct property_editor
*editprop_get_property_editor(void)
6195 if (!the_property_editor
) {
6196 the_property_editor
= property_editor_new();
6198 return the_property_editor
;
6201 /****************************************************************************
6202 Refresh the given property editor according to the given list of tiles.
6203 ****************************************************************************/
6204 void property_editor_load_tiles(struct property_editor
*pe
,
6205 const struct tile_list
*tiles
)
6207 struct property_page
*pp
;
6208 enum editor_object_type objtype
;
6210 const enum editor_object_type preferred
[] = {
6217 if (!pe
|| !tiles
) {
6221 for (objtype
= 0; objtype
< NUM_OBJTYPES
; objtype
++) {
6222 pp
= property_editor_get_page(pe
, objtype
);
6223 property_page_load_tiles(pp
, tiles
);
6226 for (i
= 0; i
< ARRAY_SIZE(preferred
) - 1; i
++) {
6227 pp
= property_editor_get_page(pe
, preferred
[i
]);
6228 if (property_page_get_num_objbinds(pp
) > 0) {
6232 objtype
= preferred
[i
];
6233 gtk_notebook_set_current_page(GTK_NOTEBOOK(pe
->notebook
), objtype
);
6236 /****************************************************************************
6237 Show the property editor to the user, with given page corresponding to
6238 'objtype' in front (if a valid object type).
6239 ****************************************************************************/
6240 void property_editor_popup(struct property_editor
*pe
,
6241 enum editor_object_type objtype
)
6243 if (!pe
|| !pe
->widget
) {
6247 gtk_widget_show_all(pe
->widget
);
6249 gtk_window_present(GTK_WINDOW(pe
->widget
));
6250 if (0 <= objtype
&& objtype
< NUM_OBJTYPES
) {
6251 gtk_notebook_set_current_page(GTK_NOTEBOOK(pe
->notebook
), objtype
);
6255 /****************************************************************************
6256 Hide the property editor window.
6257 ****************************************************************************/
6258 void property_editor_popdown(struct property_editor
*pe
)
6260 if (!pe
|| !pe
->widget
) {
6263 gtk_widget_hide(pe
->widget
);
6266 /****************************************************************************
6267 Handle a notification from the client core that some object has changed
6268 state at the server side (including being removed).
6269 ****************************************************************************/
6270 void property_editor_handle_object_changed(struct property_editor
*pe
,
6271 enum editor_object_type objtype
,
6275 struct property_page
*pp
;
6281 if (!(0 <= objtype
&& objtype
< NUM_OBJTYPES
)) {
6285 pp
= property_editor_get_page(pe
, objtype
);
6286 property_page_object_changed(pp
, object_id
, remove
);
6289 /****************************************************************************
6290 Handle a notification that an object was created under the given tag.
6291 ****************************************************************************/
6292 void property_editor_handle_object_created(struct property_editor
*pe
,
6293 int tag
, int object_id
)
6295 enum editor_object_type objtype
;
6296 struct property_page
*pp
;
6298 for (objtype
= 0; objtype
< NUM_OBJTYPES
; objtype
++) {
6299 if (objtype_is_conserved(objtype
)) {
6302 pp
= property_editor_get_page(pe
, objtype
);
6303 property_page_object_created(pp
, tag
, object_id
);
6307 /****************************************************************************
6308 Clear all property pages in the given property editor.
6309 ****************************************************************************/
6310 void property_editor_clear(struct property_editor
*pe
)
6312 enum editor_object_type objtype
;
6313 struct property_page
*pp
;
6319 for (objtype
= 0; objtype
< NUM_OBJTYPES
; objtype
++) {
6320 pp
= property_editor_get_page(pe
, objtype
);
6321 property_page_clear_objbinds(pp
);
6322 property_page_clear_tags(pp
);
6326 /****************************************************************************
6327 Clear and load objects into the property page corresponding to the given
6328 object type. Also, make it the current shown notebook page.
6329 ****************************************************************************/
6330 void property_editor_reload(struct property_editor
*pe
,
6331 enum editor_object_type objtype
)
6333 struct property_page
*pp
;
6339 pp
= property_editor_get_page(pe
, objtype
);
6344 property_page_clear_objbinds(pp
);
6347 case OBJTYPE_PLAYER
:
6348 players_iterate(pplayer
) {
6349 property_page_add_objbind(pp
, pplayer
);
6350 } players_iterate_end
;
6353 property_page_add_objbind(pp
, &game
);
6356 case OBJTYPE_STARTPOS
:
6363 property_page_fill_widgets(pp
);
6364 gtk_notebook_set_current_page(GTK_NOTEBOOK(pe
->notebook
), objtype
);
6367 /****************************************************************************
6368 Create a new property filter from the given filter string. Result
6369 should be freed by property_filter_free when no longed needed.
6371 The filter string is '|' ("or") separated list of '&' ("and") separated
6372 lists of patterns. A pattern may be preceeded by '!' to have its result
6375 NB: If you change the behaviour of this function, be sure to update
6376 the filter tooltip in property_page_new().
6377 ****************************************************************************/
6378 static struct property_filter
*property_filter_new(const char *filter
)
6380 struct property_filter
*pf
;
6381 struct pf_conjunction
*pfc
;
6382 struct pf_pattern
*pfp
;
6383 int or_clause_count
, and_clause_count
;
6384 char *or_clauses
[PF_MAX_CLAUSES
], *and_clauses
[PF_MAX_CLAUSES
];
6385 const char *pattern
;
6388 pf
= fc_calloc(1, sizeof(*pf
));
6390 if (!filter
|| filter
[0] == '\0') {
6394 or_clause_count
= get_tokens(filter
, or_clauses
,
6396 PF_DISJUNCTION_SEPARATOR
);
6398 for (i
= 0; i
< or_clause_count
; i
++) {
6399 if (or_clauses
[i
][0] == '\0') {
6402 pfc
= &pf
->disjunction
[pf
->count
];
6404 and_clause_count
= get_tokens(or_clauses
[i
], and_clauses
,
6406 PF_CONJUNCTION_SEPARATOR
);
6408 for (j
= 0; j
< and_clause_count
; j
++) {
6409 if (and_clauses
[j
][0] == '\0') {
6412 pfp
= &pfc
->conjunction
[pfc
->count
];
6413 pattern
= and_clauses
[j
];
6415 switch (pattern
[0]) {
6418 pfp
->text
= fc_strdup(pattern
+ 1);
6421 pfp
->text
= fc_strdup(pattern
);
6426 free_tokens(and_clauses
, and_clause_count
);
6430 free_tokens(or_clauses
, or_clause_count
);
6435 /****************************************************************************
6436 Returns TRUE if the filter matches the given object property.
6438 The filter matches if its truth value is TRUE. That is, it has at least
6439 one OR clause in which all AND clauses are TRUE. An AND clause is TRUE
6440 if its pattern matches the name of the given object property (case is
6441 ignored), or it is negated and does not match. For example:
6443 a - Matches all properties whose names contain "a" (or "A").
6444 !a - Matches all properties whose names do not contain "a".
6445 a|b - Matches all properties whose names contain "a" or "b".
6446 a|b&c - Matches all properties whose names contain either an "a",
6447 or contain both "b" and "c".
6449 NB: If you change the behaviour of this function, be sure to update
6450 the filter tooltip in property_page_new().
6451 ****************************************************************************/
6452 static bool property_filter_match(struct property_filter
*pf
,
6453 const struct objprop
*op
)
6455 struct pf_pattern
*pfp
;
6456 struct pf_conjunction
*pfc
;
6458 bool match
, or_result
, and_result
;
6468 name
= objprop_get_name(op
);
6473 if (pf
->count
< 1) {
6479 for (i
= 0; i
< pf
->count
; i
++) {
6480 pfc
= &pf
->disjunction
[i
];
6482 for (j
= 0; j
< pfc
->count
; j
++) {
6483 pfp
= &pfc
->conjunction
[j
];
6484 match
= (pfp
->text
[0] == '\0'
6485 || fc_strcasestr(name
, pfp
->text
));
6489 and_result
= and_result
&& match
;
6494 or_result
= or_result
|| and_result
;
6503 /****************************************************************************
6504 Frees all memory used by the property filter.
6505 ****************************************************************************/
6506 static void property_filter_free(struct property_filter
*pf
)
6508 struct pf_pattern
*pfp
;
6509 struct pf_conjunction
*pfc
;
6516 for (i
= 0; i
< pf
->count
; i
++) {
6517 pfc
= &pf
->disjunction
[i
];
6518 for (j
= 0; j
< pfc
->count
; j
++) {
6519 pfp
= &pfc
->conjunction
[j
];
6520 if (pfp
->text
!= NULL
) {
6531 /****************************************************************************
6532 Returns a translated string name for the given "vision layer".
6533 ****************************************************************************/
6534 const char *vision_layer_get_name(enum vision_layer vl
)
6538 /* TRANS: Vision layer name. Feel free to leave untranslated. */
6539 return _("Seen (Main)");
6541 /* TRANS: Vision layer name. Feel free to leave untranslated. */
6542 return _("Seen (Invis)");
6547 log_error("%s(): Unrecognized vision layer %d.", __FUNCTION__
, vl
);
6548 return _("Unknown");