Line data Source code
1 : /* File: gui_main_window.c; Copyright and License: see below */
2 :
3 : #include "gui_main_window.h"
4 : #include "gui_clipboard.h"
5 : #include "gui_type_resource.h"
6 : #include "u8/u8_trace.h"
7 : #include "entity/data_diagram_type.h"
8 : #include "entity/data_classifier_type.h"
9 : #include "entity/data_feature_type.h"
10 : #include "entity/data_relationship_type.h"
11 : #include "storage/data_database.h"
12 : #include "storage/data_change_notifier.h"
13 : #include "meta/meta_info.h"
14 : #include "gui_gtk.h"
15 : #include "gui_gdk.h"
16 : #include <stdio.h>
17 : #include <stdbool.h>
18 :
19 0 : void gui_main_window_init( gui_main_window_t *this_,
20 : ctrl_controller_t *controller,
21 : io_data_file_t *data_file,
22 : data_database_reader_t *db_reader,
23 : const gui_resources_t *resources,
24 : GtkApplication *gtk_app,
25 : observer_t *window_close_observer,
26 : observer_t *window_open_observer )
27 : {
28 0 : U8_TRACE_BEGIN();
29 0 : assert( gtk_app != NULL );
30 :
31 : /* init own attributes */
32 0 : (*this_).window_close_observer = window_close_observer;
33 0 : (*this_).window_open_observer = window_open_observer;
34 0 : (*this_).data_file = data_file;
35 0 : (*this_).resources = resources;
36 :
37 : /* init window */
38 : {
39 0 : (*this_).window = gtk_application_window_new( gtk_app );
40 : const char *window_title;
41 0 : window_title = io_data_file_get_filename_const( data_file );
42 0 : gtk_window_set_title(GTK_WINDOW( (*this_).window ), ( window_title == NULL ) ? META_INFO_PROGRAM_NAME_STR : window_title );
43 0 : gtk_widget_set_size_request( (*this_).window, 800, 400 );
44 0 : gtk_window_set_default_size( GTK_WINDOW( (*this_).window ), 16*70, 9*70 );
45 : }
46 :
47 : /* init the message widgets */
48 0 : gui_main_window_private_init_simple_message_to_user( this_ );
49 0 : gui_simple_message_to_user_init( &((*this_).message_to_user), (*this_).message_text_label, (*this_).message_icon_image, resources );
50 :
51 : /* init the keyboard shortcuts */
52 : {
53 0 : (*this_).keyboard_shortcut_ctrl = GTK_SHORTCUT_CONTROLLER(gtk_shortcut_controller_new());
54 0 : gtk_widget_add_controller( (*this_).window, GTK_EVENT_CONTROLLER((*this_).keyboard_shortcut_ctrl) );
55 : }
56 :
57 : /* init tools */
58 0 : gui_main_window_private_init_toolbox( this_ );
59 :
60 : /* determine the current/main clipboard */
61 0 : GdkClipboard *current_clipboard = NULL;
62 : {
63 0 : current_clipboard = gtk_widget_get_clipboard( GTK_WIDGET((*this_).window) ); /* idea taken from gtk demo */
64 : }
65 :
66 0 : gui_marked_set_init( &((*this_).marker_data),
67 0 : G_OBJECT((*this_).window),
68 : &gui_sketch_area_show_diagram,
69 0 : &((*this_).sketcharea_data)
70 : );
71 0 : gui_toolbox_init( &((*this_).tools_data),
72 0 : GTK_WIDGET( (*this_).tool_row ),
73 0 : gui_button_get_widget_ptr( &((*this_).view_navigate) ),
74 0 : gui_button_get_widget_ptr( &((*this_).view_edit) ),
75 0 : gui_button_get_widget_ptr( &((*this_).view_create) ),
76 0 : gui_button_get_widget_ptr( &((*this_).view_search) ),
77 : current_clipboard,
78 : &((*this_).marker_data),
79 : &((*this_).message_to_user),
80 : db_reader,
81 : controller
82 : );
83 :
84 : /* init search widgets and sketch area */
85 : observer_t search_result_observer;
86 0 : observer_init( &search_result_observer,
87 0 : &((*this_).sketcharea_data),
88 : (void(*)(void*,void*))&gui_sketch_area_show_result_list,
89 : "search_results_available"
90 : );
91 0 : gui_main_window_private_init_search_and_sketch_area( this_ );
92 0 : gui_search_runner_init( &((*this_).search_runner),
93 : &((*this_).message_to_user),
94 : db_reader,
95 : io_data_file_get_database_ptr( data_file ),
96 : search_result_observer
97 : );
98 0 : gui_search_request_init( &((*this_).search_request),
99 : (*this_).search_label,
100 : (*this_).search_entry,
101 : (*this_).search_button,
102 : &((*this_).marker_data),
103 : &((*this_).search_runner)
104 : );
105 0 : gui_sketch_area_init( &((*this_).sketcharea_data),
106 : (*this_).sketcharea,
107 : &((*this_).marker_data),
108 : &((*this_).tools_data),
109 : &((*this_).search_runner),
110 : &((*this_).message_to_user),
111 : resources,
112 : controller,
113 : db_reader
114 : );
115 :
116 : /* init attribute editor widgets */
117 0 : gui_main_window_private_init_attributes_editor( this_ );
118 :
119 0 : gui_attributes_editor_init( &((*this_).attributes_editor),
120 0 : GTK_LABEL( (*this_).id_label ),
121 0 : GTK_ENTRY( (*this_).name_entry ),
122 0 : GTK_ENTRY( (*this_).stereotype_entry ),
123 : (*this_).type_dropdown,
124 0 : GTK_WIDGET( (*this_).type_diag_grid ),
125 0 : GTK_WIDGET( (*this_).type_clas_grid ),
126 0 : GTK_WIDGET( (*this_).type_feat_grid ),
127 0 : GTK_WIDGET( (*this_).type_rel_grid ),
128 0 : GTK_TEXT_VIEW( (*this_).description_text_view ),
129 0 : gui_button_get_button_ptr( &((*this_).file_save ) ),
130 : resources,
131 : controller,
132 : db_reader,
133 : io_data_file_get_database_ptr( data_file ),
134 : &((*this_).message_to_user)
135 : );
136 :
137 : /* init the file choosers */
138 0 : gui_file_action_init( &((*this_).file_action),
139 : controller,
140 : data_file,
141 : &((*this_).message_to_user)
142 : );
143 0 : gui_file_use_db_dialog_init( &((*this_).file_use_db_dialog),
144 0 : GTK_WINDOW( (*this_).window ),
145 : &((*this_).file_action)
146 : );
147 0 : gui_file_export_dialog_init( &((*this_).file_export_dialog),
148 : io_data_file_get_database_ptr( data_file ),
149 : db_reader,
150 : (*this_).resources,
151 0 : GTK_WINDOW( (*this_).window ),
152 : &((*this_).message_to_user)
153 : );
154 :
155 0 : U8_TRACE_INFO("GTK+ Widgets are created.");
156 :
157 0 : (*this_).two_panes = gtk_paned_new( GTK_ORIENTATION_HORIZONTAL );
158 0 : gtk_paned_set_start_child( GTK_PANED((*this_).two_panes), (*this_).sketch_stack_column );
159 0 : gtk_paned_set_resize_start_child( GTK_PANED((*this_).two_panes), true );
160 0 : gtk_paned_set_shrink_start_child( GTK_PANED((*this_).two_panes), false );
161 0 : gtk_paned_set_end_child( GTK_PANED((*this_).two_panes), (*this_).attr_edit_column );
162 0 : gtk_paned_set_resize_end_child( GTK_PANED((*this_).two_panes), false );
163 0 : gtk_paned_set_shrink_end_child( GTK_PANED((*this_).two_panes), true );
164 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).sketch_stack_column ), true );
165 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).sketch_stack_column ), true );
166 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).attr_edit_column ), true );
167 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).attr_edit_column ), false );
168 0 : gtk_paned_set_position( GTK_PANED((*this_).two_panes), 11*70 );
169 :
170 0 : (*this_).main_stack_column = gtk_box_new( GTK_ORIENTATION_VERTICAL, /*spacing:*/ 0 );
171 0 : gtk_box_append( GTK_BOX((*this_).main_stack_column), GTK_WIDGET((*this_).tool_row) );
172 0 : (*this_).options_layout = gui_file_export_dialog_get_options( &((*this_).file_export_dialog) );
173 0 : gtk_box_append( GTK_BOX((*this_).main_stack_column), (*this_).options_layout );
174 0 : gtk_box_append( GTK_BOX((*this_).main_stack_column), GTK_WIDGET((*this_).two_panes) );
175 0 : gtk_box_append( GTK_BOX((*this_).main_stack_column), GTK_WIDGET((*this_).message_row) );
176 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).tool_row ), false );
177 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).tool_row ), true );
178 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).two_panes ), true );
179 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).two_panes ), true );
180 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).message_row ), false );
181 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).message_row ), true );
182 :
183 0 : gtk_window_set_child( GTK_WINDOW((*this_).window), (*this_).main_stack_column );
184 0 : gtk_window_set_resizable( GTK_WINDOW((*this_).window), true );
185 :
186 0 : U8_TRACE_INFO("GTK+ Widgets are added to containers.");
187 :
188 : /* inject dependencies by signals */
189 : /* parameter info: g_signal_connect( instance-that-emits-the-signal, signal-name, callback-handler, data-to-be-passed-to-callback-handler) */
190 0 : g_signal_connect( G_OBJECT((*this_).window), "close-request", G_CALLBACK(gui_main_window_delete_event_callback), this_ );
191 0 : g_signal_connect( G_OBJECT((*this_).window), "destroy", G_CALLBACK(gui_main_window_destroy_event_callback), this_ );
192 0 : g_signal_connect( G_OBJECT((*this_).window), "state-flags-changed", G_CALLBACK( gui_main_window_state_callback ), this_ );
193 0 : g_signal_connect( G_OBJECT((*this_).window), DATA_CHANGE_NOTIFIER_GLIB_SIGNAL_NAME, G_CALLBACK(gui_main_window_data_changed_callback), this_ );
194 0 : g_signal_connect( G_OBJECT((*this_).sketcharea), DATA_CHANGE_NOTIFIER_GLIB_SIGNAL_NAME, G_CALLBACK(gui_sketch_area_data_changed_callback), &((*this_).sketcharea_data) );
195 0 : g_signal_connect( G_OBJECT((*this_).tool_row), GUI_TOOLBOX_GLIB_SIGNAL_NAME, G_CALLBACK(gui_sketch_area_tool_changed_callback), &((*this_).sketcharea_data) );
196 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).file_new) ), "clicked", G_CALLBACK(gui_main_window_new_db_btn_callback), this_ );
197 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).file_open) ), "clicked", G_CALLBACK(gui_main_window_open_db_btn_callback), this_ );
198 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).file_save) ), "clicked", G_CALLBACK(gui_main_window_save_btn_callback), this_ );
199 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).file_export) ), "clicked", G_CALLBACK(gui_main_window_export_btn_callback), this_ );
200 :
201 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).view_new_window) ), "clicked", G_CALLBACK(gui_main_window_new_window_btn_callback), this_ );
202 :
203 0 : gui_toolbox_t *const tools = &((*this_).tools_data);
204 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).view_navigate) ), "clicked", G_CALLBACK(gui_toolbox_navigate_btn_callback), tools );
205 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).view_edit) ), "clicked", G_CALLBACK(gui_toolbox_edit_btn_callback), tools );
206 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).view_create) ), "clicked", G_CALLBACK(gui_toolbox_create_btn_callback), tools );
207 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).view_search) ), "clicked", G_CALLBACK(gui_toolbox_search_btn_callback), tools );
208 :
209 : /* The search id button is connected to two callback functions: one to switch to search mode, one to perform a search: */
210 0 : g_signal_connect( G_OBJECT((*this_).id_search_btn),
211 : "clicked",
212 : G_CALLBACK(gui_toolbox_search_id_btn_callback),
213 : tools
214 : );
215 0 : g_signal_connect( G_OBJECT((*this_).id_search_btn),
216 : "clicked",
217 : G_CALLBACK(gui_search_request_id_search_callback),
218 : &((*this_).search_request)
219 : );
220 :
221 0 : g_signal_connect( G_OBJECT((*this_).tool_row), GUI_TOOLBOX_GLIB_SIGNAL_NAME, G_CALLBACK(gui_search_request_tool_changed_callback), &((*this_).search_request) );
222 0 : g_signal_connect( G_OBJECT((*this_).search_entry), DATA_CHANGE_NOTIFIER_GLIB_SIGNAL_NAME, G_CALLBACK(gui_search_request_data_changed_callback), &((*this_).search_request) );
223 0 : g_signal_connect( G_OBJECT((*this_).search_entry), "activate", G_CALLBACK(gui_search_request_search_start_callback), &((*this_).search_request) );
224 0 : g_signal_connect( G_OBJECT((*this_).search_button), "clicked", G_CALLBACK(gui_search_request_search_start_callback), &((*this_).search_request) );
225 :
226 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_undo) ), "clicked", G_CALLBACK(gui_toolbox_undo_btn_callback), tools );
227 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_redo) ), "clicked", G_CALLBACK(gui_toolbox_redo_btn_callback), tools );
228 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_cut) ), "clicked", G_CALLBACK(gui_toolbox_cut_btn_callback), tools );
229 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_copy) ), "clicked", G_CALLBACK(gui_toolbox_copy_btn_callback), tools );
230 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_paste) ), "clicked", G_CALLBACK(gui_toolbox_paste_btn_callback), tools );
231 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_delete) ), "clicked", G_CALLBACK(gui_toolbox_delete_btn_callback), tools );
232 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_instantiate) ), "clicked", G_CALLBACK(gui_toolbox_instantiate_btn_callback), tools );
233 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_highlight) ), "clicked", G_CALLBACK(gui_toolbox_highlight_btn_callback), tools );
234 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).edit_reset) ), "clicked", G_CALLBACK(gui_toolbox_reset_btn_callback), tools );
235 :
236 0 : g_signal_connect( G_OBJECT((*this_).name_entry),
237 : "activate",
238 : G_CALLBACK(gui_attributes_editor_name_enter_callback),
239 : &((*this_).attributes_editor)
240 : );
241 : {
242 0 : GtkEventControllerFocus *evt_focus_n = GTK_EVENT_CONTROLLER_FOCUS(gtk_event_controller_focus_new());
243 0 : g_signal_connect( evt_focus_n,
244 : "leave",
245 : G_CALLBACK(gui_attributes_editor_name_focus_left_callback),
246 : &((*this_).attributes_editor)
247 : );
248 0 : gtk_widget_add_controller( (*this_).name_entry, GTK_EVENT_CONTROLLER(evt_focus_n) );
249 : }
250 : {
251 0 : GtkEventControllerFocus *evt_focus_s = GTK_EVENT_CONTROLLER_FOCUS(gtk_event_controller_focus_new());
252 0 : g_signal_connect( evt_focus_s,
253 : "leave",
254 : G_CALLBACK(gui_attributes_editor_stereotype_focus_left_callback),
255 : &((*this_).attributes_editor)
256 : );
257 0 : gtk_widget_add_controller( (*this_).stereotype_entry, GTK_EVENT_CONTROLLER(evt_focus_s) );
258 : }
259 : {
260 0 : GtkEventControllerFocus *evt_focus_d = GTK_EVENT_CONTROLLER_FOCUS(gtk_event_controller_focus_new());
261 0 : g_signal_connect( evt_focus_d,
262 : "leave",
263 : G_CALLBACK(gui_attributes_editor_description_focus_left_callback),
264 : &((*this_).attributes_editor)
265 : );
266 0 : gtk_widget_add_controller( (*this_).description_text_view, GTK_EVENT_CONTROLLER(evt_focus_d) );
267 : }
268 0 : g_signal_connect( G_OBJECT((*this_).stereotype_entry), "activate", G_CALLBACK(gui_attributes_editor_stereotype_enter_callback), &((*this_).attributes_editor) );
269 0 : g_signal_connect_after( G_OBJECT((*this_).type_dropdown), "notify::selected", G_CALLBACK(gui_attributes_editor_type_changed_callback), &((*this_).attributes_editor));
270 0 : for( int_fast32_t diag_idx = 0; diag_idx < GUI_TYPE_RESOURCE_LIST_DIAGRAMS; diag_idx ++ )
271 : {
272 0 : g_signal_connect( G_OBJECT((*this_).type_diag_btn[ diag_idx ]),
273 : "clicked",
274 : G_CALLBACK( gui_attributes_editor_type_of_diagram_btn_callback ),
275 : &((*this_).type_diag_data[diag_idx])
276 : );
277 : }
278 0 : for( int_fast32_t clas_idx = 0; clas_idx < GUI_TYPE_RESOURCE_LIST_CLASSIFIERS; clas_idx ++ )
279 : {
280 0 : g_signal_connect( G_OBJECT((*this_).type_clas_btn[ clas_idx ]),
281 : "clicked",
282 : G_CALLBACK( gui_attributes_editor_type_of_classifier_btn_callback ),
283 : &((*this_).type_clas_data[clas_idx])
284 : );
285 : }
286 0 : for( int_fast32_t feat_idx = 0; feat_idx < GUI_TYPE_RESOURCE_LIST_INV_FEATURES; feat_idx ++ )
287 : {
288 0 : g_signal_connect( G_OBJECT((*this_).type_feat_btn[ feat_idx ]),
289 : "clicked",
290 : G_CALLBACK( gui_attributes_editor_type_of_feature_btn_callback ),
291 : &((*this_).type_feat_data[feat_idx])
292 : );
293 : }
294 0 : for( int_fast32_t rel_idx = 0; rel_idx < GUI_TYPE_RESOURCE_LIST_RELATIONS; rel_idx ++ )
295 : {
296 0 : g_signal_connect( G_OBJECT((*this_).type_rel_btn[ rel_idx ]),
297 : "clicked",
298 : G_CALLBACK( gui_attributes_editor_type_of_relationship_btn_callback ),
299 : &((*this_).type_rel_data[rel_idx])
300 : );
301 : }
302 0 : g_signal_connect( G_OBJECT((*this_).window), GUI_MARKED_SET_GLIB_SIGNAL_NAME, G_CALLBACK(gui_attributes_editor_focused_object_changed_callback), &((*this_).attributes_editor) );
303 0 : g_signal_connect( G_OBJECT((*this_).name_entry), DATA_CHANGE_NOTIFIER_GLIB_SIGNAL_NAME, G_CALLBACK(gui_attributes_editor_data_changed_callback), &((*this_).attributes_editor) );
304 : /* ^-- name_entry is the proxy for all widgets of attributes_editor */
305 :
306 0 : g_signal_connect( gui_button_get_widget_ptr( &((*this_).help_about) ), "clicked", G_CALLBACK(gui_main_window_about_btn_callback), this_ );
307 :
308 0 : U8_TRACE_INFO("GTK+ Callbacks are connected to widget events.");
309 :
310 : /* register observers */
311 :
312 0 : (*this_).data_notifier = data_database_get_notifier_ptr( io_data_file_get_database_ptr( data_file ) );
313 0 : data_change_notifier_add_listener( (*this_).data_notifier, G_OBJECT((*this_).window) );
314 0 : data_change_notifier_add_listener( (*this_).data_notifier, G_OBJECT((*this_).sketcharea) );
315 0 : data_change_notifier_add_listener( (*this_).data_notifier, G_OBJECT((*this_).name_entry) );
316 : /* ^-- name_entry is the proxy for all widgets of attributes_editor */
317 0 : data_change_notifier_add_listener( (*this_).data_notifier, G_OBJECT((*this_).search_entry) );
318 :
319 0 : U8_TRACE_INFO("GTK+ Widgets are registered as listeners at signal emitter.");
320 :
321 0 : gtk_widget_set_visible( (*this_).window, TRUE );
322 0 : gtk_widget_set_sensitive( GTK_WIDGET((*this_).window), TRUE ); /* idea taken from gtk demo */
323 :
324 0 : GdkSurface *surface = gtk_native_get_surface( GTK_NATIVE((*this_).window) );
325 0 : gdk_surface_set_cursor( surface, NULL ); /* idea taken from gtk3->4 guide */
326 :
327 0 : gui_attributes_editor_update_widgets( &((*this_).attributes_editor) ); /* hide some widgets again */
328 : #ifdef NDEBUG
329 : gui_simple_message_to_user_hide( &((*this_).message_to_user) );
330 : #else
331 0 : gui_simple_message_to_user_show_message ( &((*this_).message_to_user), GUI_SIMPLE_MESSAGE_TYPE_INFO, GUI_SIMPLE_MESSAGE_CONTENT_DEBUG_MODE );
332 : #endif
333 0 : gui_search_request_hide( &((*this_).search_request) );
334 0 : U8_TRACE_INFO("GTK+ Widgets are shown.");
335 0 : U8_TRACE_END();
336 0 : }
337 :
338 0 : void gui_main_window_destroy( gui_main_window_t *this_ )
339 : {
340 0 : U8_TRACE_BEGIN();
341 :
342 : /* Note: The widgets may be destroyed already. A cast by G_OBJECT is therefore illegal. */
343 0 : data_change_notifier_remove_listener( (*this_).data_notifier, (GObject*)(*this_).window );
344 0 : data_change_notifier_remove_listener( (*this_).data_notifier, (GObject*)(*this_).name_entry );
345 0 : data_change_notifier_remove_listener( (*this_).data_notifier, (GObject*)(*this_).sketcharea );
346 0 : data_change_notifier_remove_listener( (*this_).data_notifier, (GObject*)(*this_).search_entry );
347 :
348 0 : U8_TRACE_INFO("GTK+ Widgets are unregistered as listeners from data module.");
349 0 : gui_file_export_dialog_destroy ( &((*this_).file_export_dialog) );
350 0 : gui_file_use_db_dialog_destroy ( &((*this_).file_use_db_dialog) );
351 0 : U8_TRACE_INFO("GTK+ hidden windows are destroyed.");
352 0 : gui_file_action_destroy( &((*this_).file_action) );
353 :
354 0 : gui_search_request_destroy( &((*this_).search_request) );
355 0 : gui_search_runner_destroy( &((*this_).search_runner) );
356 0 : gui_sketch_area_destroy( &((*this_).sketcharea_data) );
357 0 : gui_toolbox_destroy( &((*this_).tools_data) );
358 0 : gui_marked_set_destroy( &((*this_).marker_data) );
359 0 : gui_attributes_editor_destroy( &((*this_).attributes_editor) );
360 0 : gui_simple_message_to_user_destroy( &((*this_).message_to_user) );
361 0 : (*this_).data_file = NULL;
362 0 : (*this_).resources = NULL;
363 :
364 0 : gui_button_destroy( &((*this_).file_new) );
365 0 : gui_button_destroy( &((*this_).file_open) );
366 0 : gui_button_destroy( &((*this_).file_save) );
367 0 : gui_button_destroy( &((*this_).file_export) );
368 0 : gui_button_destroy( &((*this_).view_new_window) );
369 0 : gui_button_destroy( &((*this_).view_navigate) );
370 0 : gui_button_destroy( &((*this_).view_edit) );
371 0 : gui_button_destroy( &((*this_).view_create) );
372 0 : gui_button_destroy( &((*this_).view_search) );
373 0 : gui_button_destroy( &((*this_).edit_undo) );
374 0 : gui_button_destroy( &((*this_).edit_redo) );
375 0 : gui_button_destroy( &((*this_).edit_cut) );
376 0 : gui_button_destroy( &((*this_).edit_copy) );
377 0 : gui_button_destroy( &((*this_).edit_paste) );
378 0 : gui_button_destroy( &((*this_).edit_delete) );
379 0 : gui_button_destroy( &((*this_).edit_instantiate) );
380 0 : gui_button_destroy( &((*this_).edit_highlight) );
381 0 : gui_button_destroy( &((*this_).edit_reset) );
382 0 : gui_button_destroy( &((*this_).help_about) );
383 :
384 0 : U8_TRACE_END();
385 0 : }
386 :
387 0 : static inline void gtk_button_set_image( GtkButton *btn, GtkWidget *icon )
388 : {
389 0 : gtk_button_set_child( btn, icon );
390 0 : }
391 :
392 0 : void gui_main_window_private_init_toolbox( gui_main_window_t *this_ )
393 : {
394 0 : U8_TRACE_BEGIN();
395 0 : const gui_resources_t *const res = (*this_).resources;
396 :
397 0 : gui_button_init( &((*this_).file_new),
398 0 : GDK_PAINTABLE( gui_resources_get_file_new( res ) ),
399 : "new",
400 : "New..."
401 : );
402 :
403 0 : gui_button_init( &((*this_).file_open),
404 0 : GDK_PAINTABLE( gui_resources_get_file_open( res ) ),
405 : "open",
406 : "Open..."
407 : );
408 :
409 : /* save icon depends if latest changes are already saved */
410 0 : const bool is_saved = io_data_file_is_in_sync( (*this_).data_file );
411 0 : gui_button_init( &((*this_).file_save),
412 : is_saved
413 0 : ? GDK_PAINTABLE( gui_resources_get_file_saved( res ) )
414 0 : : GDK_PAINTABLE( gui_resources_get_file_save( res ) ),
415 : "save",
416 : "Save (Ctrl-S)"
417 : );
418 0 : GtkShortcutTrigger *commit_trig = gtk_shortcut_trigger_parse_string( "<Control>S" );
419 0 : GtkShortcutAction *commit_act = gtk_callback_action_new( &gui_main_window_save_shortcut_callback,
420 : this_,
421 : NULL
422 : );
423 0 : GtkShortcut* ctrl_s = gtk_shortcut_new_with_arguments( commit_trig,
424 : commit_act,
425 : NULL /* = format_string */
426 : );
427 0 : gtk_shortcut_controller_add_shortcut( (*this_).keyboard_shortcut_ctrl, ctrl_s );
428 :
429 0 : gui_button_init( &((*this_).file_export),
430 0 : GDK_PAINTABLE( gui_resources_get_file_export( res ) ),
431 : "export",
432 : "Export..."
433 : );
434 :
435 0 : (*this_).tool_sect_1_icon = gtk_image_new_from_paintable( GDK_PAINTABLE ( gui_resources_get_tool_sect( res ) ) );
436 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).tool_sect_1_icon), 32 /*=max(w,h)*/ );
437 0 : gtk_widget_set_halign( (*this_).tool_sect_1_icon, GTK_ALIGN_START );
438 :
439 0 : gui_button_init( &((*this_).view_new_window),
440 0 : GDK_PAINTABLE( gui_resources_get_view_new_window( res ) ),
441 : "win",
442 : "New Window"
443 : );
444 :
445 0 : gui_button_init_toggle( &((*this_).view_navigate),
446 0 : GDK_PAINTABLE( gui_resources_get_view_navigate( res ) ),
447 : "nav",
448 : "Navigate"
449 : );
450 :
451 0 : gui_button_init_toggle( &((*this_).view_edit),
452 0 : GDK_PAINTABLE( gui_resources_get_view_edit( res ) ),
453 : "edit",
454 : "Edit"
455 : );
456 0 : gtk_toggle_button_set_group( GTK_TOGGLE_BUTTON( gui_button_get_widget_ptr( &((*this_).view_edit) ) ),
457 0 : GTK_TOGGLE_BUTTON( gui_button_get_widget_ptr( &((*this_).view_navigate) ) )
458 : );
459 :
460 0 : gui_button_init_toggle( &((*this_).view_create),
461 0 : GDK_PAINTABLE( gui_resources_get_view_create( res ) ),
462 : "create",
463 : "Create"
464 : );
465 0 : gtk_toggle_button_set_group( GTK_TOGGLE_BUTTON( gui_button_get_widget_ptr( &((*this_).view_create) ) ),
466 0 : GTK_TOGGLE_BUTTON( gui_button_get_widget_ptr( &((*this_).view_edit) ) )
467 : );
468 :
469 0 : gui_button_init_toggle( &((*this_).view_search),
470 0 : GDK_PAINTABLE( gui_resources_get_view_search( res ) ),
471 : "search",
472 : "Search"
473 : );
474 0 : gtk_toggle_button_set_group( GTK_TOGGLE_BUTTON( gui_button_get_widget_ptr( &((*this_).view_search) ) ),
475 0 : GTK_TOGGLE_BUTTON( gui_button_get_widget_ptr( &((*this_).view_create) ) )
476 : );
477 :
478 0 : (*this_).tool_sect_2_icon = gtk_image_new_from_paintable( GDK_PAINTABLE ( gui_resources_get_tool_sect( res ) ) );
479 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).tool_sect_2_icon), 32 /*=max(w,h)*/ );
480 0 : gtk_widget_set_halign( (*this_).tool_sect_2_icon, GTK_ALIGN_START );
481 :
482 0 : gui_button_init( &((*this_).edit_undo),
483 0 : GDK_PAINTABLE( gui_resources_get_edit_undo( res ) ),
484 : "undo",
485 : "Undo (Ctrl-Z)"
486 : );
487 0 : GtkShortcutTrigger *undo_trig = gtk_shortcut_trigger_parse_string( "<Control>Z" );
488 0 : GtkShortcutAction *undo_act = gtk_callback_action_new( &gui_toolbox_undo_shortcut_callback,
489 0 : &((*this_).tools_data),
490 : NULL
491 : );
492 0 : GtkShortcut* ctrl_z = gtk_shortcut_new_with_arguments( undo_trig,
493 : undo_act,
494 : NULL /* = format_string */
495 : );
496 0 : gtk_shortcut_controller_add_shortcut( (*this_).keyboard_shortcut_ctrl, ctrl_z );
497 :
498 0 : gui_button_init( &((*this_).edit_redo),
499 0 : GDK_PAINTABLE( gui_resources_get_edit_redo( res ) ),
500 : "redo",
501 : "Redo (Ctrl-Y)"
502 : );
503 0 : GtkShortcutTrigger *redo_trig = gtk_shortcut_trigger_parse_string( "<Control>Y" );
504 0 : GtkShortcutAction *redo_act = gtk_callback_action_new( &gui_toolbox_redo_shortcut_callback,
505 0 : &((*this_).tools_data),
506 : NULL
507 : );
508 0 : GtkShortcut* ctrl_y = gtk_shortcut_new_with_arguments( redo_trig,
509 : redo_act,
510 : NULL /* = format_string */
511 : );
512 0 : gtk_shortcut_controller_add_shortcut( (*this_).keyboard_shortcut_ctrl, ctrl_y );
513 :
514 0 : gui_button_init( &((*this_).edit_cut),
515 0 : GDK_PAINTABLE( gui_resources_get_edit_cut( res ) ),
516 : "cut",
517 : "Cut (Ctrl-X)"
518 : );
519 : /*
520 : GDK_KEY_x + GDK_CONTROL_MASK handling see gui_sketch_area_key_press_callback();
521 : moved there to not interfere with text input fields
522 : */
523 :
524 0 : gui_button_init( &((*this_).edit_copy),
525 0 : GDK_PAINTABLE( gui_resources_get_edit_copy( res ) ),
526 : "copy",
527 : "Copy (Ctrl-C)"
528 : );
529 : /*
530 : GDK_KEY_c + GDK_CONTROL_MASK handling see gui_sketch_area_key_press_callback();
531 : moved there to not interfere with text input fields
532 : */
533 :
534 0 : gui_button_init( &((*this_).edit_paste),
535 0 : GDK_PAINTABLE( gui_resources_get_edit_paste( res ) ),
536 : "paste",
537 : "Paste (Ctrl-V)"
538 : );
539 : /*
540 : GK_KEY_v + GDK_CONTROL_MASK handling see gui_sketch_area_key_press_callback();
541 : moved there to not interfere with text input fields
542 : */
543 :
544 0 : gui_button_init( &((*this_).edit_delete),
545 0 : GDK_PAINTABLE( gui_resources_get_edit_delete( res ) ),
546 : "del",
547 : "Delete (DEL)"
548 : );
549 : /*
550 : GDK_KEY_Delete handling see gui_sketch_area_key_press_callback();
551 : moved there to not interfere with text input fields
552 : */
553 :
554 0 : gui_button_init( &((*this_).edit_instantiate),
555 0 : GDK_PAINTABLE( gui_resources_get_edit_instantiate( res ) ),
556 : "inst",
557 : "Instantiate"
558 : );
559 :
560 0 : gui_button_init( &((*this_).edit_highlight),
561 0 : GDK_PAINTABLE( gui_resources_get_edit_highlight( res ) ),
562 : "mark",
563 : "Mark"
564 : );
565 :
566 0 : gui_button_init( &((*this_).edit_reset),
567 0 : GDK_PAINTABLE( gui_resources_get_edit_reset( res ) ),
568 : "zero",
569 : "Reset Selection"
570 : );
571 :
572 0 : gui_button_init( &((*this_).help_about),
573 0 : GDK_PAINTABLE( gui_resources_get_crystal_facet_uml( res ) ),
574 : "info",
575 : "About"
576 : );
577 :
578 : /* insert widgets to box container */
579 : {
580 0 : (*this_).tool_row = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, /*spacing:*/ 4 );
581 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).file_new) ) );
582 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).file_open) ) );
583 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).file_save) ) );
584 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).file_export) ) );
585 0 : gtk_box_append( GTK_BOX((*this_).tool_row), GTK_WIDGET((*this_).tool_sect_1_icon) );
586 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).view_new_window) ) );
587 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).view_search) ) );
588 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).view_navigate) ) );
589 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).view_edit) ) );
590 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).view_create) ) );
591 0 : gtk_box_append( GTK_BOX((*this_).tool_row), GTK_WIDGET((*this_).tool_sect_2_icon) );
592 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_undo) ) );
593 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_redo) ) );
594 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_cut) ) );
595 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_copy) ) );
596 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_paste) ) );
597 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_delete) ) );
598 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_instantiate) ) );
599 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_highlight) ) );
600 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).edit_reset) ) );
601 0 : gtk_box_append( GTK_BOX((*this_).tool_row), gui_button_get_widget_ptr( &((*this_).help_about) ) );
602 : }
603 :
604 0 : U8_TRACE_END();
605 0 : }
606 :
607 0 : void gui_main_window_private_init_attributes_editor( gui_main_window_t *this_ )
608 : {
609 0 : U8_TRACE_BEGIN();
610 0 : const gui_resources_t *const res = (*this_).resources;
611 :
612 0 : (*this_).attr_section_icon = gtk_image_new_from_paintable( GDK_PAINTABLE ( gui_resources_get_edit_attributes_sect( res ) ) );
613 0 : gtk_widget_set_margin_start( GTK_WIDGET((*this_).attr_section_icon), 8 );
614 0 : gtk_widget_set_margin_end( GTK_WIDGET((*this_).attr_section_icon), 8 );
615 0 : gtk_widget_set_margin_top( GTK_WIDGET((*this_).attr_section_icon), 0 );
616 0 : gtk_widget_set_margin_bottom( GTK_WIDGET((*this_).attr_section_icon), 0 );
617 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).attr_section_icon), 32 /*=max(w,h)*/ );
618 0 : gtk_widget_set_halign( (*this_).attr_section_icon, GTK_ALIGN_START );
619 :
620 0 : (*this_).id_label = gtk_label_new( "" );
621 0 : gtk_label_set_selectable( GTK_LABEL( (*this_).id_label ), true );
622 0 : (*this_).name_label = gtk_label_new( "Name:" );
623 0 : (*this_).description_label = gtk_label_new( "Description:" );
624 0 : (*this_).stereotype_label = gtk_label_new( "Stereotype:" );
625 0 : (*this_).type_label = gtk_label_new( "Type:" );
626 0 : gtk_label_set_xalign( GTK_LABEL( (*this_).id_label ), 1.0 ); /* align right */
627 0 : gtk_label_set_xalign( GTK_LABEL( (*this_).name_label ), 0.0 );
628 0 : gtk_label_set_xalign( GTK_LABEL( (*this_).description_label ), 0.0 );
629 0 : gtk_label_set_xalign( GTK_LABEL( (*this_).stereotype_label ), 0.0 );
630 0 : gtk_label_set_xalign( GTK_LABEL( (*this_).type_label ), 0.0 );
631 :
632 0 : (*this_).id_search_btn = gtk_button_new();
633 0 : (*this_).id_search_btn_icon = gtk_image_new_from_paintable( GDK_PAINTABLE( gui_resources_get_view_search_now( res ) ) );
634 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).id_search_btn_icon), 32 /*=max(w,h)*/ );
635 0 : gtk_button_set_image( GTK_BUTTON((*this_).id_search_btn), (*this_).id_search_btn_icon );
636 0 : gtk_widget_set_tooltip_text( GTK_WIDGET((*this_).id_search_btn), "Find Occurrences" );
637 0 : gtk_widget_set_size_request( GTK_WIDGET((*this_).id_search_btn_icon), 32 /*=w*/ , 32 /*=h*/ );
638 :
639 : /* insert widgets to box container */
640 : {
641 0 : (*this_).id_row = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, /*spacing:*/ 8 );
642 0 : gtk_box_append( GTK_BOX((*this_).id_row), GTK_WIDGET((*this_).attr_section_icon) );
643 0 : gtk_box_append( GTK_BOX((*this_).id_row), GTK_WIDGET((*this_).id_label) );
644 0 : gtk_box_append( GTK_BOX((*this_).id_row), GTK_WIDGET((*this_).id_search_btn) );
645 :
646 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).attr_section_icon ), false );
647 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).attr_section_icon ), false );
648 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).id_label ), false );
649 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).id_label ), true );
650 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).id_search_btn ), false );
651 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).id_search_btn ), false );
652 : }
653 :
654 0 : (*this_).name_entry = gtk_entry_new();
655 :
656 0 : (*this_).stereotype_entry = gtk_entry_new();
657 :
658 0 : (*this_).description_text_view = gtk_text_view_new();
659 0 : gtk_text_view_set_wrap_mode( GTK_TEXT_VIEW( (*this_).description_text_view ),
660 : GTK_WRAP_WORD_CHAR
661 : );
662 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).description_text_view ), true );
663 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).description_text_view ), false );
664 : //gtk_widget_set_size_request( GTK_WIDGET((*this_).description_text_view), 128 /*=w*/ , 48 /*=h*/ );
665 : /* need own scroll window container */
666 0 : gtk_text_view_set_bottom_margin( GTK_TEXT_VIEW( (*this_).description_text_view ), 12 );
667 0 : gtk_text_view_set_left_margin( GTK_TEXT_VIEW( (*this_).description_text_view ), 12 );
668 0 : gtk_text_view_set_right_margin( GTK_TEXT_VIEW( (*this_).description_text_view ), 12 );
669 0 : gtk_text_view_set_top_margin( GTK_TEXT_VIEW( (*this_).description_text_view ), 12 );
670 0 : (*this_).description_scroll_win = gtk_scrolled_window_new();
671 0 : gtk_scrolled_window_set_child( GTK_SCROLLED_WINDOW((*this_).description_scroll_win), (*this_).description_text_view );
672 0 : gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( (*this_).description_scroll_win ),
673 : GTK_POLICY_AUTOMATIC,
674 : GTK_POLICY_AUTOMATIC
675 : );
676 :
677 0 : GListStore *model = NULL; /* the model is set later */
678 0 : GtkExpression *expression = NULL; /* not needed: there is no search entry that could find list elements */
679 0 : (*this_).type_dropdown = GTK_DROP_DOWN( gtk_drop_down_new ( G_LIST_MODEL( model ), expression ) );
680 0 : GtkBuilderScope *scope = gtk_builder_cscope_new();
681 : static const char bytes[] =
682 : "<interface>\n"
683 : "<template class=\"GtkListItem\">\n"
684 : " <property name=\"child\">\n"
685 : " <object class=\"GtkPaned\">\n"
686 : " <property name=\"start-child\">\n"
687 : " <object class=\"GtkImage\">\n"
688 : " <property name=\"pixel-size\">32</property>\n"
689 : " <binding name=\"paintable\">\n"
690 : " <lookup name=\"icon\" type=\"GuiTypeResource\">\n"
691 : " <lookup name=\"item\">GtkListItem</lookup>\n"
692 : " </lookup>\n"
693 : " </binding>\n"
694 : " </object>\n"
695 : " </property>\n"
696 : " <property name=\"end-child\">\n"
697 : " <object class=\"GtkLabel\">\n"
698 : " <property name=\"xalign\">0</property>\n"
699 : " <binding name=\"label\">\n"
700 : " <lookup name=\"name\" type=\"GuiTypeResource\">\n"
701 : " <lookup name=\"item\">GtkListItem</lookup>\n"
702 : " </lookup>\n"
703 : " </binding>\n"
704 : " </object>\n"
705 : " </property>\n"
706 : " <property name=\"position\">48</property>\n"
707 : " </object>\n"
708 : " </property>\n"
709 : "</template>\n"
710 : "</interface>\n";
711 0 : GBytes *byteptr = g_bytes_new( &bytes, sizeof(bytes)-sizeof(char) );
712 0 : GtkListItemFactory *factory = gtk_builder_list_item_factory_new_from_bytes( scope, byteptr );
713 0 : gtk_drop_down_set_factory( (*this_).type_dropdown, factory );
714 0 : g_object_unref( scope );
715 0 : g_object_unref( factory );
716 0 : g_bytes_unref( byteptr );
717 :
718 : static gui_type_resource_list_t res_select;
719 0 : gui_type_resource_list_init( &res_select, res );
720 : {
721 0 : (*this_).type_diag_grid = GTK_GRID( gtk_grid_new() );
722 0 : gtk_widget_set_halign( GTK_WIDGET( (*this_).type_diag_grid ), GTK_ALIGN_END );
723 : const gui_type_resource_t (*diag_data)[];
724 : unsigned int diag_data_length;
725 0 : gui_type_resource_list_get_all_diagram_types( &res_select, &diag_data, &diag_data_length );
726 0 : assert( diag_data_length == GUI_TYPE_RESOURCE_LIST_DIAGRAMS );
727 0 : for( int_fast32_t diag_idx = 0; diag_idx < GUI_TYPE_RESOURCE_LIST_DIAGRAMS; diag_idx ++ )
728 : {
729 0 : const gui_type_resource_t *const type_data = &((*diag_data)[diag_idx]);
730 0 : const data_diagram_type_t diag_type = data_type_get_diagram_type( gui_type_resource_get_type_id( type_data ) );
731 0 : GdkTexture *const diag_icon = gui_type_resource_get_icon( type_data );
732 0 : const char *const diag_name = gui_type_resource_get_name( type_data );
733 :
734 0 : gui_attribute_type_of_diagram_init( &((*this_).type_diag_data[diag_idx]), diag_type, &((*this_).attributes_editor) );
735 0 : (*this_).type_diag_img[ diag_idx ] = GTK_IMAGE( gtk_image_new_from_paintable( GDK_PAINTABLE( diag_icon ) ) );
736 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).type_diag_img[ diag_idx ]), 32 /*=max(w,h)*/ );
737 0 : (*this_).type_diag_btn[ diag_idx ] = GTK_BUTTON( gtk_button_new() );
738 0 : gtk_button_set_image( (*this_).type_diag_btn[ diag_idx ], GTK_WIDGET( (*this_).type_diag_img[ diag_idx ] ) );
739 0 : gtk_widget_set_tooltip_text( GTK_WIDGET( (*this_).type_diag_btn[ diag_idx ] ), diag_name );
740 0 : gtk_grid_attach( (*this_).type_diag_grid, GTK_WIDGET( (*this_).type_diag_btn[ diag_idx ] ), diag_idx%7, diag_idx/7, 1, 1 );
741 : }
742 :
743 0 : (*this_).type_clas_grid = GTK_GRID( gtk_grid_new() );
744 0 : gtk_widget_set_halign( GTK_WIDGET( (*this_).type_clas_grid ), GTK_ALIGN_END );
745 : const gui_type_resource_t (*clas_data)[];
746 : unsigned int clas_data_length;
747 0 : gui_type_resource_list_get_all_classifier_types( &res_select, &clas_data, &clas_data_length );
748 0 : assert( clas_data_length == GUI_TYPE_RESOURCE_LIST_CLASSIFIERS );
749 0 : for( int_fast32_t clas_idx = 0; clas_idx < GUI_TYPE_RESOURCE_LIST_CLASSIFIERS; clas_idx ++ )
750 : {
751 0 : const gui_type_resource_t *const type_data = &((*clas_data)[clas_idx]);
752 0 : const data_classifier_type_t clas_type = data_type_get_classifier_type( gui_type_resource_get_type_id( type_data ) );
753 0 : GdkTexture *const clas_icon = gui_type_resource_get_icon( type_data );
754 0 : const char *const clas_name = gui_type_resource_get_name( type_data );
755 :
756 0 : gui_attribute_type_of_classifier_init( &((*this_).type_clas_data[clas_idx]), clas_type, &((*this_).attributes_editor) );
757 0 : (*this_).type_clas_img[ clas_idx ] = GTK_IMAGE( gtk_image_new_from_paintable( GDK_PAINTABLE( clas_icon ) ) );
758 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).type_clas_img[ clas_idx ]), 32 /*=max(w,h)*/ );
759 0 : (*this_).type_clas_btn[ clas_idx ] = GTK_BUTTON( gtk_button_new() );
760 0 : gtk_button_set_image( (*this_).type_clas_btn[ clas_idx ], GTK_WIDGET( (*this_).type_clas_img[ clas_idx ] ) );
761 0 : gtk_widget_set_tooltip_text( GTK_WIDGET( (*this_).type_clas_btn[ clas_idx ] ), clas_name );
762 0 : gtk_grid_attach( (*this_).type_clas_grid, GTK_WIDGET( (*this_).type_clas_btn[ clas_idx ] ), clas_idx%7, clas_idx/7, 1, 1 );
763 : }
764 :
765 0 : (*this_).type_feat_grid = GTK_GRID( gtk_grid_new() );
766 0 : gtk_widget_set_halign( GTK_WIDGET( (*this_).type_feat_grid ), GTK_ALIGN_END );
767 : const gui_type_resource_t (*feat_data)[];
768 : unsigned int feat_data_length;
769 0 : gui_type_resource_list_get_invariant_feature_types( &res_select, &feat_data, &feat_data_length );
770 0 : assert( feat_data_length == GUI_TYPE_RESOURCE_LIST_INV_FEATURES );
771 0 : for( int_fast32_t feat_idx = 0; feat_idx < GUI_TYPE_RESOURCE_LIST_INV_FEATURES; feat_idx ++ ) /* ignore the lifeline */
772 : {
773 0 : const gui_type_resource_t *const type_data = &((*feat_data)[feat_idx]);
774 0 : const data_feature_type_t feat_type = data_type_get_feature_type( gui_type_resource_get_type_id( type_data ) );
775 0 : GdkTexture *const feat_icon = gui_type_resource_get_icon( type_data );
776 0 : const char *const feat_name = gui_type_resource_get_name( type_data );
777 :
778 0 : gui_attribute_type_of_feature_init( &((*this_).type_feat_data[feat_idx]), feat_type, &((*this_).attributes_editor) );
779 0 : (*this_).type_feat_img[ feat_idx ] = GTK_IMAGE( gtk_image_new_from_paintable( GDK_PAINTABLE( feat_icon ) ) );
780 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).type_feat_img[ feat_idx ]), 32 /*=max(w,h)*/ );
781 0 : (*this_).type_feat_btn[ feat_idx ] = GTK_BUTTON( gtk_button_new() );
782 0 : gtk_button_set_image( (*this_).type_feat_btn[ feat_idx ], GTK_WIDGET( (*this_).type_feat_img[ feat_idx ] ) );
783 0 : gtk_widget_set_tooltip_text( GTK_WIDGET( (*this_).type_feat_btn[ feat_idx ] ), feat_name );
784 0 : gtk_grid_attach( (*this_).type_feat_grid, GTK_WIDGET( (*this_).type_feat_btn[ feat_idx ] ), feat_idx%7, feat_idx/7, 1, 1 );
785 : }
786 :
787 0 : (*this_).type_rel_grid = GTK_GRID( gtk_grid_new() );
788 0 : gtk_widget_set_halign( GTK_WIDGET( (*this_).type_rel_grid ), GTK_ALIGN_END );
789 : const gui_type_resource_t (*rel_data)[];
790 : unsigned int rel_data_length;
791 0 : gui_type_resource_list_get_all_relationship_types( &res_select, &rel_data, &rel_data_length );
792 0 : assert( rel_data_length == GUI_TYPE_RESOURCE_LIST_RELATIONS );
793 0 : for( int_fast32_t rel_idx = 0; rel_idx < GUI_TYPE_RESOURCE_LIST_RELATIONS; rel_idx ++ )
794 : {
795 0 : const gui_type_resource_t *const type_data = &((*rel_data)[rel_idx]);
796 0 : const data_relationship_type_t rel_type = data_type_get_relationship_type( gui_type_resource_get_type_id( type_data ) );
797 0 : GdkTexture *const rel_icon = gui_type_resource_get_icon( type_data );
798 0 : const char *const rel_name = gui_type_resource_get_name( type_data );
799 :
800 0 : gui_attribute_type_of_relationship_init( &((*this_).type_rel_data[rel_idx]), rel_type, &((*this_).attributes_editor) );
801 0 : (*this_).type_rel_img[ rel_idx ] = GTK_IMAGE( gtk_image_new_from_paintable( GDK_PAINTABLE( rel_icon ) ) );
802 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).type_rel_img[ rel_idx ]), 32 /*=max(w,h)*/ );
803 0 : (*this_).type_rel_btn[ rel_idx ] = GTK_BUTTON( gtk_button_new() );
804 0 : gtk_button_set_image( (*this_).type_rel_btn[ rel_idx ], GTK_WIDGET( (*this_).type_rel_img[ rel_idx ] ) );
805 0 : gtk_widget_set_tooltip_text( GTK_WIDGET( (*this_).type_rel_btn[ rel_idx ] ), rel_name );
806 0 : gtk_grid_attach( (*this_).type_rel_grid, GTK_WIDGET( (*this_).type_rel_btn[ rel_idx ] ), rel_idx%7, rel_idx/7, 1, 1 );
807 : }
808 : }
809 0 : gui_type_resource_list_destroy( &res_select );
810 :
811 : /* insert widgets to box container */
812 : {
813 0 : (*this_).attr_edit_column = gtk_box_new( GTK_ORIENTATION_VERTICAL, /*spacing:*/ 4 );
814 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).id_row) );
815 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).name_label) );
816 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).name_entry) );
817 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).stereotype_label) );
818 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).stereotype_entry) );
819 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).type_label) );
820 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).type_dropdown) );
821 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).type_diag_grid) );
822 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).type_clas_grid) );
823 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).type_feat_grid) );
824 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).type_rel_grid) );
825 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).description_label) );
826 0 : gtk_box_append( GTK_BOX((*this_).attr_edit_column), GTK_WIDGET((*this_).description_scroll_win) );
827 :
828 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).description_scroll_win ), true );
829 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).description_scroll_win ), true );
830 : }
831 :
832 0 : U8_TRACE_END();
833 0 : }
834 :
835 0 : void gui_main_window_private_init_simple_message_to_user( gui_main_window_t *this_ )
836 : {
837 0 : U8_TRACE_BEGIN();
838 0 : const gui_resources_t *const res = (*this_).resources;
839 :
840 0 : (*this_).message_text_label = gtk_label_new( "" );
841 0 : gtk_label_set_xalign(GTK_LABEL( (*this_).message_text_label ), 0.0 );
842 :
843 0 : (*this_).message_icon_image = gtk_image_new_from_paintable( GDK_PAINTABLE ( gui_resources_get_crystal_facet_uml( res ) ) );
844 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).message_icon_image), 32 /*=max(w,h)*/ );
845 0 : gtk_widget_set_margin_start( GTK_WIDGET((*this_).message_icon_image), 8 );
846 0 : gtk_widget_set_margin_end( GTK_WIDGET((*this_).message_icon_image), 8 );
847 0 : gtk_widget_set_margin_top( GTK_WIDGET((*this_).message_icon_image), 0 );
848 0 : gtk_widget_set_margin_bottom( GTK_WIDGET((*this_).message_icon_image), 0 );
849 :
850 : /* insert widgets to box container */
851 : {
852 0 : (*this_).message_row = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, /*spacing:*/ 20 );
853 0 : gtk_box_append( GTK_BOX((*this_).message_row), GTK_WIDGET((*this_).message_icon_image) );
854 0 : gtk_box_append( GTK_BOX((*this_).message_row), GTK_WIDGET((*this_).message_text_label) );
855 :
856 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).message_icon_image ), false );
857 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).message_icon_image ), false );
858 0 : gtk_widget_set_vexpand( GTK_WIDGET( (*this_).message_text_label ), false );
859 0 : gtk_widget_set_hexpand( GTK_WIDGET( (*this_).message_text_label ), true );
860 : }
861 :
862 0 : U8_TRACE_END();
863 0 : }
864 :
865 0 : void gui_main_window_private_init_search_and_sketch_area( gui_main_window_t *this_ )
866 : {
867 0 : U8_TRACE_BEGIN();
868 0 : const gui_resources_t *const res = (*this_).resources;
869 :
870 : /* init search widgets */
871 : {
872 0 : (*this_).search_label = gtk_label_new ( "Search:" );
873 0 : gtk_label_set_xalign( GTK_LABEL( (*this_).search_label ), 1.0 ); /* align right */
874 0 : gtk_label_set_yalign( GTK_LABEL( (*this_).search_label ), 0.5 ); /* align middle-height */
875 :
876 0 : (*this_).search_entry = gtk_entry_new();
877 :
878 0 : (*this_).search_button = gtk_button_new();
879 0 : (*this_).search_btn_icon = gtk_image_new_from_paintable( GDK_PAINTABLE( gui_resources_get_view_search_now( res ) ) );
880 0 : gtk_image_set_pixel_size( GTK_IMAGE((*this_).search_btn_icon), 32 /*=max(w,h)*/ );
881 0 : gtk_button_set_image( GTK_BUTTON((*this_).search_button), (*this_).search_btn_icon );
882 0 : gtk_widget_set_tooltip_text( GTK_WIDGET((*this_).search_button), "Search" );
883 : }
884 :
885 : /* insert widgets to box container */
886 : {
887 0 : (*this_).search_row = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, /*spacing:*/ 4 );
888 0 : gtk_box_append( GTK_BOX((*this_).search_row), GTK_WIDGET((*this_).search_label) );
889 0 : gtk_box_append( GTK_BOX((*this_).search_row), GTK_WIDGET((*this_).search_entry) );
890 0 : gtk_box_append( GTK_BOX((*this_).search_row), GTK_WIDGET((*this_).search_button) );
891 :
892 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).search_label ), false );
893 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).search_label ), false );
894 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).search_entry ), false );
895 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).search_entry ), true );
896 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).search_button ), false );
897 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).search_button ), false );
898 : }
899 :
900 : /* init sketch area */
901 : {
902 0 : (*this_).sketcharea = gtk_drawing_area_new();
903 0 : gtk_widget_set_can_focus( GTK_WIDGET( (*this_).sketcharea ), TRUE ); /* this allows the text entry widgets to lose the focus */
904 0 : gtk_widget_set_size_request( GTK_WIDGET( (*this_).sketcharea ), 600, 360); /* set a minimum initial size */
905 : /*gtk_widget_set_focus_on_click( GTK_WIDGET( (*this_).sketcharea ), TRUE ); not yet existing: since GTK 3.2 */
906 : }
907 :
908 : /* insert widgets to box container */
909 : {
910 0 : (*this_).sketch_stack_column = gtk_box_new( GTK_ORIENTATION_VERTICAL, /*spacing:*/ 0 );
911 0 : gtk_box_append( GTK_BOX((*this_).sketch_stack_column), GTK_WIDGET((*this_).search_row) );
912 0 : gtk_box_append( GTK_BOX((*this_).sketch_stack_column), GTK_WIDGET((*this_).sketcharea) );
913 :
914 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).search_row ), false );
915 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).search_row ), true );
916 0 : gtk_widget_set_vexpand ( GTK_WIDGET( (*this_).sketcharea ), true );
917 0 : gtk_widget_set_hexpand ( GTK_WIDGET( (*this_).sketcharea ), true );
918 : }
919 :
920 0 : U8_TRACE_END();
921 0 : }
922 :
923 0 : void gui_main_window_destroy_event_callback( GtkWidget *widget, gpointer data )
924 : {
925 0 : U8_TRACE_BEGIN();
926 0 : gui_main_window_t *this_ = data;
927 0 : assert( (*this_).window == (void*)widget );
928 :
929 : /* forward destroy request to gui_window_manager: */
930 0 : observer_notify( (*this_).window_close_observer, this_ );
931 :
932 0 : U8_TRACE_TIMESTAMP();
933 0 : U8_TRACE_END();
934 0 : }
935 :
936 0 : gboolean gui_main_window_delete_event_callback( GtkWindow *widget, gpointer data )
937 : {
938 0 : U8_TRACE_BEGIN();
939 0 : gui_main_window_t *this_ = data;
940 0 : assert( (*this_).window == (void*)widget );
941 : (void) this_; /* unused in case of NDEBUG */
942 :
943 0 : U8_TRACE_TIMESTAMP();
944 0 : U8_TRACE_END();
945 0 : return false; /* return false to trigger destroy event */
946 : }
947 :
948 0 : void gui_main_window_new_db_btn_callback( GtkWidget* button, gpointer data )
949 : {
950 0 : U8_TRACE_BEGIN();
951 0 : gui_main_window_t *this_ = data;
952 :
953 0 : gui_simple_message_to_user_hide( &((*this_).message_to_user) );
954 :
955 0 : gui_file_use_db_dialog_show( &((*this_).file_use_db_dialog), false /* open_existing*/ );
956 :
957 0 : U8_TRACE_TIMESTAMP();
958 0 : U8_TRACE_END();
959 0 : }
960 :
961 0 : void gui_main_window_open_db_btn_callback( GtkWidget* button, gpointer data )
962 : {
963 0 : U8_TRACE_BEGIN();
964 0 : gui_main_window_t *this_ = data;
965 :
966 0 : gui_simple_message_to_user_hide( &((*this_).message_to_user) );
967 :
968 0 : gui_file_use_db_dialog_show( &((*this_).file_use_db_dialog), true /* open_existing*/ );
969 :
970 0 : U8_TRACE_TIMESTAMP();
971 0 : U8_TRACE_END();
972 0 : }
973 :
974 0 : void gui_main_window_save_btn_callback( GtkButton *button, gpointer user_data )
975 : {
976 0 : U8_TRACE_BEGIN();
977 : gui_main_window_t *this_;
978 0 : this_ = (gui_main_window_t*) user_data;
979 0 : assert( this_ != NULL );
980 :
981 0 : gui_simple_message_to_user_hide( &((*this_).message_to_user) );
982 :
983 0 : gui_attributes_editor_commit_changes ( &((*this_).attributes_editor) );
984 :
985 0 : const u8_error_t err = gui_file_action_save( &((*this_).file_action) );
986 0 : if ( err == U8_ERROR_NONE )
987 : {
988 0 : GtkImage *const save_image = gui_button_get_icon_ptr( &((*this_).file_save) );
989 0 : const GdkPaintable *const shown_icon = gtk_image_get_paintable( save_image );
990 0 : GdkPaintable *const saved_icon = GDK_PAINTABLE( gui_resources_get_file_saved( (*this_).resources ) );
991 0 : if ( shown_icon != saved_icon )
992 : {
993 0 : gtk_image_set_from_paintable( save_image, saved_icon );
994 0 : U8_TRACE_INFO("icon of save button set to SAVED(/)");
995 : }
996 : }
997 :
998 0 : U8_TRACE_TIMESTAMP();
999 0 : U8_TRACE_END();
1000 0 : }
1001 :
1002 0 : gboolean gui_main_window_save_shortcut_callback( GtkWidget* widget, GVariant* args, gpointer user_data )
1003 : {
1004 0 : gui_main_window_save_btn_callback( (GtkButton*)widget, user_data );
1005 0 : return TRUE;
1006 : }
1007 :
1008 0 : void gui_main_window_export_btn_callback( GtkWidget* button, gpointer data )
1009 : {
1010 0 : U8_TRACE_BEGIN();
1011 0 : gui_main_window_t *this_ = data;
1012 :
1013 0 : gui_simple_message_to_user_hide( &((*this_).message_to_user) );
1014 :
1015 0 : const bool was_visible = gtk_widget_get_visible( (*this_).options_layout );
1016 0 : gtk_widget_set_visible( (*this_).options_layout, ! was_visible );
1017 :
1018 0 : U8_TRACE_TIMESTAMP();
1019 0 : U8_TRACE_END();
1020 0 : }
1021 :
1022 0 : void gui_main_window_new_window_btn_callback( GtkWidget* button, gpointer data )
1023 : {
1024 0 : U8_TRACE_BEGIN();
1025 0 : gui_main_window_t *this_ = data;
1026 :
1027 : /* hide last message */
1028 0 : gui_simple_message_to_user_hide( &((*this_).message_to_user) );
1029 :
1030 : /* forward new window request to gui_window_manager: */
1031 0 : observer_notify( (*this_).window_open_observer, &((*this_).message_to_user) );
1032 :
1033 0 : U8_TRACE_TIMESTAMP();
1034 0 : U8_TRACE_END();
1035 0 : }
1036 :
1037 0 : void gui_main_window_about_btn_callback( GtkWidget* button, gpointer data )
1038 : {
1039 0 : U8_TRACE_BEGIN();
1040 0 : gui_main_window_t *this_ = data;
1041 :
1042 0 : if ( GUI_SIMPLE_MESSAGE_TYPE_ABOUT == gui_simple_message_to_user_get_type_id( &((*this_).message_to_user) ) )
1043 : {
1044 : /* hide the already visible about message */
1045 0 : gui_simple_message_to_user_hide( &((*this_).message_to_user) );
1046 : }
1047 : else
1048 : {
1049 0 : gui_simple_message_to_user_show_message( &((*this_).message_to_user),
1050 : GUI_SIMPLE_MESSAGE_TYPE_ABOUT,
1051 : GUI_SIMPLE_MESSAGE_CONTENT_ABOUT
1052 : );
1053 : }
1054 :
1055 0 : U8_TRACE_TIMESTAMP();
1056 0 : U8_TRACE_END();
1057 0 : }
1058 :
1059 0 : void gui_main_window_data_changed_callback( GtkWidget *window, data_change_message_t *msg, gpointer user_data )
1060 : {
1061 0 : U8_TRACE_BEGIN();
1062 0 : gui_main_window_t *this_ = user_data;
1063 :
1064 : /* update the window title */
1065 0 : if (( DATA_CHANGE_EVENT_TYPE_DB_OPENED == data_change_message_get_event( msg ) )
1066 0 : || ( DATA_CHANGE_EVENT_TYPE_DB_CLOSED == data_change_message_get_event( msg ) ))
1067 : {
1068 : /* the database has changed */
1069 0 : const char *filename = io_data_file_get_filename_const( (*this_).data_file );
1070 0 : if ( NULL == filename )
1071 : {
1072 0 : gtk_window_set_title(GTK_WINDOW((*this_).window), META_INFO_PROGRAM_NAME_STR );
1073 : }
1074 : else
1075 : {
1076 0 : gtk_window_set_title(GTK_WINDOW((*this_).window), filename );
1077 : }
1078 : }
1079 :
1080 : /* update the save button status indicator */
1081 0 : GtkImage *const save_image = gui_button_get_icon_ptr( &((*this_).file_save) );
1082 0 : const GdkPaintable *const shown_icon = gtk_image_get_paintable( save_image );
1083 :
1084 0 : if (( DATA_CHANGE_EVENT_TYPE_DB_OPENED == data_change_message_get_event( msg ) )
1085 0 : || ( DATA_CHANGE_EVENT_TYPE_DB_CLOSED == data_change_message_get_event( msg ) ))
1086 0 : {
1087 0 : GdkPaintable *const saved_icon = GDK_PAINTABLE( gui_resources_get_file_saved( (*this_).resources ) );
1088 0 : if ( shown_icon != saved_icon )
1089 : {
1090 0 : gtk_image_set_from_paintable( save_image, saved_icon );
1091 0 : U8_TRACE_INFO("icon of save button set to SAVED(/)");
1092 : }
1093 : }
1094 0 : else if ( DATA_CHANGE_EVENT_TYPE_REVISION_CHANGED == data_change_message_get_event( msg ) )
1095 : {
1096 : /* undo or redo caused a rewriting of the database revision */
1097 0 : const bool is_saved = io_data_file_is_in_sync( (*this_).data_file );
1098 0 : GdkPaintable *const save_icon
1099 : = is_saved
1100 0 : ? GDK_PAINTABLE( gui_resources_get_file_saved( (*this_).resources ) )
1101 0 : : GDK_PAINTABLE( gui_resources_get_file_save( (*this_).resources ) );
1102 0 : if ( shown_icon != save_icon )
1103 : {
1104 0 : gtk_image_set_from_paintable( save_image, save_icon );
1105 0 : U8_TRACE_INFO("icon of save button set to NOT-SAVED(*)");
1106 : }
1107 : }
1108 0 : else if ( DATA_CHANGE_EVENT_TYPE_DB_PREPARE_CLOSE != data_change_message_get_event( msg ) )
1109 : {
1110 0 : GdkPaintable *const save_icon = GDK_PAINTABLE( gui_resources_get_file_save( (*this_).resources ) );
1111 0 : if ( shown_icon != save_icon )
1112 : {
1113 0 : gtk_image_set_from_paintable( save_image, save_icon );
1114 0 : U8_TRACE_INFO("icon of save button set to NOT-SAVED(*)");
1115 : }
1116 : }
1117 :
1118 0 : U8_TRACE_TIMESTAMP();
1119 0 : U8_TRACE_END();
1120 0 : }
1121 :
1122 0 : void gui_main_window_state_callback( GtkWidget* window, GtkStateFlags old_flags, gpointer user_data )
1123 : {
1124 0 : U8_TRACE_BEGIN();
1125 0 : gui_main_window_t *this_ = user_data;
1126 :
1127 : /* GTK_STATE_FLAG_ACTIVE is close to what is needed */
1128 : /* but it is sometimes not called when window loses focus */
1129 : /* and it is sometimes activated multiple times when window gains focus */
1130 :
1131 0 : GtkStateFlags new_flags = gtk_widget_get_state_flags ( window );
1132 0 : GtkStateFlags changed = old_flags ^ new_flags;
1133 :
1134 0 : if ( 0 != ( changed & GTK_STATE_FLAG_BACKDROP ) )
1135 : {
1136 0 : U8_TRACE_INFO_INT("GTK_STATE_FLAGS & GTK_STATE_FLAG_BACKDROP:", changed & GTK_STATE_FLAG_BACKDROP );
1137 :
1138 : /* check if latest changes are already saved */
1139 0 : const bool is_saved = io_data_file_is_in_sync( (*this_).data_file );
1140 :
1141 : /* update the icon of the save button */
1142 0 : GtkImage *const save_image = gui_button_get_icon_ptr( &((*this_).file_save) );
1143 0 : const GdkPaintable *const shown_icon = gtk_image_get_paintable( save_image );
1144 :
1145 0 : GdkPaintable *const current_icon
1146 : = is_saved
1147 0 : ? GDK_PAINTABLE( gui_resources_get_file_saved( (*this_).resources ) )
1148 0 : : ( 0 != ( new_flags & GTK_STATE_FLAG_BACKDROP ) )
1149 0 : ? GDK_PAINTABLE( gui_resources_get_file_unsaved( (*this_).resources ) )
1150 0 : : GDK_PAINTABLE( gui_resources_get_file_save( (*this_).resources ) );
1151 :
1152 : /* update the save button status indicator */
1153 0 : if ( shown_icon != current_icon )
1154 : {
1155 0 : gtk_image_set_from_paintable( save_image, current_icon );
1156 0 : U8_TRACE_INFO("icon of save button updated");
1157 : }
1158 : // U8_TRACE_FLUSH();
1159 :
1160 : /* check if json file on disk was modified in the meantime */
1161 0 : const bool json_was_modified = io_data_file_is_externally_modified( (*this_).data_file );
1162 0 : if ( json_was_modified )
1163 : {
1164 0 : gui_simple_message_to_user_show_message_with_name( &((*this_).message_to_user),
1165 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
1166 : GUI_SIMPLE_MESSAGE_CONTENT_JSON_MODIFIED,
1167 0 : io_data_file_get_filename_const( (*this_).data_file )
1168 : );
1169 : }
1170 :
1171 : }
1172 :
1173 0 : U8_TRACE_TIMESTAMP();
1174 0 : U8_TRACE_END();
1175 0 : }
1176 :
1177 :
1178 : /*
1179 : Copyright 2016-2025 Andreas Warnke
1180 :
1181 : Licensed under the Apache License, Version 2.0 (the "License");
1182 : you may not use this file except in compliance with the License.
1183 : You may obtain a copy of the License at
1184 :
1185 : http://www.apache.org/licenses/LICENSE-2.0
1186 :
1187 : Unless required by applicable law or agreed to in writing, software
1188 : distributed under the License is distributed on an "AS IS" BASIS,
1189 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1190 : See the License for the specific language governing permissions and
1191 : limitations under the License.
1192 : */
|