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