Line data Source code
1 : /* File: gui_toolbox.c; Copyright and License: see below */
2 :
3 : #include "gui_toolbox.h"
4 : #include "ctrl_multi_step_changer.h"
5 : #include "u8/u8_trace.h"
6 : #include "u8/u8_error.h"
7 : #include "utf8stringbuf/utf8string.h"
8 : #include <assert.h>
9 : #include "gui_gtk.h"
10 : #include <stdbool.h>
11 :
12 : static bool gui_toolbox_glib_signal_initialized = false;
13 : static guint gui_toolbox_glib_signal_id = 0;
14 : const char *GUI_TOOLBOX_GLIB_SIGNAL_NAME = "cfu_tool_changed";
15 :
16 0 : void gui_toolbox_init( gui_toolbox_t *this_,
17 : GtkWidget *toolbar,
18 : GtkWidget *tool_navigate,
19 : GtkWidget *tool_edit,
20 : GtkWidget *tool_create,
21 : GtkWidget *tool_search,
22 : GdkClipboard *gtk_clipboard,
23 : gui_marked_set_t *marker,
24 : gui_simple_message_to_user_t *message_to_user,
25 : data_database_reader_t *db_reader,
26 : ctrl_controller_t *controller )
27 : {
28 0 : U8_TRACE_BEGIN();
29 0 : assert( NULL != tool_navigate );
30 0 : assert( NULL != tool_edit );
31 0 : assert( NULL != tool_create );
32 0 : assert( NULL != tool_search );
33 0 : assert( NULL != gtk_clipboard );
34 0 : assert( NULL != marker );
35 0 : assert( NULL != message_to_user );
36 0 : assert( NULL != db_reader );
37 0 : assert( NULL != controller );
38 :
39 0 : (*this_).selected_tool = GUI_TOOL_NAVIGATE;
40 0 : (*this_).marker = marker;
41 0 : (*this_).message_to_user = message_to_user;
42 0 : (*this_).db_reader = db_reader;
43 0 : (*this_).controller = controller;
44 0 : (*this_).toolbar = toolbar;
45 0 : (*this_).tool_navigate = tool_navigate;
46 0 : (*this_).tool_edit = tool_edit;
47 0 : (*this_).tool_create = tool_create;
48 0 : (*this_).tool_search = tool_search;
49 :
50 0 : gui_clipboard_init( &((*this_).clipboard),
51 : gtk_clipboard,
52 : this_,
53 : message_to_user,
54 : db_reader,
55 : controller
56 : );
57 :
58 : /* define a new signal */
59 0 : if ( ! gui_toolbox_glib_signal_initialized )
60 : {
61 0 : gui_toolbox_glib_signal_id = g_signal_new(
62 : GUI_TOOLBOX_GLIB_SIGNAL_NAME,
63 : G_TYPE_OBJECT,
64 : G_SIGNAL_RUN_FIRST,
65 : 0,
66 : NULL,
67 : NULL,
68 : g_cclosure_marshal_VOID__INT,
69 : G_TYPE_NONE,
70 : 1,
71 : G_TYPE_INT /* gui_tool_t */
72 : );
73 0 : gui_toolbox_glib_signal_initialized = true;
74 0 : U8_TRACE_INFO_INT( "g_signal_new(\"cfu_tool_changed\") returned new signal id", gui_toolbox_glib_signal_id );
75 : }
76 :
77 0 : U8_TRACE_END();
78 0 : }
79 :
80 0 : void gui_toolbox_destroy( gui_toolbox_t *this_ )
81 : {
82 0 : U8_TRACE_BEGIN();
83 :
84 0 : gui_clipboard_destroy( &((*this_).clipboard) );
85 :
86 0 : (*this_).db_reader = NULL;
87 0 : (*this_).controller = NULL;
88 0 : (*this_).marker = NULL;
89 0 : (*this_).message_to_user = NULL;
90 0 : (*this_).toolbar = NULL;
91 0 : (*this_).tool_navigate = NULL;
92 0 : (*this_).tool_edit = NULL;
93 0 : (*this_).tool_create = NULL;
94 0 : (*this_).tool_search = NULL;
95 :
96 0 : U8_TRACE_END();
97 0 : }
98 :
99 0 : void gui_toolbox_set_selected_tool( gui_toolbox_t *this_, gui_tool_t tool )
100 : {
101 0 : U8_TRACE_BEGIN();
102 :
103 0 : switch ( tool )
104 : {
105 0 : case GUI_TOOL_NAVIGATE:
106 : {
107 0 : gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( (*this_).tool_navigate ), true );
108 0 : (*this_).selected_tool = GUI_TOOL_NAVIGATE;
109 0 : gui_toolbox_private_notify_listeners( this_ );
110 : }
111 0 : break;
112 :
113 0 : case GUI_TOOL_EDIT:
114 : {
115 0 : gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( (*this_).tool_edit ), true );
116 0 : (*this_).selected_tool = GUI_TOOL_EDIT;
117 0 : gui_toolbox_private_notify_listeners( this_ );
118 : }
119 0 : break;
120 :
121 0 : case GUI_TOOL_SEARCH:
122 : {
123 0 : gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( (*this_).tool_search ), true );
124 0 : (*this_).selected_tool = GUI_TOOL_SEARCH;
125 0 : gui_toolbox_private_notify_listeners( this_ );
126 : }
127 0 : break;
128 :
129 0 : case GUI_TOOL_CREATE:
130 : {
131 0 : gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( (*this_).tool_create ), true );
132 0 : (*this_).selected_tool = GUI_TOOL_CREATE;
133 0 : gui_toolbox_private_notify_listeners( this_ );
134 : }
135 0 : break;
136 :
137 0 : default:
138 : {
139 0 : U8_LOG_ERROR( "invalid enum value" );
140 : }
141 0 : break;
142 : }
143 :
144 0 : U8_TRACE_END();
145 0 : }
146 :
147 0 : void gui_toolbox_set_selected_tool_and_focus ( gui_toolbox_t *this_, gui_tool_t tool, data_id_t focused_diagram )
148 : {
149 0 : U8_TRACE_BEGIN();
150 :
151 : /* switch tool */
152 0 : gui_toolbox_set_selected_tool( this_, tool );
153 :
154 0 : const data_id_t current_focus = gui_marked_set_get_focused_diagram( (*this_).marker );
155 0 : if ( ! data_id_equals_or_both_void( &focused_diagram, ¤t_focus ) )
156 : {
157 : /* clear selected set (no notification) */
158 0 : gui_marked_set_clear_selected_set( (*this_).marker );
159 :
160 : /* request to load the diagram to focus on */
161 0 : gui_marked_set_request_focused_diagram( (*this_).marker, focused_diagram );
162 : }
163 :
164 0 : U8_TRACE_END();
165 0 : }
166 :
167 0 : void gui_toolbox_navigate_btn_callback( GtkWidget* button, gpointer data )
168 : {
169 0 : U8_TRACE_BEGIN();
170 0 : gui_toolbox_t *this_ = data;
171 0 : assert( NULL != this_ );
172 :
173 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
174 :
175 0 : (*this_).selected_tool = GUI_TOOL_NAVIGATE;
176 :
177 0 : gui_toolbox_private_notify_listeners( this_ );
178 :
179 0 : U8_TRACE_TIMESTAMP();
180 0 : U8_TRACE_END();
181 0 : }
182 :
183 0 : void gui_toolbox_edit_btn_callback( GtkWidget* button, gpointer data )
184 : {
185 0 : U8_TRACE_BEGIN();
186 0 : gui_toolbox_t *this_ = data;
187 0 : assert( NULL != this_ );
188 :
189 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
190 :
191 0 : (*this_).selected_tool = GUI_TOOL_EDIT;
192 :
193 0 : gui_toolbox_private_notify_listeners( this_ );
194 :
195 0 : U8_TRACE_TIMESTAMP();
196 0 : U8_TRACE_END();
197 0 : }
198 :
199 0 : void gui_toolbox_create_btn_callback( GtkWidget* button, gpointer data )
200 : {
201 0 : U8_TRACE_BEGIN();
202 0 : gui_toolbox_t *this_ = data;
203 0 : assert( NULL != this_ );
204 :
205 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
206 :
207 0 : (*this_).selected_tool = GUI_TOOL_CREATE;
208 :
209 0 : gui_toolbox_private_notify_listeners( this_ );
210 :
211 0 : U8_TRACE_TIMESTAMP();
212 0 : U8_TRACE_END();
213 0 : }
214 :
215 0 : void gui_toolbox_search_btn_callback( GtkWidget* button, gpointer data )
216 : {
217 0 : U8_TRACE_BEGIN();
218 0 : gui_toolbox_t *this_ = data;
219 0 : assert( NULL != this_ );
220 :
221 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
222 :
223 0 : (*this_).selected_tool = GUI_TOOL_SEARCH;
224 :
225 0 : gui_toolbox_private_notify_listeners( this_ );
226 :
227 0 : U8_TRACE_TIMESTAMP();
228 0 : U8_TRACE_END();
229 0 : }
230 :
231 0 : void gui_toolbox_search_id_btn_callback( GtkWidget* button, gpointer data )
232 : {
233 0 : U8_TRACE_BEGIN();
234 0 : gui_toolbox_t *this_ = data;
235 0 : assert( NULL != this_ );
236 :
237 0 : gui_toolbox_set_selected_tool( this_, GUI_TOOL_SEARCH );
238 :
239 0 : U8_TRACE_TIMESTAMP();
240 0 : U8_TRACE_END();
241 0 : }
242 :
243 0 : void gui_toolbox_cut_btn_callback( GtkWidget* button, gpointer data )
244 : {
245 0 : U8_TRACE_BEGIN();
246 0 : gui_toolbox_t *this_ = data;
247 0 : assert( NULL != this_ );
248 :
249 0 : gui_toolbox_cut( this_ );
250 :
251 0 : U8_TRACE_TIMESTAMP();
252 0 : U8_TRACE_END();
253 0 : }
254 :
255 0 : void gui_toolbox_cut( gui_toolbox_t *this_ )
256 : {
257 0 : U8_TRACE_BEGIN();
258 : u8_error_t ctrl_err;
259 :
260 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
261 :
262 : data_stat_t stat;
263 0 : data_stat_init(&stat);
264 :
265 0 : const data_small_set_t *const set_to_be_cut = gui_marked_set_get_selected_set_const( (*this_).marker );
266 :
267 : /* do not check if set is empty; gui_clipboard_copy_set_to_clipboard will do this */
268 :
269 0 : gui_clipboard_copy_set_to_clipboard( &((*this_).clipboard), set_to_be_cut, &stat );
270 :
271 0 : ctrl_err = gui_toolbox_private_delete_set( this_, set_to_be_cut, &stat );
272 :
273 0 : gui_marked_set_clear_selected_set( (*this_).marker );
274 :
275 0 : if ( U8_ERROR_INPUT_EMPTY == ctrl_err )
276 : {
277 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
278 : GUI_SIMPLE_MESSAGE_TYPE_WARNING,
279 : GUI_SIMPLE_MESSAGE_CONTENT_NO_SELECTION
280 : );
281 : }
282 0 : else if ( u8_error_contains( ctrl_err, U8_ERROR_OBJECT_STILL_REFERENCED ) )
283 : {
284 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
285 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
286 : GUI_SIMPLE_MESSAGE_CONTENT_DELETING_NOT_POSSIBLE
287 : );
288 : }
289 0 : else if ( U8_ERROR_NONE != ctrl_err )
290 : {
291 0 : U8_LOG_ERROR_HEX( "Error in ctrl_classifier_controller_delete_set_from_diagram", ctrl_err );
292 : }
293 : else
294 : {
295 0 : gui_simple_message_to_user_show_message_with_stat ( (*this_).message_to_user,
296 : GUI_SIMPLE_MESSAGE_TYPE_INFO,
297 : GUI_SIMPLE_MESSAGE_CONTENT_CUT_TO_CLIPBOARD,
298 : &stat
299 : );
300 : }
301 :
302 0 : data_stat_destroy(&stat);
303 :
304 0 : U8_TRACE_END();
305 0 : }
306 :
307 0 : void gui_toolbox_copy_btn_callback( GtkWidget* button, gpointer data )
308 : {
309 0 : U8_TRACE_BEGIN();
310 0 : gui_toolbox_t *this_ = data;
311 0 : assert( NULL != this_ );
312 :
313 0 : gui_toolbox_copy( this_ );
314 :
315 0 : U8_TRACE_TIMESTAMP();
316 0 : U8_TRACE_END();
317 0 : }
318 :
319 0 : void gui_toolbox_copy( gui_toolbox_t *this_ )
320 : {
321 0 : U8_TRACE_BEGIN();
322 : int out_err;
323 :
324 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
325 :
326 : data_stat_t stat;
327 0 : data_stat_init(&stat);
328 :
329 0 : const data_small_set_t *const set_to_be_copied = gui_marked_set_get_selected_set_const( (*this_).marker );
330 :
331 : /* even in case data_small_set_is_empty( set_to_be_copied ),
332 : * it is possible to copy an empty set to the clipboard
333 : * --> therefore simply continue... */
334 0 : out_err = gui_clipboard_copy_set_to_clipboard( &((*this_).clipboard), set_to_be_copied, &stat );
335 :
336 0 : if ( out_err == 0 )
337 : {
338 0 : if ( 0 == data_stat_get_total_count( &stat ) )
339 : {
340 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
341 : GUI_SIMPLE_MESSAGE_TYPE_WARNING,
342 : GUI_SIMPLE_MESSAGE_CONTENT_NO_SELECTION
343 : );
344 : }
345 : else
346 : {
347 0 : gui_simple_message_to_user_show_message_with_stat( (*this_).message_to_user,
348 : GUI_SIMPLE_MESSAGE_TYPE_INFO,
349 : GUI_SIMPLE_MESSAGE_CONTENT_COPY_TO_CLIPBOARD,
350 : &stat
351 : );
352 : }
353 : }
354 : else
355 : {
356 : /* error to be shown to the user */
357 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
358 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
359 : GUI_SIMPLE_MESSAGE_CONTENT_STRING_TRUNCATED
360 : );
361 : }
362 :
363 0 : data_stat_destroy(&stat);
364 :
365 0 : U8_TRACE_END();
366 0 : }
367 :
368 0 : void gui_toolbox_paste_btn_callback( GtkWidget* button, gpointer data )
369 : {
370 0 : U8_TRACE_BEGIN();
371 0 : gui_toolbox_t *this_ = data;
372 0 : assert( NULL != this_ );
373 :
374 0 : gui_toolbox_paste( this_ );
375 :
376 0 : U8_TRACE_TIMESTAMP();
377 0 : U8_TRACE_END();
378 0 : }
379 :
380 0 : void gui_toolbox_paste( gui_toolbox_t *this_ )
381 : {
382 0 : U8_TRACE_BEGIN();
383 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
384 :
385 0 : const data_id_t destination_diagram_id = gui_marked_set_get_focused_diagram( (*this_).marker );
386 0 : if ( data_id_is_valid( &destination_diagram_id ) )
387 : {
388 0 : const data_row_t dest_diagram_row = data_id_get_row( &destination_diagram_id );
389 0 : gui_clipboard_request_clipboard_text( &((*this_).clipboard), dest_diagram_row );
390 :
391 : /* this call triggers a callback later to gui_clipboard_clipboard_text_received_callback */
392 :
393 : /* Note: (*this_).message_to_user is updated by (*this_).clipboard already - nothing to do here */
394 : }
395 : else
396 : {
397 : /* error to be shown to the user */
398 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
399 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
400 : GUI_SIMPLE_MESSAGE_CONTENT_NO_FOCUS
401 : );
402 : }
403 :
404 0 : U8_TRACE_END();
405 0 : }
406 :
407 0 : void gui_toolbox_delete_btn_callback( GtkWidget* button, gpointer data )
408 : {
409 0 : U8_TRACE_BEGIN();
410 0 : gui_toolbox_t *this_ = data;
411 0 : assert( NULL != this_ );
412 :
413 0 : gui_toolbox_delete( this_ );
414 :
415 0 : U8_TRACE_TIMESTAMP();
416 0 : U8_TRACE_END();
417 0 : }
418 :
419 0 : void gui_toolbox_delete( gui_toolbox_t *this_ )
420 : {
421 0 : U8_TRACE_BEGIN();
422 : u8_error_t ctrl_err;
423 :
424 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
425 :
426 : data_stat_t stat;
427 0 : data_stat_init(&stat);
428 :
429 0 : const data_small_set_t *const set_to_be_deleted = gui_marked_set_get_selected_set_const( (*this_).marker );
430 :
431 : /* do not check if set is empty; gui_toolbox_private_delete_set will do this */
432 :
433 0 : ctrl_err = gui_toolbox_private_delete_set( this_, set_to_be_deleted, &stat );
434 :
435 0 : gui_marked_set_clear_selected_set( (*this_).marker );
436 :
437 0 : if ( U8_ERROR_INPUT_EMPTY == ctrl_err )
438 : {
439 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
440 : GUI_SIMPLE_MESSAGE_TYPE_WARNING,
441 : GUI_SIMPLE_MESSAGE_CONTENT_NO_SELECTION
442 : );
443 : }
444 0 : else if ( u8_error_contains( ctrl_err, U8_ERROR_OBJECT_STILL_REFERENCED ) )
445 : {
446 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
447 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
448 : GUI_SIMPLE_MESSAGE_CONTENT_DELETING_NOT_POSSIBLE
449 : );
450 : }
451 0 : else if ( U8_ERROR_NONE != ctrl_err )
452 : {
453 0 : U8_LOG_ERROR_HEX( "Error in ctrl_classifier_controller_delete_set_from_diagram", ctrl_err );
454 : }
455 : else
456 : {
457 0 : gui_simple_message_to_user_show_message_with_stat ( (*this_).message_to_user,
458 : GUI_SIMPLE_MESSAGE_TYPE_INFO,
459 : GUI_SIMPLE_MESSAGE_CONTENT_DELETE,
460 : &stat
461 : );
462 : }
463 :
464 0 : data_stat_destroy(&stat);
465 :
466 0 : U8_TRACE_END();
467 0 : }
468 :
469 0 : u8_error_t gui_toolbox_private_delete_set( gui_toolbox_t *this_,
470 : const data_small_set_t *set_to_be_deleted,
471 : data_stat_t *io_stat )
472 : {
473 0 : U8_TRACE_BEGIN();
474 0 : assert( NULL != set_to_be_deleted );
475 0 : assert( NULL != io_stat );
476 : u8_error_t ctrl_err;
477 :
478 : ctrl_multi_step_changer_t multi_stepper;
479 0 : ctrl_multi_step_changer_init( &multi_stepper, (*this_).controller, (*this_).db_reader );
480 :
481 0 : ctrl_err = ctrl_multi_step_changer_delete_set ( &multi_stepper, set_to_be_deleted, io_stat );
482 :
483 0 : ctrl_multi_step_changer_destroy( &multi_stepper );
484 :
485 0 : U8_TRACE_END_ERR( ctrl_err );
486 0 : return ctrl_err;
487 : }
488 :
489 0 : void gui_toolbox_highlight_btn_callback( GtkWidget* button, gpointer data )
490 : {
491 0 : U8_TRACE_BEGIN();
492 0 : gui_toolbox_t *this_ = data;
493 0 : assert( NULL != this_ );
494 :
495 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
496 :
497 0 : const data_small_set_t *const set_to_be_highlighted = gui_marked_set_get_selected_set_const( (*this_).marker );
498 :
499 : /* do not check if set is empty; gui_toolbox_private_toggle_display_flag_in_set will do this */
500 :
501 0 : gui_toolbox_private_toggle_display_flag_in_set( this_,
502 : set_to_be_highlighted,
503 : DATA_DIAGRAMELEMENT_FLAG_EMPHASIS | DATA_DIAGRAMELEMENT_FLAG_GRAY_OUT
504 : );
505 :
506 0 : U8_TRACE_TIMESTAMP();
507 0 : U8_TRACE_END();
508 0 : }
509 :
510 0 : void gui_toolbox_instantiate_btn_callback( GtkWidget* button, gpointer data )
511 : {
512 0 : U8_TRACE_BEGIN();
513 0 : gui_toolbox_t *this_ = data;
514 0 : assert( NULL != this_ );
515 :
516 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
517 :
518 0 : const data_small_set_t *const set_to_be_instantiated = gui_marked_set_get_selected_set_const( (*this_).marker );
519 :
520 : /* do not check if set is empty; gui_toolbox_private_toggle_display_flag_in_set will do this */
521 :
522 0 : gui_toolbox_private_toggle_display_flag_in_set( this_,
523 : set_to_be_instantiated,
524 : DATA_DIAGRAMELEMENT_FLAG_NAMED_INSTANCE | DATA_DIAGRAMELEMENT_FLAG_ANONYMOUS_INSTANCE
525 : );
526 :
527 0 : U8_TRACE_TIMESTAMP();
528 0 : U8_TRACE_END();
529 0 : }
530 :
531 0 : void gui_toolbox_reset_btn_callback( GtkWidget* button, gpointer data )
532 : {
533 0 : U8_TRACE_BEGIN();
534 0 : gui_toolbox_t *this_ = data;
535 0 : assert( NULL != this_ );
536 :
537 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
538 :
539 0 : gui_marked_set_clear_selected_set( (*this_).marker );
540 : //gui_marked_set_clear_focused( (*this_).marker );
541 :
542 : /* trigger redraw */
543 0 : gui_toolbox_private_notify_listeners( this_ );
544 :
545 0 : U8_TRACE_TIMESTAMP();
546 0 : U8_TRACE_END();
547 0 : }
548 :
549 0 : void gui_toolbox_private_toggle_display_flag_in_set( gui_toolbox_t *this_,
550 : const data_small_set_t *set_to_be_toggled,
551 : data_diagramelement_flag_t flag_bits_to_toggle )
552 : {
553 0 : U8_TRACE_BEGIN();
554 0 : u8_error_t error = U8_ERROR_NONE;
555 0 : bool new_pattern_initialized = false;
556 0 : data_diagramelement_flag_t new_pattern = DATA_DIAGRAMELEMENT_FLAG_NONE;
557 0 : bool is_first = true;
558 :
559 0 : for ( int index = 0; index < data_small_set_get_count( set_to_be_toggled ); index ++ )
560 : {
561 : data_id_t current_id;
562 0 : current_id = data_small_set_get_id( set_to_be_toggled, index );
563 0 : switch ( data_id_get_table( ¤t_id ) )
564 : {
565 0 : case DATA_TABLE_CLASSIFIER:
566 : {
567 : /* program internal error */
568 0 : U8_LOG_WARNING( "gui_toolbox_private_toggle_display_flag_in_set cannot toggle display flags in non-diagramelements." );
569 0 : error |= U8_ERROR_INVALID_REQUEST;
570 : }
571 0 : break;
572 :
573 0 : case DATA_TABLE_FEATURE:
574 : {
575 : /* program internal error */
576 0 : U8_LOG_WARNING( "gui_toolbox_private_toggle_display_flag_in_set cannot toggle display flags in non-diagramelements." );
577 0 : error |= U8_ERROR_INVALID_REQUEST;
578 : }
579 0 : break;
580 :
581 0 : case DATA_TABLE_RELATIONSHIP:
582 : {
583 : /* program internal error */
584 0 : U8_LOG_WARNING( "gui_toolbox_private_toggle_display_flag_in_set cannot toggle display flags in non-diagramelements." );
585 0 : error |= U8_ERROR_INVALID_REQUEST;
586 : }
587 0 : break;
588 :
589 0 : case DATA_TABLE_DIAGRAMELEMENT:
590 : {
591 : data_diagramelement_t out_diagramelement;
592 0 : data_row_t diag_elem_id = data_id_get_row( ¤t_id );
593 : ctrl_diagram_controller_t *diag_ctrl;
594 0 : diag_ctrl = ctrl_controller_get_diagram_control_ptr( (*this_).controller );
595 :
596 0 : error |= (u8_error_t) data_database_reader_get_diagramelement_by_id ( (*this_).db_reader,
597 : diag_elem_id,
598 : &out_diagramelement
599 : );
600 : data_diagramelement_flag_t current_flags;
601 0 : current_flags = data_diagramelement_get_display_flags( &out_diagramelement );
602 :
603 0 : if ( ! new_pattern_initialized )
604 : {
605 : /* select zero or one bit to set. alg: select the next highest bit */
606 0 : bool last_was_set = true;
607 0 : new_pattern = DATA_DIAGRAMELEMENT_FLAG_NONE;
608 0 : for ( int bit = 0; bit < 8*sizeof(data_diagramelement_flag_t); bit ++ )
609 : {
610 0 : data_diagramelement_flag_t probe = (1 << bit);
611 0 : if ( 0 != ( probe & flag_bits_to_toggle ) )
612 : {
613 : /* this is a relevant bit */
614 0 : if ( 0 != ( probe & current_flags ) )
615 : {
616 0 : new_pattern = DATA_DIAGRAMELEMENT_FLAG_NONE;
617 0 : last_was_set = true;
618 : }
619 : else
620 : {
621 0 : if ( last_was_set )
622 : {
623 0 : new_pattern = probe;
624 : }
625 0 : last_was_set = false;
626 : }
627 : }
628 : }
629 0 : new_pattern_initialized = true;
630 : }
631 :
632 0 : current_flags = (current_flags & (~flag_bits_to_toggle)) | new_pattern;
633 :
634 0 : error |= ctrl_diagram_controller_update_diagramelement_display_flags( diag_ctrl,
635 : diag_elem_id,
636 : current_flags,
637 : ( is_first
638 : ? CTRL_UNDO_REDO_ACTION_BOUNDARY_START_NEW
639 0 : : CTRL_UNDO_REDO_ACTION_BOUNDARY_APPEND )
640 : );
641 0 : is_first = false;
642 : }
643 0 : break;
644 :
645 0 : case DATA_TABLE_DIAGRAM:
646 : {
647 : /* program internal error */
648 0 : U8_LOG_WARNING( "gui_toolbox_private_toggle_display_flag_in_set cannot toggle display flags in non-diagramelements." );
649 0 : error |= U8_ERROR_INVALID_REQUEST;
650 : }
651 0 : break;
652 :
653 0 : default:
654 : {
655 : /* program internal error */
656 0 : U8_LOG_ERROR( "gui_toolbox_private_toggle_display_flag_in_set fould illegal data_table_t enum value." );
657 : }
658 0 : break;
659 : }
660 : }
661 :
662 0 : if ( error != U8_ERROR_NONE )
663 : {
664 : /* error to be shown to the user */
665 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
666 : GUI_SIMPLE_MESSAGE_TYPE_WARNING,
667 : GUI_SIMPLE_MESSAGE_CONTENT_SET_PARTLY_UNSUITABLE
668 : );
669 : }
670 0 : else if ( 0 == data_small_set_get_count( set_to_be_toggled ) )
671 : {
672 : /* error to be shown to the user */
673 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
674 : GUI_SIMPLE_MESSAGE_TYPE_WARNING,
675 : GUI_SIMPLE_MESSAGE_CONTENT_NO_SELECTION
676 : );
677 : }
678 :
679 0 : U8_TRACE_END();
680 0 : }
681 :
682 0 : void gui_toolbox_undo_btn_callback( GtkWidget* button, gpointer data )
683 : {
684 0 : U8_TRACE_BEGIN();
685 0 : gui_toolbox_t *this_ = data;
686 0 : assert( this_ != NULL );
687 : u8_error_t ctrl_err;
688 :
689 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
690 :
691 : data_stat_t stat;
692 0 : data_stat_init( &stat );
693 :
694 0 : ctrl_err = ctrl_controller_undo( (*this_).controller, &stat );
695 :
696 : /* find a diagram that can show the changes */
697 0 : if ( U8_ERROR_NONE == ctrl_err )
698 : {
699 : ctrl_undo_redo_iterator_t iter;
700 0 : ctrl_undo_redo_iterator_init_empty( &iter );
701 0 : ctrl_err |= ctrl_controller_get_redo_iterator( (*this_).controller, &iter );
702 0 : gui_toolbox_private_show_changes( this_, &iter );
703 0 : ctrl_undo_redo_iterator_destroy( &iter );
704 : }
705 :
706 : /* show error/success message to the user */
707 0 : if ( U8_ERROR_INVALID_REQUEST == ctrl_err )
708 : {
709 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
710 : GUI_SIMPLE_MESSAGE_TYPE_WARNING,
711 : GUI_SIMPLE_MESSAGE_CONTENT_NO_MORE_UNDO
712 : );
713 : }
714 0 : else if ( U8_ERROR_ARRAY_BUFFER_EXCEEDED == ctrl_err )
715 : {
716 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
717 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
718 : GUI_SIMPLE_MESSAGE_CONTENT_UNDO_NOT_POSSIBLE
719 : );
720 : }
721 : else
722 : {
723 : /* success */
724 0 : gui_simple_message_to_user_show_message_with_stat( (*this_).message_to_user,
725 : GUI_SIMPLE_MESSAGE_TYPE_INFO,
726 : GUI_SIMPLE_MESSAGE_CONTENT_UNDO,
727 : &stat
728 : );
729 : }
730 :
731 0 : data_stat_destroy( &stat );
732 :
733 0 : U8_TRACE_TIMESTAMP();
734 0 : U8_TRACE_END();
735 0 : }
736 :
737 0 : gboolean gui_toolbox_undo_shortcut_callback( GtkWidget* widget, GVariant* args, gpointer user_data )
738 : {
739 0 : gui_toolbox_undo_btn_callback( widget, user_data );
740 0 : return TRUE;
741 : }
742 :
743 0 : void gui_toolbox_redo_btn_callback( GtkWidget* button, gpointer data )
744 : {
745 0 : U8_TRACE_BEGIN();
746 0 : gui_toolbox_t *this_ = data;
747 0 : assert( this_ != NULL );
748 : u8_error_t ctrl_err;
749 :
750 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
751 :
752 : data_stat_t stat;
753 0 : data_stat_init( &stat );
754 :
755 0 : ctrl_err = ctrl_controller_redo( (*this_).controller, &stat );
756 :
757 : /* find a diagram that can show the changes */
758 0 : if ( U8_ERROR_NONE == ctrl_err )
759 : {
760 : ctrl_undo_redo_iterator_t iter;
761 0 : ctrl_undo_redo_iterator_init_empty( &iter );
762 0 : ctrl_err |= ctrl_controller_get_undo_iterator( (*this_).controller, &iter );
763 0 : gui_toolbox_private_show_changes( this_, &iter );
764 0 : ctrl_undo_redo_iterator_destroy( &iter );
765 : }
766 :
767 : /* show error/success message to the user */
768 0 : if ( U8_ERROR_INVALID_REQUEST == ctrl_err )
769 : {
770 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
771 : GUI_SIMPLE_MESSAGE_TYPE_WARNING,
772 : GUI_SIMPLE_MESSAGE_CONTENT_NO_MORE_REDO
773 : );
774 : }
775 : else
776 : {
777 : /* success */
778 0 : gui_simple_message_to_user_show_message_with_stat( (*this_).message_to_user,
779 : GUI_SIMPLE_MESSAGE_TYPE_INFO,
780 : GUI_SIMPLE_MESSAGE_CONTENT_REDO,
781 : &stat
782 : );
783 : }
784 :
785 0 : data_stat_destroy( &stat );
786 :
787 0 : U8_TRACE_TIMESTAMP();
788 0 : U8_TRACE_END();
789 0 : }
790 :
791 : /*!
792 : * \brief callback that informs that the redo shortcut was activated
793 : */
794 0 : gboolean gui_toolbox_redo_shortcut_callback( GtkWidget* widget, GVariant* args, gpointer user_data )
795 : {
796 0 : gui_toolbox_redo_btn_callback( widget, user_data );
797 0 : return TRUE;
798 : }
799 :
800 0 : void gui_toolbox_private_show_changes( gui_toolbox_t *this_,
801 : ctrl_undo_redo_iterator_t *action_iterator )
802 : {
803 0 : U8_TRACE_BEGIN();
804 0 : data_id_t display_diag_nav = DATA_ID_VOID; /* display this diagram in navigation mode, prio 1 */
805 0 : data_id_t display_diag_edit = DATA_ID_VOID; /* display this diagram in edit mode, prio 2 */
806 0 : data_id_t display_classifier = DATA_ID_VOID; /* display this classifier somehow, prio 3 */
807 :
808 0 : while( ctrl_undo_redo_iterator_has_next( action_iterator ) )
809 : {
810 0 : const ctrl_undo_redo_entry_t *const probe = ctrl_undo_redo_iterator_next( action_iterator );
811 :
812 0 : switch( ctrl_undo_redo_entry_get_action_type( probe ) )
813 : {
814 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_DELETE_DIAGRAM:
815 : {
816 : const data_diagram_t *const diag
817 0 : = ctrl_undo_redo_entry_get_diagram_before_action_const( probe );
818 0 : assert( data_diagram_is_valid( diag ) );
819 0 : display_diag_nav = data_diagram_get_parent_data_id( diag );
820 : }
821 0 : break;
822 :
823 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_CREATE_DIAGRAM:
824 : {
825 : const data_diagram_t *const diag
826 0 : = ctrl_undo_redo_entry_get_diagram_after_action_const( probe );
827 0 : assert( data_diagram_is_valid( diag ) );
828 0 : display_diag_nav = data_diagram_get_parent_data_id( diag );
829 : }
830 0 : break;
831 :
832 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_UPDATE_DIAGRAM:
833 : {
834 : const data_diagram_t *const diag
835 0 : = ctrl_undo_redo_entry_get_diagram_after_action_const( probe );
836 0 : assert( data_diagram_is_valid( diag ) );
837 0 : display_diag_edit = data_diagram_get_data_id( diag );
838 : }
839 0 : break;
840 :
841 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_DELETE_DIAGRAMELEMENT:
842 : {
843 : const data_diagramelement_t *const diagele
844 0 : = ctrl_undo_redo_entry_get_diagramelement_before_action_const( probe );
845 0 : assert( data_diagramelement_is_valid( diagele ) );
846 0 : display_diag_edit = data_diagramelement_get_diagram_data_id( diagele );
847 : }
848 0 : break;
849 :
850 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_CREATE_DIAGRAMELEMENT: /* ... or ... */
851 : case CTRL_UNDO_REDO_ENTRY_TYPE_UPDATE_DIAGRAMELEMENT:
852 : {
853 : const data_diagramelement_t *const diagele
854 0 : = ctrl_undo_redo_entry_get_diagramelement_after_action_const( probe );
855 0 : assert( data_diagramelement_is_valid( diagele ) );
856 0 : display_diag_edit = data_diagramelement_get_diagram_data_id( diagele );
857 : }
858 0 : break;
859 :
860 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_DELETE_CLASSIFIER: /* ... or ... */
861 : case CTRL_UNDO_REDO_ENTRY_TYPE_CREATE_CLASSIFIER:
862 : {
863 : /* Nothing to do: When a classifier is created or deleted, also */
864 : /* a diagramelement is created or deleted. */
865 : }
866 0 : break;
867 :
868 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_UPDATE_CLASSIFIER:
869 : {
870 : const data_classifier_t *const classifier
871 0 : = ctrl_undo_redo_entry_get_classifier_after_action_const( probe );
872 0 : assert( data_classifier_is_valid( classifier ) );
873 0 : display_classifier = data_classifier_get_data_id( classifier );
874 : }
875 0 : break;
876 :
877 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_DELETE_FEATURE:
878 : {
879 : const data_feature_t *const feature
880 0 : = ctrl_undo_redo_entry_get_feature_before_action_const( probe );
881 0 : assert( data_feature_is_valid( feature ) );
882 0 : display_classifier = data_feature_get_classifier_data_id( feature );
883 : }
884 0 : break;
885 :
886 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_UPDATE_FEATURE: /* ... or ... */
887 : case CTRL_UNDO_REDO_ENTRY_TYPE_CREATE_FEATURE:
888 : {
889 : const data_feature_t *const feature
890 0 : = ctrl_undo_redo_entry_get_feature_after_action_const( probe );
891 0 : assert( data_feature_is_valid( feature ) );
892 0 : display_classifier = data_feature_get_classifier_data_id( feature );
893 : }
894 0 : break;
895 :
896 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_DELETE_RELATIONSHIP:
897 : {
898 : const data_relationship_t *const relationship
899 0 : = ctrl_undo_redo_entry_get_relationship_before_action_const( probe );
900 0 : assert( data_relationship_is_valid( relationship ) );
901 0 : display_classifier = data_relationship_get_from_classifier_data_id( relationship );
902 : }
903 0 : break;
904 :
905 0 : case CTRL_UNDO_REDO_ENTRY_TYPE_UPDATE_RELATIONSHIP: /* ... or ... */
906 : case CTRL_UNDO_REDO_ENTRY_TYPE_CREATE_RELATIONSHIP:
907 : {
908 : const data_relationship_t *const relationship
909 0 : = ctrl_undo_redo_entry_get_relationship_after_action_const( probe );
910 0 : assert( data_relationship_is_valid( relationship ) );
911 0 : display_classifier = data_relationship_get_from_classifier_data_id( relationship );
912 : }
913 0 : break;
914 :
915 0 : default: /* ... or ... */
916 : case CTRL_UNDO_REDO_ENTRY_TYPE_BOUNDARY:
917 : {
918 0 : U8_LOG_WARNING( "gui_toolbox_private_show_changes runs into unexpected switch-case." );
919 0 : assert( false );
920 : }
921 : break;
922 : }
923 : }
924 :
925 0 : if ( data_id_is_valid( &display_diag_nav ) )
926 : {
927 0 : gui_toolbox_set_selected_tool_and_focus( this_, GUI_TOOL_NAVIGATE, display_diag_nav );
928 : }
929 0 : else if ( data_id_is_valid( &display_diag_edit ) )
930 : {
931 0 : gui_toolbox_set_selected_tool_and_focus( this_, GUI_TOOL_EDIT, display_diag_edit );
932 : }
933 0 : else if ( data_id_is_valid( &display_classifier ) )
934 : {
935 : /* try to find ANY diagram that MAY show the modified object: */
936 0 : assert( data_id_get_table( &display_classifier ) == DATA_TABLE_CLASSIFIER );
937 : data_small_set_t diagram_ids;
938 0 : data_small_set_init( &diagram_ids );
939 : const u8_error_t d_err
940 0 : = data_database_reader_get_diagram_ids_by_classifier_id( (*this_).db_reader,
941 : data_id_get_row( &display_classifier ),
942 : &diagram_ids
943 : );
944 0 : if (( d_err == U8_ERROR_NONE )||( d_err == U8_ERROR_ARRAY_BUFFER_EXCEEDED ))
945 : {
946 0 : if ( data_small_set_contains( &diagram_ids, gui_marked_set_get_focused_diagram( (*this_).marker ) ) )
947 : {
948 : /* the right diagram is already shown, just update the tool */
949 0 : gui_toolbox_set_selected_tool( this_, GUI_TOOL_EDIT );
950 : }
951 0 : else if ( ! data_small_set_is_empty( &diagram_ids ) )
952 : {
953 0 : gui_toolbox_set_selected_tool_and_focus( this_, GUI_TOOL_EDIT, data_small_set_get_id( &diagram_ids, 0 ) );
954 : }
955 : else
956 : {
957 0 : U8_LOG_ANOMALY( "gui_toolbox_private_show_changes found a classifier without a diagram." );
958 : }
959 : }
960 0 : data_small_set_destroy( &diagram_ids );
961 : }
962 : else
963 : {
964 0 : U8_LOG_ANOMALY( "gui_toolbox_private_show_changes did not find a diagram to show the changes." );
965 : }
966 :
967 0 : U8_TRACE_END();
968 0 : }
969 :
970 0 : void gui_toolbox_private_notify_listeners( gui_toolbox_t *this_ )
971 : {
972 0 : U8_TRACE_BEGIN();
973 :
974 0 : U8_TRACE_INFO( "g_signal_emit to listeners" );
975 0 : g_signal_emit( (*this_).toolbar, gui_toolbox_glib_signal_id, 0, (*this_).selected_tool );
976 :
977 0 : U8_TRACE_END();
978 0 : }
979 :
980 :
981 : /*
982 : Copyright 2016-2026 Andreas Warnke
983 :
984 : Licensed under the Apache License, Version 2.0 (the "License");
985 : you may not use this file except in compliance with the License.
986 : You may obtain a copy of the License at
987 :
988 : http://www.apache.org/licenses/LICENSE-2.0
989 :
990 : Unless required by applicable law or agreed to in writing, software
991 : distributed under the License is distributed on an "AS IS" BASIS,
992 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
993 : See the License for the specific language governing permissions and
994 : limitations under the License.
995 : */
|