Line data Source code
1 : /* File: gui_sketch_area.c; Copyright and License: see below */
2 :
3 : #include "sketch/gui_sketch_area.h"
4 : #include "gui_sketch_card_layouter.h"
5 : #include "gui_tool.h"
6 : #include "pencil_diagram_maker.h"
7 : #include "geometry/geometry_rectangle.h"
8 : #include "entity/data_table.h"
9 : #include "entity/data_id.h"
10 : #include "u8/u8_trace.h"
11 : #include "u8/u8_log.h"
12 : #include "gui_gtk.h"
13 : #include "gui_gdk.h"
14 : #include <stdint.h>
15 : #include <stdbool.h>
16 :
17 0 : void gui_sketch_area_init( gui_sketch_area_t *this_,
18 : GtkWidget *drawing_area,
19 : gui_marked_set_t *marker,
20 : gui_toolbox_t *toolbox,
21 : gui_search_runner_t *search_runner,
22 : gui_simple_message_to_user_t *message_to_user,
23 : const gui_resources_t *resources,
24 : ctrl_controller_t *controller,
25 : data_database_reader_t *db_reader )
26 : {
27 0 : U8_TRACE_BEGIN();
28 0 : assert( NULL != drawing_area );
29 0 : assert( NULL != marker );
30 0 : assert( NULL != toolbox );
31 0 : assert( NULL != search_runner );
32 0 : assert( NULL != message_to_user );
33 0 : assert( NULL != resources );
34 0 : assert( NULL != controller );
35 0 : assert( NULL != db_reader );
36 :
37 : /* init pointers to external objects */
38 0 : (*this_).drawing_area = drawing_area;
39 0 : (*this_).toolbox = toolbox;
40 0 : (*this_).search_runner = search_runner;
41 0 : (*this_).message_to_user = message_to_user;
42 0 : (*this_).resources = resources;
43 0 : (*this_).db_reader = db_reader;
44 0 : (*this_).controller = controller;
45 :
46 : /* init instance of requested tool-mode and diagram-ids */
47 0 : gui_sketch_request_init( &((*this_).request) );
48 :
49 : /* init instances of own objects */
50 0 : (*this_).card_num = 0;
51 0 : data_small_set_init( &((*this_).card_draw_list) );
52 0 : (*this_).marker = marker;
53 0 : gui_sketch_texture_init( &((*this_).texture_downloader) );
54 0 : gui_sketch_nav_tree_init( &((*this_).nav_tree), resources, &((*this_).texture_downloader) );
55 0 : gui_sketch_result_list_init( &((*this_).result_list), resources, &((*this_).texture_downloader) );
56 0 : gui_sketch_drag_state_init ( &((*this_).drag_state) );
57 0 : gui_sketch_card_painter_init( &((*this_).card_overlay), resources, &((*this_).texture_downloader) );
58 0 : gui_sketch_background_init( &((*this_).background), resources, &((*this_).texture_downloader) );
59 0 : gui_sketch_object_creator_init ( &((*this_).object_creator), controller, db_reader, message_to_user );
60 :
61 : /* connect draw/update and mouse move and key signals to the controllers of this widget */
62 0 : gtk_drawing_area_set_draw_func( GTK_DRAWING_AREA((*this_).drawing_area),
63 : (GtkDrawingAreaDrawFunc) gui_sketch_area_draw_callback,
64 : this_,
65 : NULL
66 : );
67 0 : gtk_widget_set_sensitive( (*this_).drawing_area, true );
68 0 : gtk_widget_set_can_focus( (*this_).drawing_area, true );
69 0 : gtk_widget_set_focusable( (*this_).drawing_area, true );
70 0 : gtk_widget_set_can_target( (*this_).drawing_area, true );
71 0 : gtk_widget_set_focus_on_click( (*this_).drawing_area, true );
72 :
73 0 : GtkEventController *evt_move = gtk_event_controller_motion_new();
74 0 : g_signal_connect( evt_move, "enter", G_CALLBACK(gui_sketch_area_motion_notify_callback), this_ );
75 0 : g_signal_connect( evt_move, "motion", G_CALLBACK(gui_sketch_area_motion_notify_callback), this_ );
76 0 : g_signal_connect( evt_move, "leave", G_CALLBACK(gui_sketch_area_leave_notify_callback), this_ );
77 0 : gtk_widget_add_controller( (*this_).drawing_area, GTK_EVENT_CONTROLLER(evt_move) );
78 :
79 0 : GtkEventController *evt_key = gtk_event_controller_key_new();
80 0 : g_signal_connect( evt_key, "key-pressed", G_CALLBACK(gui_sketch_area_key_press_callback), this_ );
81 : /*
82 : g_signal_connect( evt_key, "modifiers", G_CALLBACK(gui_sketch_area_key_callback), this_ );
83 : g_signal_connect( evt_key, "key-released", G_CALLBACK(gui_sketch_area_key_release_callback), this_ );
84 : */
85 0 : gtk_widget_add_controller( (*this_).drawing_area, GTK_EVENT_CONTROLLER(evt_key) );
86 :
87 0 : GtkGesture *evt_button = gtk_gesture_click_new();
88 0 : g_signal_connect( evt_button, "pressed", G_CALLBACK(gui_sketch_area_button_press_callback), this_ );
89 0 : g_signal_connect( evt_button, "released", G_CALLBACK(gui_sketch_area_button_release_callback), this_ );
90 0 : gtk_widget_add_controller( (*this_).drawing_area, GTK_EVENT_CONTROLLER(evt_button) );
91 :
92 : /* fetch initial data from the database */
93 0 : gui_sketch_area_show_diagram( this_, DATA_ID_VOID );
94 :
95 0 : U8_TRACE_END();
96 0 : }
97 :
98 0 : void gui_sketch_area_destroy( gui_sketch_area_t *this_ )
99 : {
100 0 : U8_TRACE_BEGIN();
101 :
102 : /* destroy instances of own objects */
103 0 : gui_sketch_result_list_destroy( &((*this_).result_list) );
104 0 : gui_sketch_nav_tree_destroy( &((*this_).nav_tree) );
105 :
106 : /* destroy all cards */
107 0 : data_small_set_destroy( &((*this_).card_draw_list) );
108 0 : for ( int idx = 0; idx < (*this_).card_num; idx ++ )
109 : {
110 0 : gui_sketch_card_destroy( &((*this_).cards[idx]) );
111 : }
112 0 : (*this_).card_num = 0;
113 :
114 : /* destroy instances of own objects */
115 0 : gui_sketch_object_creator_destroy ( &((*this_).object_creator) );
116 0 : gui_sketch_card_painter_destroy( &((*this_).card_overlay) );
117 0 : gui_sketch_background_destroy( &((*this_).background) );
118 0 : gui_sketch_drag_state_destroy ( &((*this_).drag_state) );
119 0 : gui_sketch_texture_destroy( &((*this_).texture_downloader) );
120 :
121 : /* destroy instance of requested tool-mode and diagram-ids */
122 0 : gui_sketch_request_destroy( &((*this_).request) );
123 :
124 : /* unset pointers to external objects */
125 0 : (*this_).drawing_area = NULL;
126 0 : (*this_).marker = NULL;
127 0 : (*this_).search_runner = NULL;
128 0 : (*this_).toolbox = NULL;
129 0 : (*this_).message_to_user = NULL;
130 0 : (*this_).resources = NULL;
131 0 : (*this_).db_reader = NULL;
132 0 : (*this_).controller = NULL;
133 :
134 0 : U8_TRACE_END();
135 0 : }
136 :
137 0 : void gui_sketch_area_show_result_list ( gui_sketch_area_t *this_, gui_search_runner_t *search_runner )
138 : {
139 0 : U8_TRACE_BEGIN();
140 0 : assert( NULL != search_runner );
141 :
142 0 : const pos_scroll_page_t *const requested_page = gui_search_runner_get_page_request( search_runner );
143 0 : const data_search_result_list_t *const result_list = gui_search_runner_get_result_list( search_runner );
144 0 : const uint32_t result_buffer_start = gui_search_runner_get_result_buffer_start( search_runner );
145 0 : const bool result_buffer_more_after = gui_search_runner_get_result_buffer_more_after( search_runner );
146 0 : data_search_result_list_trace(result_list);
147 0 : const uint_fast32_t result_buffer_length = data_search_result_list_get_length( result_list );
148 :
149 : /* do not fully trust the consistency between requested_page and result_list. */
150 : /* These information come from different sources. */
151 0 : pos_scroll_page_trace( requested_page );
152 0 : const int_fast32_t requested_anchor_idx = pos_scroll_page_get_anchor_index( requested_page );
153 0 : const bool valid_anchor
154 0 : = (( result_buffer_start <= requested_anchor_idx )&&( requested_anchor_idx < ( result_buffer_start + result_buffer_length )));
155 0 : assert( valid_anchor || ( result_buffer_length == 0 ) );
156 0 : const int_fast32_t anchor_idx = valid_anchor ? requested_anchor_idx : result_buffer_start;
157 0 : const bool requested_backwards = pos_scroll_page_get_backwards( requested_page );
158 0 : const bool backwards = valid_anchor ? requested_backwards : false;
159 :
160 : /* copy non-duplicate diagram ids to request list */
161 0 : data_small_set_t* requested_diagrams = gui_sketch_request_get_search_result_diagrams_ptr( &((*this_).request) );
162 0 : data_small_set_clear( requested_diagrams );
163 0 : unsigned int dropped_duplicates = 0;
164 0 : unsigned int dropped_too_many = 0;
165 0 : if ( backwards )
166 : {
167 0 : for ( int32_t buffer_idx = anchor_idx - result_buffer_start; buffer_idx >= 0; buffer_idx -- )
168 : {
169 0 : const data_search_result_t *diag_rec = data_search_result_list_get_const( result_list, buffer_idx );
170 0 : const data_id_t diag_id = data_search_result_get_diagram_id( diag_rec );
171 0 : const u8_error_t d_err = data_small_set_add_obj( requested_diagrams, diag_id );
172 0 : if ( d_err == U8_ERROR_DUPLICATE_ID )
173 : {
174 0 : dropped_duplicates ++;
175 : }
176 0 : else if ( d_err == U8_ERROR_ARRAY_BUFFER_EXCEEDED )
177 : {
178 0 : dropped_too_many ++;
179 : }
180 : }
181 : }
182 : else
183 : {
184 0 : for ( uint32_t buffer_idx = 0; buffer_idx < result_buffer_length; buffer_idx ++ )
185 : {
186 0 : const data_search_result_t *diag_rec = data_search_result_list_get_const( result_list, buffer_idx );
187 0 : const data_id_t diag_id = data_search_result_get_diagram_id( diag_rec );
188 0 : const u8_error_t d_err = data_small_set_add_obj( requested_diagrams, diag_id );
189 0 : if ( d_err == U8_ERROR_DUPLICATE_ID )
190 : {
191 0 : dropped_duplicates ++;
192 : }
193 0 : else if ( d_err == U8_ERROR_ARRAY_BUFFER_EXCEEDED )
194 : {
195 0 : dropped_too_many ++;
196 : }
197 : }
198 : }
199 0 : if ( (dropped_duplicates + dropped_too_many) > 0 )
200 : {
201 0 : U8_TRACE_INFO_INT_INT( "dropped_duplicates, dropped_too_many:", dropped_duplicates, dropped_too_many );
202 : }
203 :
204 : /* load new data */
205 0 : gui_sketch_area_private_load_cards_data ( this_ );
206 :
207 : /* load new data in subwidgets */
208 0 : gui_sketch_result_list_load_data( &((*this_).result_list),
209 : requested_page,
210 : result_buffer_start,
211 : result_list,
212 : result_buffer_more_after,
213 : (*this_).db_reader
214 : );
215 :
216 : /* notify listener */
217 0 : gui_marked_set_clear_focused( (*this_).marker );
218 0 : gui_marked_set_clear_selected_set( (*this_).marker );
219 :
220 : /* mark dirty rect */
221 0 : gtk_widget_queue_draw( (*this_).drawing_area );
222 :
223 0 : U8_TRACE_END();
224 0 : }
225 :
226 0 : void gui_sketch_area_draw_callback( GtkDrawingArea *widget, cairo_t *cr, int width, int height, gpointer data )
227 : {
228 0 : U8_TRACE_BEGIN();
229 0 : U8_TRACE_TIMESTAMP();
230 0 : assert( NULL != cr );
231 0 : gui_sketch_area_t *this_ = data;
232 0 : assert( NULL != this_ );
233 0 : assert( GTK_WIDGET(widget) == (*this_).drawing_area );
234 :
235 0 : gui_sketch_area_draw( this_, width, height, cr );
236 :
237 0 : U8_TRACE_END();
238 0 : }
239 :
240 0 : void gui_sketch_area_draw( gui_sketch_area_t *this_, int width, int height, cairo_t *cr )
241 : {
242 0 : U8_TRACE_BEGIN();
243 0 : assert( NULL != cr );
244 :
245 0 : if ( ! data_database_reader_is_open( (*this_).db_reader ) )
246 : {
247 : shape_int_rectangle_t card_bounds;
248 0 : shape_int_rectangle_init( &card_bounds, 0, 0, width, height );
249 : shape_int_rectangle_t label_bounds;
250 0 : shape_int_rectangle_init( &label_bounds, 0, 0, 0, height );
251 0 : gui_sketch_background_set_bounds( &((*this_).background), &card_bounds, &label_bounds );
252 0 : gui_sketch_background_draw_introduction( &((*this_).background), cr );
253 : }
254 : else
255 : {
256 : shape_int_rectangle_t bounds;
257 0 : shape_int_rectangle_init( &bounds, 0, 0, width, height );
258 0 : if ( ! gui_sketch_drag_state_is_dragging ( &((*this_).drag_state) ) )
259 : {
260 0 : gui_sketch_area_private_layout_subwidgets( this_, bounds, cr );
261 : }
262 0 : gui_sketch_area_private_draw_subwidgets( this_, bounds, cr );
263 : }
264 :
265 0 : U8_TRACE_END();
266 0 : }
267 :
268 0 : void gui_sketch_area_show_diagram ( gui_sketch_area_t *this_, data_id_t main_diagram_id )
269 : {
270 0 : U8_TRACE_BEGIN();
271 :
272 0 : data_id_trace( &main_diagram_id );
273 : const uint32_t src_results_cnt
274 0 : = data_small_set_get_count( gui_sketch_request_get_search_result_diagrams_const( &((*this_).request) ) );
275 0 : U8_TRACE_INFO_INT( "src_results_cnt:", src_results_cnt );
276 :
277 : /* determine diagram id of root diagram */
278 0 : if ( ! data_id_is_valid( &main_diagram_id ) )
279 : {
280 : /* load all without parent */
281 : data_small_set_t roots;
282 0 : data_small_set_init( &roots );
283 : const u8_error_t db_err
284 0 : = data_database_reader_get_diagram_ids_by_parent_id( (*this_).db_reader,
285 : DATA_ROW_VOID,
286 : &roots
287 : );
288 0 : const uint32_t count = data_small_set_get_count( &roots );
289 0 : if ( u8_error_contains( db_err, U8_ERROR_NO_DB ) )
290 : {
291 0 : U8_TRACE_INFO( "database not open.");
292 : }
293 0 : else if ( U8_ERROR_NONE != db_err )
294 : {
295 0 : U8_LOG_ERROR_HEX( "data_database_reader_get_diagrams_by_parent_id failed.", db_err );
296 : }
297 0 : else if ( count > 1 )
298 : {
299 0 : U8_LOG_ERROR_INT( "more than one root diagram exists!", count );
300 : }
301 0 : else if ( count < 1 )
302 : {
303 0 : U8_TRACE_INFO( "no root diagram exists!" );
304 : }
305 : else
306 : {
307 0 : main_diagram_id = data_small_set_get_id( &roots, 0 );
308 0 : U8_TRACE_INFO_INT( "main_diagram_id:", data_id_get_row_id( &main_diagram_id ));
309 : }
310 :
311 : /* cleanup */
312 0 : data_small_set_destroy( &roots );
313 : }
314 :
315 : /* store request */
316 0 : gui_sketch_request_set_focused_diagram( &((*this_).request), main_diagram_id );
317 :
318 : /* load data to be drawn */
319 0 : gui_sketch_area_private_load_cards_data ( this_ );
320 :
321 0 : U8_TRACE_END();
322 0 : }
323 :
324 0 : void gui_sketch_area_private_refocus_and_reload_data ( gui_sketch_area_t *this_ )
325 : {
326 0 : U8_TRACE_BEGIN();
327 :
328 : /* determine currently selected diagram id and parent id from cache for emergency-fallback */
329 0 : const data_id_t former_diagram_id = gui_sketch_request_get_focused_diagram( &((*this_).request) );
330 0 : const data_id_t former_parent_diagram_id = gui_sketch_request_get_parent_diagram( &((*this_).request) );
331 :
332 : /* reload diagram data */
333 0 : gui_sketch_area_show_diagram( this_, former_diagram_id );
334 :
335 0 : if ( GUI_TOOL_SEARCH != gui_sketch_request_get_tool_mode( &((*this_).request) ) )
336 : {
337 0 : if ( data_id_is_valid( &former_diagram_id )
338 0 : &&( DATA_ROW_VOID == gui_sketch_area_private_get_focused_diagram_id( this_ ) ))
339 : {
340 : /* the requested diagram was not loaded, try the parent: */
341 0 : gui_sketch_area_show_diagram( this_, former_parent_diagram_id );
342 :
343 0 : if ( data_id_is_valid( &former_parent_diagram_id )
344 0 : &&( DATA_ROW_VOID == gui_sketch_area_private_get_focused_diagram_id( this_ ) ))
345 : {
346 : /* the requested diagram was not loaded, go back to root diagram: */
347 0 : gui_sketch_area_show_diagram( this_, DATA_ID_VOID );
348 : }
349 :
350 : /* clear the selected set */
351 0 : gui_marked_set_clear_selected_set( (*this_).marker );
352 : }
353 : }
354 :
355 0 : U8_TRACE_END();
356 0 : }
357 :
358 0 : void gui_sketch_area_private_load_cards_data ( gui_sketch_area_t *this_ )
359 : {
360 0 : U8_TRACE_BEGIN();
361 :
362 : /* destroy _all_ old cards */
363 0 : data_small_set_reinit( &((*this_).card_draw_list) );
364 0 : for ( uint_fast32_t idx = 0; idx < (*this_).card_num; idx ++ )
365 : {
366 0 : gui_sketch_card_destroy( &((*this_).cards[idx]) );
367 : }
368 0 : (*this_).card_num = 0;
369 :
370 : /* load new cards */
371 0 : switch ( gui_sketch_request_get_tool_mode( &((*this_).request) ) )
372 : {
373 0 : case GUI_TOOL_SEARCH:
374 : {
375 : const data_small_set_t* requested_diagrams
376 0 : = gui_sketch_request_get_search_result_diagrams_const( &((*this_).request) );
377 : /* Note: the list of requested diagrams from the search page may be larger than what can be displayed */
378 :
379 0 : const uint_fast32_t d_count = data_small_set_get_count( requested_diagrams );
380 0 : for ( uint_fast32_t index = 0; index < d_count; index ++ )
381 : {
382 0 : const data_id_t diag_id = data_small_set_get_id( requested_diagrams, index );
383 0 : if ( (*this_).card_num < GUI_SKETCH_AREA_CONST_MAX_CARDS )
384 : {
385 0 : gui_sketch_card_init( &((*this_).cards[(*this_).card_num]) );
386 0 : gui_sketch_card_load_data( &((*this_).cards[(*this_).card_num]), diag_id, (*this_).db_reader );
387 0 : if ( gui_sketch_card_is_valid( &((*this_).cards[(*this_).card_num]) ) )
388 : {
389 0 : (*this_).card_num ++;
390 : }
391 : else
392 : {
393 0 : U8_TRACE_INFO_INT( "could not load diagram:", data_id_get_row_id(&diag_id) );
394 : }
395 : }
396 : else
397 : {
398 0 : U8_TRACE_INFO_INT( "max diagrams exeeded, dropping diagram:", data_id_get_row_id(&diag_id) );
399 : }
400 : }
401 : }
402 0 : break;
403 :
404 0 : default:
405 : {
406 0 : const data_id_t main_diagram_id = gui_sketch_request_get_focused_diagram( &((*this_).request) );
407 :
408 0 : gui_sketch_card_init( &((*this_).cards[GUI_SKETCH_AREA_CONST_FOCUSED_CARD]) );
409 0 : gui_sketch_card_load_data( &((*this_).cards[GUI_SKETCH_AREA_CONST_FOCUSED_CARD]), main_diagram_id, (*this_).db_reader );
410 0 : (*this_).card_num = 1;
411 0 : gui_sketch_nav_tree_load_data( &((*this_).nav_tree), data_id_get_row_id( &main_diagram_id ), (*this_).db_reader );
412 :
413 : /* determine ids */
414 0 : const data_diagram_t *selected_diag = gui_sketch_area_private_get_focused_diagram_ptr( this_ );
415 0 : const data_row_t selected_diagram_row_id = data_diagram_get_row_id( selected_diag );
416 0 : const data_id_t selected_diagram_id = data_diagram_get_data_id( selected_diag );
417 0 : U8_TRACE_INFO_INT( "selected_diagram_row_id:", selected_diagram_row_id );
418 0 : const data_id_t parent_diagram_id = data_diagram_get_parent_data_id( selected_diag );
419 0 : U8_TRACE_INFO_INT( "parent_diagram_id:", data_id_get_row_id( &parent_diagram_id ) );
420 :
421 0 : const data_id_t former_focused_diag = gui_marked_set_get_focused_diagram( (*this_).marker);
422 0 : gui_sketch_request_set_parent_diagram( &((*this_).request), parent_diagram_id );
423 0 : if ( ! data_id_equals_or_both_void( &former_focused_diag, &selected_diagram_id ) )
424 : {
425 : /* clear focused but keep selected_diagram_id, needed for gui_toolbox_paste */
426 0 : gui_marked_set_set_focused( (*this_).marker, DATA_FULL_ID_VOID, selected_diagram_id );
427 : }
428 : else
429 : {
430 : /* DO NOT NOTIFY CHANGES IN A POSSIBLE DATA CHANGE CALLBACK - MAY CAUSE ENDLESS RECURSION */
431 : }
432 :
433 : const gui_tool_t selected_tool
434 0 : = gui_sketch_request_get_tool_mode( &((*this_).request) );
435 0 : if ( GUI_TOOL_NAVIGATE == selected_tool )
436 : {
437 :
438 : /* load parent even if there is no parent (-->VOID) */
439 0 : gui_sketch_card_init( &((*this_).cards[GUI_SKETCH_AREA_CONST_PARENT_CARD]) );
440 0 : gui_sketch_card_load_data( &((*this_).cards[GUI_SKETCH_AREA_CONST_PARENT_CARD]), parent_diagram_id, (*this_).db_reader );
441 0 : (*this_).card_num = 2;
442 :
443 : /* load all children (up to GUI_SKETCH_AREA_CONST_MAX_TEMP_DIAGRAMS)*/
444 : data_small_set_t children;
445 0 : data_small_set_init( &children );
446 : const u8_error_t db_err
447 0 : = data_database_reader_get_diagram_ids_by_parent_id( (*this_).db_reader,
448 : selected_diagram_row_id,
449 : &children
450 : );
451 0 : if ( u8_error_contains( db_err, U8_ERROR_NO_DB ) )
452 : {
453 0 : U8_TRACE_INFO( "database not open.");
454 : }
455 0 : else if ( U8_ERROR_NONE != db_err )
456 : {
457 0 : U8_LOG_ERROR_HEX( "data_database_reader_get_diagram_ids_by_parent_id failed.", db_err );
458 : }
459 : else
460 : {
461 0 : for ( uint_fast32_t index = 0; index < data_small_set_get_count( &children ); index ++ )
462 : {
463 0 : const data_id_t child = data_small_set_get_id( &children, index );
464 0 : if ( (*this_).card_num < GUI_SKETCH_AREA_CONST_MAX_CARDS )
465 : {
466 0 : gui_sketch_card_init( &((*this_).cards[(*this_).card_num]) );
467 0 : gui_sketch_card_load_data( &((*this_).cards[(*this_).card_num]), child, (*this_).db_reader );
468 0 : (*this_).card_num ++;
469 : }
470 : else
471 : {
472 0 : U8_LOG_ERROR_INT( "more children diagrams exist than fit into cards array:", data_id_get_row_id( &child ) );
473 : }
474 : }
475 : }
476 : /* cleanup */
477 0 : data_small_set_destroy( &children );
478 : }
479 :
480 : }
481 0 : break;
482 : }
483 :
484 0 : U8_TRACE_END();
485 0 : }
486 :
487 : static const uint32_t NAV_TREE_WIDTH = 224;
488 : static const uint32_t RESULT_LIST_WIDTH = 240;
489 :
490 0 : void gui_sketch_area_private_layout_subwidgets ( gui_sketch_area_t *this_, shape_int_rectangle_t area_bounds, cairo_t *cr )
491 : {
492 0 : U8_TRACE_BEGIN();
493 0 : assert( NULL != cr );
494 0 : assert((*this_).card_num <= GUI_SKETCH_AREA_CONST_MAX_CARDS);
495 :
496 : gui_tool_t selected_tool;
497 0 : selected_tool = gui_sketch_request_get_tool_mode( &((*this_).request) );
498 :
499 : /* reset the list of cards to be shown */
500 0 : data_small_set_reinit( &((*this_).card_draw_list) );
501 :
502 : /* fetch area bounds */
503 0 : const uint32_t width = shape_int_rectangle_get_width( &area_bounds );
504 0 : const uint32_t height = shape_int_rectangle_get_height( &area_bounds );
505 0 : const int32_t left = shape_int_rectangle_get_left( &area_bounds );
506 0 : const int32_t top = shape_int_rectangle_get_top( &area_bounds );
507 0 : U8_TRACE_INFO_INT_INT( "width, height", width, height );
508 :
509 : /* layout result list */
510 0 : const bool result_list_visible = ( GUI_TOOL_SEARCH == selected_tool );
511 : {
512 : shape_int_rectangle_t result_list_bounds;
513 0 : shape_int_rectangle_init( &result_list_bounds, left, top, RESULT_LIST_WIDTH, height );
514 0 : gui_sketch_result_list_set_bounds( &((*this_).result_list ), result_list_bounds );
515 0 : gui_sketch_result_list_set_visible( &((*this_).result_list), result_list_visible );
516 0 : if ( result_list_visible )
517 : {
518 0 : gui_sketch_result_list_do_layout( &((*this_).result_list), cr );
519 0 : gui_sketch_result_list_get_visible_diagrams( &((*this_).result_list), &((*this_).card_draw_list) );
520 : }
521 : }
522 :
523 : /* layout nav tree */
524 0 : const bool nav_tree_visible = ( GUI_TOOL_NAVIGATE == selected_tool );
525 : {
526 : shape_int_rectangle_t nav_tree_bounds;
527 0 : shape_int_rectangle_init( &nav_tree_bounds, left, top, NAV_TREE_WIDTH, height );
528 0 : gui_sketch_nav_tree_set_bounds( &((*this_).nav_tree), nav_tree_bounds );
529 0 : gui_sketch_nav_tree_set_visible( &((*this_).nav_tree), nav_tree_visible );
530 0 : if ( nav_tree_visible )
531 : {
532 0 : gui_sketch_nav_tree_do_layout( &((*this_).nav_tree), cr );
533 : }
534 : }
535 :
536 : /* layout cards */
537 0 : const int32_t cards_left = left + (nav_tree_visible ? NAV_TREE_WIDTH : (result_list_visible ? RESULT_LIST_WIDTH : 0 ));
538 0 : const uint32_t cards_width = (width > (cards_left-left)) ? (width - (cards_left-left)) : 0;
539 : {
540 : shape_int_rectangle_t cards_bounds;
541 0 : shape_int_rectangle_init( &cards_bounds, cards_left, top, cards_width, height );
542 : gui_sketch_card_layouter_t cards_layouter;
543 0 : gui_sketch_card_layouter_init( &cards_layouter, &cards_bounds );
544 0 : gui_sketch_card_layouter_layout( &cards_layouter, selected_tool, &((*this_).cards[0]), (*this_).card_num, &((*this_).card_draw_list), cr );
545 0 : gui_sketch_card_layouter_destroy( &cards_layouter );
546 : }
547 :
548 : /* layout background */
549 : {
550 : shape_int_rectangle_t card_ground_bounds;
551 0 : shape_int_rectangle_init( &card_ground_bounds, cards_left, top, cards_width, height );
552 : shape_int_rectangle_t label_list_bounds;
553 0 : shape_int_rectangle_init( &label_list_bounds, left, top, cards_left-left, height );
554 0 : gui_sketch_background_set_bounds( &((*this_).background), &card_ground_bounds, &label_list_bounds );
555 : }
556 :
557 0 : U8_TRACE_END();
558 0 : }
559 :
560 0 : void gui_sketch_area_private_draw_subwidgets ( gui_sketch_area_t *this_, shape_int_rectangle_t area_bounds, cairo_t *cr )
561 : {
562 0 : U8_TRACE_BEGIN();
563 0 : assert( NULL != cr );
564 :
565 0 : const gui_tool_t selected_tool = gui_sketch_request_get_tool_mode( &((*this_).request) );
566 :
567 : /* draw background */
568 0 : switch( selected_tool )
569 : {
570 0 : case GUI_TOOL_SEARCH:
571 : {
572 0 : gui_sketch_background_draw_search( &((*this_).background), cr );
573 :
574 : /* draw link from highlighted search result to diagram */
575 0 : const data_id_t high_obj = gui_marked_set_get_highlighted ( (*this_).marker );
576 0 : const data_id_t high_diag = gui_marked_set_get_highlighted_diagram ( (*this_).marker );
577 : shape_int_rectangle_t search_envelope_box;
578 : const u8_error_t search_found
579 0 : = gui_sketch_result_list_get_result_envelope( &((*this_).result_list),
580 : &high_obj,
581 : &high_diag,
582 : &search_envelope_box
583 : );
584 : const gui_sketch_card_t* card;
585 0 : const u8_error_t card_found = gui_sketch_area_private_get_card_of_id( this_, &high_diag, &card );
586 0 : if (( search_found == U8_ERROR_NONE )&&( card_found == U8_ERROR_NONE ))
587 : {
588 0 : const shape_int_rectangle_t card_box = gui_sketch_card_get_bounds( card );
589 0 : gui_sketch_background_draw_appears_link( &((*this_).background), &search_envelope_box, &card_box, cr );
590 : }
591 : }
592 0 : break;
593 :
594 0 : case GUI_TOOL_NAVIGATE:
595 : {
596 0 : const unsigned int depth
597 0 : = ( gui_sketch_card_is_valid( &((*this_).cards[GUI_SKETCH_AREA_CONST_PARENT_CARD]) ) ) ? 1 : 0; /* currently, only root and non-root can be distinguished */
598 0 : const unsigned int children
599 0 : = (*this_).card_num-2;
600 0 : gui_sketch_background_draw_navigation( &((*this_).background), depth, children, cr );
601 :
602 : /* draw link from highlighted nav_tree_node to diagram */
603 0 : const data_id_t highlighted = gui_marked_set_get_highlighted ( (*this_).marker );
604 : shape_int_rectangle_t nav_envelope_box;
605 : const u8_error_t nav_found
606 0 : = gui_sketch_nav_tree_get_node_envelope( &((*this_).nav_tree), &highlighted, &nav_envelope_box );
607 : const gui_sketch_card_t* card;
608 0 : const u8_error_t card_found = gui_sketch_area_private_get_card_of_id( this_, &highlighted, &card );
609 0 : if (( nav_found == U8_ERROR_NONE )&&( card_found == U8_ERROR_NONE ))
610 : {
611 0 : const shape_int_rectangle_t card_box = gui_sketch_card_get_bounds( card );
612 0 : gui_sketch_background_draw_appears_link( &((*this_).background), &nav_envelope_box, &card_box, cr );
613 : }
614 : }
615 0 : break;
616 :
617 0 : case GUI_TOOL_EDIT:
618 : {
619 0 : gui_sketch_background_draw_edit( &((*this_).background), cr );
620 : }
621 0 : break;
622 :
623 0 : case GUI_TOOL_CREATE:
624 : {
625 0 : gui_sketch_background_draw_create( &((*this_).background), cr );
626 : }
627 0 : break;
628 :
629 0 : default:
630 : {
631 0 : assert(false);
632 : }
633 : break;
634 : }
635 :
636 : /* draw result list */
637 : {
638 0 : gui_sketch_result_list_draw( &((*this_).result_list), (*this_).marker, cr );
639 : }
640 :
641 : /* draw nav tree */
642 : {
643 0 : gui_sketch_nav_tree_draw( &((*this_).nav_tree), (*this_).marker, cr );
644 : }
645 :
646 : /* draw all cards, backwards */
647 0 : for ( signed int card_idx = (*this_).card_num-1; card_idx >= 0; card_idx -- )
648 : {
649 0 : gui_sketch_card_t *card = &((*this_).cards[card_idx]);
650 : /* lazy layouting: only re-calulate positions when these are needed: */
651 0 : if ( gui_sketch_card_needs_layout( card ) )
652 : {
653 0 : gui_sketch_card_layout_elements( card, cr );
654 : }
655 :
656 0 : gui_sketch_card_painter_draw_paper( &((*this_).card_overlay),
657 : selected_tool,
658 0 : &((*this_).drag_state),
659 : card,
660 : cr
661 : );
662 0 : gui_sketch_card_draw( card, (*this_).marker, cr );
663 : }
664 :
665 : /* overlay tool-helper lines */
666 0 : const int32_t mouse_x = gui_sketch_drag_state_get_to_x( &((*this_).drag_state) );
667 0 : const int32_t mouse_y = gui_sketch_drag_state_get_to_y( &((*this_).drag_state) );
668 0 : gui_sketch_card_painter_draw_overlay( &((*this_).card_overlay),
669 : selected_tool,
670 0 : &((*this_).drag_state),
671 0 : gui_sketch_area_private_get_card_at_pos ( this_, mouse_x, mouse_y ),
672 : (*this_).marker,
673 : cr
674 : );
675 0 : gui_sketch_nav_tree_draw_overlay( &((*this_).nav_tree), &((*this_).drag_state) , cr );
676 :
677 0 : U8_TRACE_END();
678 0 : }
679 :
680 0 : void gui_sketch_area_leave_notify_callback( GtkEventControllerMotion* self, gpointer user_data )
681 : {
682 0 : U8_TRACE_BEGIN();
683 0 : U8_TRACE_TIMESTAMP();
684 0 : gui_sketch_area_t *this_ = user_data;
685 0 : assert( NULL != this_ );
686 :
687 0 : gui_marked_set_clear_highlighted( (*this_).marker );
688 : /* mark dirty rect */
689 0 : gtk_widget_queue_draw( (*this_).drawing_area );
690 :
691 0 : U8_TRACE_END();
692 0 : }
693 :
694 0 : void gui_sketch_area_motion_notify_callback( GtkEventControllerMotion* self,
695 : gdouble in_x,
696 : gdouble in_y,
697 : gpointer user_data )
698 : {
699 0 : U8_TRACE_BEGIN();
700 0 : gui_sketch_area_t *this_ = user_data;
701 0 : assert( NULL != this_ );
702 :
703 0 : gui_sketch_area_motion_notify( this_, (int)in_x, (int)in_y );
704 :
705 0 : U8_TRACE_END();
706 0 : }
707 :
708 0 : void gui_sketch_area_motion_notify( gui_sketch_area_t *this_, int x, int y )
709 : {
710 0 : U8_TRACE_BEGIN();
711 0 : U8_TRACE_INFO_INT_INT( "x/y", x, y );
712 :
713 : /* update drag coordinates */
714 0 : gui_sketch_drag_state_set_to ( &((*this_).drag_state), x, y );
715 :
716 : /* do highlight */
717 0 : const gui_tool_t selected_tool = gui_sketch_request_get_tool_mode( &((*this_).request) );
718 0 : switch ( selected_tool )
719 : {
720 0 : case GUI_TOOL_SEARCH: /* or */
721 : case GUI_TOOL_NAVIGATE:
722 : {
723 0 : if ( gui_sketch_drag_state_is_dragging ( &((*this_).drag_state) ) )
724 : {
725 : /* always redraw while dragging */
726 0 : gtk_widget_queue_draw( (*this_).drawing_area );
727 : }
728 : else /* not dragging */
729 : {
730 0 : data_id_t object_under_mouse = DATA_ID_VOID;
731 0 : data_id_t diag_under_mouse = DATA_ID_VOID;
732 0 : gui_sketch_area_private_get_diagram_and_object_id_at_pos ( this_, x, y, &diag_under_mouse, &object_under_mouse );
733 0 : gui_sketch_action_t btn_under_mouse = GUI_SKETCH_ACTION_NONE;
734 0 : if ( selected_tool == GUI_TOOL_NAVIGATE )
735 : {
736 0 : gui_sketch_nav_tree_get_button_at_pos( &((*this_).nav_tree), x, y, &btn_under_mouse );
737 : }
738 : else
739 : {
740 0 : gui_sketch_result_list_get_button_at_pos( &((*this_).result_list), x, y, &btn_under_mouse );
741 : }
742 :
743 : const data_id_t object_highlighted
744 0 : = gui_marked_set_get_highlighted( (*this_).marker );
745 : const data_id_t diag_highlighted
746 0 : = gui_marked_set_get_highlighted_diagram( (*this_).marker );
747 : const gui_sketch_action_t btn_highlighted
748 0 : = gui_marked_set_get_highlighted_button( (*this_).marker );
749 0 : const bool obj_both_void
750 0 : = ( ! data_id_is_valid( &object_under_mouse ) )&&( ! data_id_is_valid( &object_highlighted ) );
751 0 : const bool obj_equal_or_both_void
752 0 : = data_id_equals( &object_under_mouse, &object_highlighted ) || obj_both_void;
753 0 : const bool diag_both_void
754 0 : = ( ! data_id_is_valid( &diag_under_mouse ) )&&( ! data_id_is_valid( &diag_highlighted ) );
755 0 : const bool diag_equal_or_both_void
756 0 : = data_id_equals( &diag_under_mouse, &diag_highlighted ) || diag_both_void;
757 0 : const bool btn_changed = ( btn_under_mouse != btn_highlighted );
758 0 : if (( btn_under_mouse != GUI_SKETCH_ACTION_NONE )&&( btn_changed ))
759 : {
760 : /* highlighted button is activated and has changed */
761 0 : gui_marked_set_set_highlighted_button( (*this_).marker, btn_under_mouse );
762 :
763 : /* mark dirty rect */
764 0 : gtk_widget_queue_draw( (*this_).drawing_area );
765 : }
766 0 : else if (( ! obj_equal_or_both_void )||( ! diag_equal_or_both_void )||( btn_changed ))
767 : {
768 : /* highlight changed */
769 0 : gui_marked_set_set_highlighted( (*this_).marker, object_under_mouse, diag_under_mouse );
770 :
771 : /* mark dirty rect */
772 0 : gtk_widget_queue_draw( (*this_).drawing_area );
773 : }
774 : }
775 : }
776 0 : break;
777 :
778 0 : case GUI_TOOL_EDIT:
779 : {
780 0 : if ( gui_sketch_drag_state_is_dragging ( &((*this_).drag_state) ) )
781 : {
782 : /* what is dragged? */
783 : const data_full_id_t *const dragged_object
784 0 : = gui_sketch_drag_state_get_dragged_object_ptr ( &((*this_).drag_state) );
785 : const data_id_t dragged_element
786 0 : = data_full_id_get_primary_id( dragged_object );
787 : const data_id_t dragged_classifier
788 0 : = data_full_id_get_secondary_id( dragged_object );
789 :
790 : /* what is the target location? */
791 0 : gui_sketch_card_t *target_card = gui_sketch_area_private_get_card_at_pos ( this_, x, y );
792 0 : if ( NULL != target_card )
793 : {
794 : /* mark again - in case the marker was lost when mouse was outside window */
795 0 : const data_id_t diag_id = gui_sketch_card_get_diagram_id( target_card );
796 0 : gui_marked_set_set_highlighted( (*this_).marker, dragged_element, diag_id );
797 :
798 : layout_order_t layout_order;
799 0 : if ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( &dragged_element ) )
800 : {
801 0 : layout_order = gui_sketch_card_get_order_at_pos( target_card, dragged_classifier, x, y );
802 0 : layout_order_trace( &layout_order );
803 0 : gui_sketch_card_move_object_to_order( target_card, dragged_classifier, &layout_order );
804 : }
805 : else
806 : {
807 0 : layout_order = gui_sketch_card_get_order_at_pos( target_card, dragged_element, x, y );
808 0 : layout_order_trace( &layout_order );
809 0 : gui_sketch_card_move_object_to_order( target_card, dragged_element, &layout_order );
810 : }
811 :
812 : /* mark dirty rect */
813 0 : gtk_widget_queue_draw( (*this_).drawing_area );
814 : }
815 : else
816 : {
817 0 : gui_marked_set_clear_highlighted( (*this_).marker );
818 0 : U8_TRACE_INFO( "mouse click outside sketch card." );
819 : }
820 : }
821 : else /* not dragging */
822 : {
823 : data_full_id_t object_under_mouse;
824 : data_id_t diag_id;
825 0 : gui_sketch_area_private_get_element_id_at_pos( this_,
826 : x,
827 : y,
828 : true /* FILTER_LIFELINE */,
829 : &object_under_mouse,
830 : &diag_id
831 : );
832 : const data_id_t object_highlighted
833 0 : = gui_marked_set_get_highlighted( (*this_).marker );
834 : const data_id_t mouseover_element
835 0 : = data_full_id_get_primary_id( &object_under_mouse );
836 0 : if ( ! data_id_equals( &mouseover_element, &object_highlighted ) )
837 : {
838 0 : if ( data_full_id_is_valid( &object_under_mouse ) || data_id_is_valid( &object_highlighted ) )
839 : {
840 : /* highlight changed */
841 0 : gui_marked_set_set_highlighted( (*this_).marker, mouseover_element, diag_id );
842 :
843 : /* mark dirty rect */
844 0 : gtk_widget_queue_draw( (*this_).drawing_area );
845 : }
846 : }
847 : }
848 : }
849 0 : break;
850 :
851 0 : case GUI_TOOL_CREATE:
852 : {
853 0 : const gui_sketch_card_t *const target_card = gui_sketch_area_private_get_card_at_pos ( this_, x, y );
854 0 : const layout_subelement_id_t element_id
855 : = ( NULL == target_card )
856 : ? LAYOUT_SUBELEMENT_ID_VOID
857 0 : : gui_sketch_card_get_element_at_pos( target_card, x, y, false /* FILTER_LIFELINE */ );
858 0 : const data_id_t diagram_id
859 : = ( NULL == target_card )
860 : ? DATA_ID_VOID
861 0 : : gui_sketch_card_get_diagram_id( target_card );
862 : const data_full_id_t *const element_under_mouse_full
863 0 : = layout_subelement_id_get_id_const( &element_id ); /* may return VOID */
864 : const layout_subelement_kind_t element_under_mouse_kind
865 0 : = layout_subelement_id_get_kind( &element_id ); /* may return VOID */
866 : const data_id_t element_under_mouse
867 0 : = data_full_id_get_primary_id( element_under_mouse_full );
868 :
869 : const data_id_t element_highlighted
870 0 : = gui_marked_set_get_highlighted( (*this_).marker );
871 : const layout_subelement_kind_t element_highlighted_kind
872 0 : = gui_marked_set_get_highlighted_kind( (*this_).marker );
873 0 : const bool highlighted_element_changed
874 0 : = ( ! data_id_equals( &element_under_mouse, &element_highlighted ) )
875 0 : || ( element_under_mouse_kind != element_highlighted_kind );
876 0 : if ( highlighted_element_changed )
877 : {
878 0 : if ( data_id_is_valid( &element_under_mouse ) || data_id_is_valid( &element_highlighted ) )
879 : {
880 0 : gui_marked_set_set_highlighted_subelement( (*this_).marker, &element_id, diagram_id );
881 :
882 : /* mark dirty rect */
883 0 : gtk_widget_queue_draw( (*this_).drawing_area );
884 : }
885 : }
886 0 : else if ( gui_sketch_drag_state_is_dragging ( &((*this_).drag_state) ) )
887 : {
888 : /* always redraw while dragging */
889 0 : gtk_widget_queue_draw( (*this_).drawing_area );
890 : }
891 : else
892 : {
893 : /* always redraw while moving the mouse to move the new-box-and-arrow icon */
894 0 : gtk_widget_queue_draw( (*this_).drawing_area );
895 : }
896 : }
897 0 : break;
898 :
899 0 : default:
900 : {
901 0 : U8_LOG_ERROR("selected_tool is out of range");
902 : }
903 0 : break;
904 : }
905 :
906 0 : U8_TRACE_END();
907 0 : }
908 :
909 0 : void gui_sketch_area_button_press_callback( GtkGestureClick* self,
910 : gint n_press,
911 : gdouble x,
912 : gdouble y,
913 : gpointer user_data )
914 : {
915 0 : U8_TRACE_BEGIN();
916 0 : U8_TRACE_TIMESTAMP();
917 0 : gui_sketch_area_t *this_ = user_data;
918 0 : assert( NULL != this_ );
919 :
920 0 : gui_sketch_area_button_press( this_, (int)x, (int)y );
921 :
922 0 : U8_TRACE_END();
923 0 : }
924 :
925 0 : void gui_sketch_area_button_press( gui_sketch_area_t *this_, int x, int y )
926 : {
927 0 : U8_TRACE_BEGIN();
928 :
929 : /* in general, hide the last message */
930 0 : gui_simple_message_to_user_hide( (*this_).message_to_user );
931 :
932 0 : U8_TRACE_INFO("press");
933 :
934 : /* cause the text edit widgets to lose the focus so that these can store the latest changes */
935 0 : gtk_widget_grab_focus( (*this_).drawing_area );
936 :
937 : /* get position */
938 0 : U8_TRACE_INFO_INT_INT( "x/y", x, y );
939 :
940 : /* check that drag state is false */
941 0 : if ( ( gui_sketch_drag_state_is_dragging( &((*this_).drag_state) ) )
942 0 : || ( gui_sketch_drag_state_is_waiting_for_move( &((*this_).drag_state) ) ) )
943 : {
944 0 : U8_LOG_ERROR("drag state indicates dragging - but button was not pressed before!");
945 : }
946 :
947 : /* update drag coordinates */
948 0 : gui_sketch_drag_state_set_from ( &((*this_).drag_state), x, y );
949 0 : gui_sketch_drag_state_set_to ( &((*this_).drag_state), x, y );
950 :
951 : /* do action */
952 0 : const gui_tool_t selected_tool = gui_sketch_request_get_tool_mode( &((*this_).request) );
953 0 : switch ( selected_tool )
954 : {
955 0 : case GUI_TOOL_NAVIGATE:
956 : {
957 0 : U8_TRACE_INFO( "GUI_TOOL_NAVIGATE" );
958 :
959 : /* determine clicked diagram */
960 0 : data_id_t clicked_diagram_id = DATA_ID_VOID;
961 0 : data_id_t clicked_object_id = DATA_ID_VOID;
962 0 : gui_sketch_area_private_get_diagram_and_object_id_at_pos ( this_, x, y, &clicked_diagram_id, &clicked_object_id );
963 0 : data_id_trace( &clicked_diagram_id );
964 0 : data_id_trace( &clicked_object_id );
965 :
966 : /* load diagram */
967 0 : if ( data_id_is_valid( &clicked_diagram_id ) )
968 : {
969 : /* update drag state */
970 : data_full_id_t dragged_object;
971 0 : data_full_id_init_solo ( &dragged_object, &clicked_diagram_id );
972 0 : gui_sketch_drag_state_start_dragging_when_move ( &((*this_).drag_state), dragged_object );
973 :
974 : /* if a diagram was clicked (not a button) then redraw to show the hint-to-drag */
975 0 : gtk_widget_queue_draw( (*this_).drawing_area );
976 : }
977 : else
978 : {
979 0 : U8_TRACE_INFO("invalid clicked object at gui_sketch_area_button_press_callback");
980 :
981 : gui_sketch_action_t action_button_id;
982 0 : gui_sketch_nav_tree_get_button_at_pos ( &((*this_).nav_tree), x, y, &action_button_id );
983 0 : if ( action_button_id != GUI_SKETCH_ACTION_NONE )
984 : {
985 : /* create a new diagram */
986 0 : u8_error_t c_result = U8_ERROR_NONE;
987 0 : data_row_t new_diag_id = DATA_ROW_VOID;
988 :
989 : {
990 : const data_diagram_t *selected_diag;
991 0 : selected_diag = gui_sketch_nav_tree_get_diagram_ptr ( &((*this_).nav_tree) );
992 0 : switch ( action_button_id )
993 : {
994 0 : case GUI_SKETCH_ACTION_NEW_SIBLING_DIAGRAM:
995 : {
996 0 : assert( data_diagram_is_valid( selected_diag ) );
997 : data_row_t parent_diagram_id;
998 0 : parent_diagram_id = data_diagram_get_parent_row_id( selected_diag );
999 : int32_t list_order;
1000 0 : list_order = gui_sketch_nav_tree_get_siblings_highest_order ( &((*this_).nav_tree) );
1001 :
1002 :
1003 0 : c_result = gui_sketch_object_creator_create_diagram( &((*this_).object_creator),
1004 : parent_diagram_id,
1005 : list_order + 32768,
1006 : &new_diag_id
1007 : );
1008 : }
1009 0 : break;
1010 :
1011 0 : case GUI_SKETCH_ACTION_NEW_CHILD_DIAGRAM:
1012 : {
1013 0 : assert( data_diagram_is_valid( selected_diag ) );
1014 : data_row_t selected_diagram_id;
1015 0 : selected_diagram_id = data_diagram_get_row_id( selected_diag );
1016 : int32_t list_order;
1017 0 : list_order = gui_sketch_nav_tree_get_children_highest_order ( &((*this_).nav_tree) );
1018 0 : c_result = gui_sketch_object_creator_create_diagram( &((*this_).object_creator),
1019 : selected_diagram_id,
1020 : list_order + 32768,
1021 : &new_diag_id
1022 : );
1023 : }
1024 0 : break;
1025 :
1026 0 : case GUI_SKETCH_ACTION_NEW_ROOT_DIAGRAM:
1027 : {
1028 0 : c_result = gui_sketch_object_creator_create_diagram( &((*this_).object_creator),
1029 : DATA_ROW_VOID,
1030 : 0,
1031 : &new_diag_id
1032 : );
1033 : }
1034 0 : break;
1035 :
1036 0 : default:
1037 : {
1038 0 : U8_LOG_ERROR_INT("illegal action value in gui_sketch_action_t:",action_button_id);
1039 0 : assert(false);
1040 : c_result = U8_ERROR_INVALID_REQUEST;
1041 : }
1042 : break;
1043 : }
1044 : }
1045 :
1046 0 : if ( U8_ERROR_NONE != c_result )
1047 : {
1048 0 : U8_LOG_ERROR("unexpected error at gui_sketch_object_creator_create_diagram");
1049 : }
1050 : else
1051 : {
1052 : data_id_t focused_id;
1053 0 : data_id_init( &focused_id, DATA_TABLE_DIAGRAM, new_diag_id );
1054 :
1055 : /* load/reload data to be drawn */
1056 0 : gui_sketch_area_show_diagram( this_, focused_id );
1057 :
1058 : /* update marked set */
1059 : data_full_id_t focused_full_id;
1060 0 : data_full_id_init_solo( &focused_full_id, &focused_id );
1061 :
1062 0 : gui_marked_set_set_focused( (*this_).marker, focused_full_id, focused_id );
1063 0 : gui_marked_set_clear_selected_set( (*this_).marker );
1064 : }
1065 : }
1066 : }
1067 : }
1068 0 : break;
1069 :
1070 0 : case GUI_TOOL_EDIT:
1071 : {
1072 0 : U8_TRACE_INFO( "GUI_TOOL_EDIT" );
1073 :
1074 : /* determine the focused object */
1075 : data_full_id_t focused_object;
1076 : data_id_t diag_id;
1077 0 : gui_sketch_area_private_get_element_id_at_pos( this_,
1078 : x,
1079 : y,
1080 : true /* FILTER_LIFELINE */,
1081 : &focused_object,
1082 : &diag_id
1083 : );
1084 0 : data_full_id_trace( &focused_object );
1085 :
1086 : /* update drag state */
1087 0 : gui_sketch_drag_state_start_dragging_when_move ( &((*this_).drag_state), focused_object );
1088 :
1089 : /* toggle objects according to rules of gui_marked_set_t */
1090 0 : gui_marked_set_toggle_obj( (*this_).marker, focused_object, diag_id );
1091 :
1092 : /* mark dirty rect */
1093 0 : gtk_widget_queue_draw( (*this_).drawing_area );
1094 : }
1095 0 : break;
1096 :
1097 0 : case GUI_TOOL_SEARCH:
1098 : {
1099 0 : U8_TRACE_INFO( "GUI_TOOL_SEARCH" );
1100 :
1101 : /* determine clicked diagram */
1102 0 : data_id_t clicked_diagram_id = DATA_ID_VOID;
1103 0 : data_id_t clicked_object_id = DATA_ID_VOID;
1104 0 : gui_sketch_area_private_get_diagram_and_object_id_at_pos ( this_, x, y, &clicked_diagram_id, &clicked_object_id );
1105 0 : data_id_trace( &clicked_diagram_id );
1106 0 : data_id_trace( &clicked_object_id );
1107 :
1108 : /* store diagram id to drag_state */
1109 0 : if ( data_id_is_valid( &clicked_diagram_id ) )
1110 : {
1111 : /* update drag state */
1112 : data_full_id_t dragged_object;
1113 0 : data_full_id_init_solo ( &dragged_object, &clicked_diagram_id );
1114 0 : gui_sketch_drag_state_start_dragging_when_move ( &((*this_).drag_state), dragged_object );
1115 : }
1116 : else
1117 : {
1118 0 : U8_TRACE_INFO("invalid clicked object at gui_sketch_area_button_press_callback");
1119 :
1120 : gui_sketch_action_t action_button_id;
1121 0 : gui_sketch_result_list_get_button_at_pos ( &((*this_).result_list), x, y, &action_button_id );
1122 0 : if ( action_button_id == GUI_SKETCH_ACTION_PREVIOUS_PAGE )
1123 : {
1124 0 : pos_scroll_page_t prev_page = gui_sketch_result_list_get_prev_page( &((*this_).result_list) );
1125 0 : gui_search_runner_rerun( (*this_).search_runner, prev_page );
1126 : }
1127 0 : else if ( action_button_id == GUI_SKETCH_ACTION_NEXT_PAGE )
1128 : {
1129 0 : pos_scroll_page_t next_page = gui_sketch_result_list_get_next_page( &((*this_).result_list) );
1130 0 : gui_search_runner_rerun( (*this_).search_runner, next_page );
1131 : }
1132 : }
1133 :
1134 : /* which object is currently focused? */
1135 0 : const data_id_t focused_before = gui_marked_set_get_focused_obj( (*this_).marker );
1136 :
1137 0 : if ( data_id_equals ( &clicked_object_id, &focused_before ) )
1138 : {
1139 : /* the clicked object is already focused */
1140 : }
1141 : else
1142 : {
1143 : /* set focused object */
1144 0 : assert( DATA_TABLE_DIAGRAMELEMENT != data_id_get_table( &clicked_object_id ) );
1145 : data_full_id_t focused_object;
1146 0 : data_full_id_init_solo ( &focused_object, &clicked_object_id );
1147 :
1148 0 : gui_marked_set_set_focused( (*this_).marker, focused_object, clicked_diagram_id );
1149 : }
1150 : }
1151 0 : break;
1152 :
1153 0 : case GUI_TOOL_CREATE:
1154 : {
1155 0 : U8_TRACE_INFO( "GUI_TOOL_CREATE" );
1156 :
1157 : /* what is the target location? */
1158 0 : gui_sketch_card_t *target_card = gui_sketch_area_private_get_card_at_pos ( this_, x, y );
1159 :
1160 0 : if ( NULL == target_card )
1161 : {
1162 0 : U8_TRACE_INFO_INT_INT("No card at",x,y);
1163 :
1164 : /* if this happens, invalidate the marked object. */
1165 0 : gui_marked_set_clear_focused( (*this_).marker );
1166 : }
1167 : else
1168 : {
1169 : /* determine the object at click location */
1170 0 : const data_id_t diagram_id = gui_sketch_card_get_diagram_id( target_card );
1171 : const layout_subelement_id_t element_id
1172 0 : = gui_sketch_card_get_element_at_pos( target_card, x, y, false /* FILTER_LIFELINE */ );
1173 : const data_full_id_t *const clicked_object
1174 0 : = layout_subelement_id_get_id_const( &element_id );
1175 0 : data_full_id_trace( clicked_object );
1176 : const layout_subelement_kind_t kind
1177 0 : = layout_subelement_id_get_kind( &element_id );
1178 0 : if ( kind != LAYOUT_SUBELEMENT_KIND_SPACE )
1179 : {
1180 : /* update drag state */
1181 0 : gui_sketch_drag_state_start_dragging_when_move ( &((*this_).drag_state), *clicked_object );
1182 :
1183 : /* set focused object (either a diagramelement or a feature - but not a lifeline!) */
1184 : const layout_subelement_id_t filtered_element_id
1185 0 : = gui_sketch_card_get_element_at_pos( target_card, x, y, true /* FILTER_LIFELINE */ );
1186 : const data_full_id_t *const clicked_filtered_object
1187 0 : = layout_subelement_id_get_id_const( &filtered_element_id );
1188 0 : gui_marked_set_set_focused( (*this_).marker, *clicked_filtered_object, diagram_id );
1189 : }
1190 : else /* clicked either into inner space of a classifier or of a diagram */
1191 : {
1192 : /* stop dragging */
1193 0 : gui_sketch_drag_state_stop_dragging ( &((*this_).drag_state) );
1194 :
1195 : /* get data */
1196 : const data_id_t *const surrounding_element
1197 0 : = data_full_id_get_primary_id_const( clicked_object );
1198 0 : assert( data_id_is_valid( surrounding_element ) );
1199 :
1200 : /* create a new classifier */
1201 : data_id_t dummy_classifier;
1202 0 : data_id_init( &dummy_classifier, DATA_TABLE_CLASSIFIER, DATA_ROW_VOID );
1203 0 : layout_order_t layout_order = gui_sketch_card_get_order_at_pos( target_card, dummy_classifier, x, y );
1204 0 : const int32_t x_order = layout_order_get_first( &layout_order );
1205 0 : const int32_t y_order = layout_order_get_second( &layout_order );
1206 0 : U8_TRACE_INFO_INT_INT( "x-order/y-order", x_order, y_order );
1207 :
1208 : /* create a classifier or a child-classifier */
1209 : u8_error_t c_result;
1210 : data_row_t new_diagele_id;
1211 : data_row_t new_classifier_id;
1212 0 : if ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( surrounding_element ) )
1213 : {
1214 : const data_id_t *const surrounding_classifier
1215 0 : = data_full_id_get_secondary_id_const( clicked_object );
1216 0 : assert( data_id_is_valid( surrounding_classifier ) );
1217 :
1218 : data_row_t new_relationship_id;
1219 0 : c_result = gui_sketch_object_creator_create_classifier_as_child( &((*this_).object_creator),
1220 : data_id_get_row_id( &diagram_id ),
1221 : data_id_get_row_id( surrounding_classifier ),
1222 : x_order,
1223 : y_order,
1224 : &new_diagele_id,
1225 : &new_classifier_id,
1226 : &new_relationship_id
1227 : );
1228 : }
1229 : else
1230 : {
1231 0 : assert( DATA_TABLE_DIAGRAM == data_id_get_table( surrounding_element ) );
1232 0 : c_result = gui_sketch_object_creator_create_classifier( &((*this_).object_creator),
1233 : data_id_get_row_id( &diagram_id ),
1234 : x_order,
1235 : y_order,
1236 : &new_diagele_id,
1237 : &new_classifier_id
1238 : );
1239 : }
1240 :
1241 0 : if ( U8_ERROR_DUPLICATE_NAME == c_result )
1242 : {
1243 : /* this should not happen: names are auto-generated */
1244 0 : gui_simple_message_to_user_show_message_with_name( (*this_).message_to_user,
1245 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
1246 : GUI_SIMPLE_MESSAGE_CONTENT_NAME_NOT_UNIQUE,
1247 : ""
1248 : );
1249 : }
1250 0 : else if ( U8_ERROR_NONE != c_result )
1251 : {
1252 0 : U8_LOG_ERROR("unexpected error at gui_sketch_object_creator_create_classifier/_as_child");
1253 : }
1254 : else
1255 : {
1256 : /* set focused object and notify listener */
1257 0 : const data_full_id_t focused_object
1258 : = DATA_FULL_ID( DATA_TABLE_DIAGRAMELEMENT, new_diagele_id, new_classifier_id );
1259 0 : gui_marked_set_set_focused( (*this_).marker, focused_object, diagram_id );
1260 0 : gui_marked_set_clear_selected_set( (*this_).marker );
1261 :
1262 0 : U8_TRACE_INFO_INT( "new_classifier_id:", new_classifier_id );
1263 : }
1264 : }
1265 : }
1266 : }
1267 0 : break;
1268 :
1269 0 : default:
1270 : {
1271 0 : U8_LOG_ERROR( "selected_tool is out of range" );
1272 : }
1273 0 : break;
1274 : }
1275 :
1276 0 : U8_TRACE_END();
1277 0 : }
1278 :
1279 0 : void gui_sketch_area_button_release_callback( GtkGestureClick* self,
1280 : gint n_press,
1281 : gdouble x,
1282 : gdouble y,
1283 : gpointer user_data )
1284 : {
1285 0 : U8_TRACE_BEGIN();
1286 0 : U8_TRACE_TIMESTAMP();
1287 0 : gui_sketch_area_t *this_ = user_data;
1288 0 : assert( NULL != this_ );
1289 :
1290 0 : gui_sketch_area_button_release( this_, (int)x, (int)y );
1291 :
1292 0 : U8_TRACE_END();
1293 0 : }
1294 :
1295 0 : void gui_sketch_area_button_release( gui_sketch_area_t *this_, int x, int y )
1296 : {
1297 0 : U8_TRACE_BEGIN();
1298 :
1299 0 : U8_TRACE_INFO("release");
1300 :
1301 : /* get position */
1302 0 : U8_TRACE_INFO_INT_INT("x/y",x,y);
1303 :
1304 : /* update drag coordinates */
1305 0 : gui_sketch_drag_state_set_to ( &((*this_).drag_state), x, y );
1306 :
1307 : /* check that drag state is true */
1308 0 : if ( ( ! gui_sketch_drag_state_is_dragging( &((*this_).drag_state) ) )
1309 0 : && ( ! gui_sketch_drag_state_is_waiting_for_move( &((*this_).drag_state) ) ) )
1310 : {
1311 0 : U8_TRACE_INFO("drag state indicates no dragging and no waiting - but button was pressed before!");
1312 : }
1313 :
1314 : /* do action */
1315 : const gui_tool_t selected_tool
1316 0 : = gui_sketch_request_get_tool_mode( &((*this_).request) );
1317 0 : switch ( selected_tool )
1318 : {
1319 0 : case GUI_TOOL_NAVIGATE:
1320 : {
1321 0 : U8_TRACE_INFO("GUI_TOOL_NAVIGATE");
1322 0 : if ( gui_sketch_drag_state_is_dragging ( &((*this_).drag_state) ) )
1323 : {
1324 : /* which diagram was dragged? */
1325 : data_full_id_t *dragged_object;
1326 0 : dragged_object = gui_sketch_drag_state_get_dragged_object_ptr ( &((*this_).drag_state) );
1327 : data_id_t dragged_diagram;
1328 0 : dragged_diagram = data_full_id_get_primary_id( dragged_object );
1329 :
1330 : /* to which diagram-gap was it dragged to? */
1331 : gui_error_t gui_err;
1332 : data_id_t target_parent_id;
1333 : int32_t target_list_order;
1334 : shape_int_rectangle_t dummy_gap_marker_border;
1335 0 : gui_err = gui_sketch_nav_tree_get_gap_info_at_pos( &((*this_).nav_tree),
1336 : x,
1337 : y,
1338 : &target_parent_id,
1339 : &target_list_order,
1340 : &dummy_gap_marker_border
1341 : );
1342 :
1343 0 : if (( DATA_TABLE_DIAGRAM == data_id_get_table( &dragged_diagram ) )
1344 0 : && ( DATA_TABLE_DIAGRAM == data_id_get_table( &target_parent_id ) )
1345 0 : && ( GUI_ERROR_NONE == gui_err ))
1346 : {
1347 0 : U8_TRACE_INFO_INT( "dragged_diagram:", data_id_get_row_id( &dragged_diagram ) );
1348 0 : U8_TRACE_INFO_INT( "target_parent_id:", data_id_get_row_id( &target_parent_id ) );
1349 0 : U8_TRACE_INFO_INT( "target_list_order:", target_list_order );
1350 : bool is_descendant;
1351 : bool is_self;
1352 : gui_error_t not_found;
1353 0 : not_found = gui_sketch_nav_tree_is_descendant( &((*this_).nav_tree),
1354 : data_id_get_row_id( &dragged_diagram ),
1355 : data_id_get_row_id( &target_parent_id ),
1356 : &is_descendant
1357 : );
1358 0 : is_self = ( data_id_get_row_id( &dragged_diagram ) == data_id_get_row_id( &target_parent_id ) );
1359 0 : if ( ( ! is_self ) && ( not_found == GUI_ERROR_NONE ) && ( ! is_descendant ) )
1360 0 : {
1361 : ctrl_diagram_controller_t *diag_control;
1362 0 : diag_control = ctrl_controller_get_diagram_control_ptr ( (*this_).controller );
1363 :
1364 : u8_error_t c_err;
1365 0 : c_err = ctrl_diagram_controller_update_diagram_list_order( diag_control,
1366 : data_id_get_row_id( &dragged_diagram ),
1367 : target_list_order
1368 : );
1369 0 : if ( U8_ERROR_NONE != c_err )
1370 : {
1371 0 : U8_LOG_ERROR_HEX( "U8_ERROR_NONE !=", c_err );
1372 : }
1373 0 : c_err = ctrl_diagram_controller_update_diagram_parent_id( diag_control,
1374 : data_id_get_row_id( &dragged_diagram ),
1375 : data_id_get_row_id( &target_parent_id ),
1376 : CTRL_UNDO_REDO_ACTION_BOUNDARY_APPEND
1377 : );
1378 0 : if ( U8_ERROR_NONE != c_err )
1379 : {
1380 0 : U8_LOG_ERROR_HEX( "U8_ERROR_NONE !=", c_err );
1381 : }
1382 : }
1383 0 : else if ( DATA_ROW_VOID == data_id_get_row_id( &target_parent_id ) )
1384 : {
1385 : /* a diagram is dragged to the root location */
1386 : ctrl_diagram_controller_t *const diag_control2
1387 0 : = ctrl_controller_get_diagram_control_ptr ( (*this_).controller );
1388 :
1389 : const data_row_t root_id
1390 0 : = gui_sketch_nav_tree_get_root_diagram_id ( &((*this_).nav_tree) );
1391 0 : if (( root_id != DATA_ROW_VOID )&&( root_id != data_id_get_row_id( &dragged_diagram ) ))
1392 0 : {
1393 : u8_error_t c_err;
1394 0 : c_err = ctrl_diagram_controller_update_diagram_parent_id( diag_control2,
1395 : data_id_get_row_id( &dragged_diagram ),
1396 : DATA_ROW_VOID,
1397 : CTRL_UNDO_REDO_ACTION_BOUNDARY_START_NEW
1398 : );
1399 0 : if ( U8_ERROR_NONE != c_err )
1400 : {
1401 0 : U8_LOG_ERROR_HEX( "U8_ERROR_NONE !=", c_err );
1402 : }
1403 0 : c_err = ctrl_diagram_controller_update_diagram_parent_id( diag_control2,
1404 : root_id,
1405 : data_id_get_row_id( &dragged_diagram ),
1406 : CTRL_UNDO_REDO_ACTION_BOUNDARY_APPEND
1407 : );
1408 0 : if ( U8_ERROR_NONE != c_err )
1409 : {
1410 0 : U8_LOG_ERROR_HEX( "U8_ERROR_NONE !=", c_err );
1411 : }
1412 : }
1413 : else
1414 : {
1415 0 : U8_LOG_WARNING("dragging a diagram to the root location but no root exists or dragged diagram is root?");
1416 : }
1417 : }
1418 : else
1419 : {
1420 0 : U8_LOG_WARNING("diagram dragging to invalid target location");
1421 : /* current diagram is root */
1422 0 : gui_simple_message_to_user_show_message( (*this_).message_to_user,
1423 : GUI_SIMPLE_MESSAGE_TYPE_ERROR,
1424 : GUI_SIMPLE_MESSAGE_CONTENT_ANCESTOR_IS_NOT_DESCENDANT
1425 : );
1426 : }
1427 : }
1428 : }
1429 0 : else if ( gui_sketch_drag_state_is_waiting_for_move( &((*this_).drag_state) ) )
1430 : {
1431 : /* click on diagram without drag */
1432 : const data_full_id_t *const dragged_object
1433 0 : = gui_sketch_drag_state_get_dragged_object_ptr ( &((*this_).drag_state) );
1434 : const data_id_t dragged_diagram
1435 0 : = data_full_id_get_primary_id( dragged_object );
1436 0 : if ( DATA_TABLE_DIAGRAM == data_id_get_table( &dragged_diagram ) )
1437 : {
1438 0 : const data_row_t drag_id = data_id_get_row_id( &dragged_diagram );
1439 0 : const data_row_t focus_id = gui_sketch_area_private_get_focused_diagram_id( this_ );
1440 0 : if ( ( focus_id != DATA_ROW_VOID )&&( focus_id == drag_id ) )
1441 : {
1442 : /* if clicked diagram is already the focused diagram, switch to edit mode */
1443 0 : gui_toolbox_set_selected_tool( (*this_).toolbox, GUI_TOOL_EDIT );
1444 : }
1445 : else
1446 : {
1447 : /* load/reload data to be drawn */
1448 0 : gui_sketch_area_show_diagram( this_, dragged_diagram );
1449 :
1450 : /* set focus */
1451 0 : gui_marked_set_set_focused( (*this_).marker, *dragged_object, dragged_diagram );
1452 0 : gui_marked_set_clear_selected_set( (*this_).marker );
1453 :
1454 : /* mark dirty rect */
1455 0 : gtk_widget_queue_draw( (*this_).drawing_area );
1456 : }
1457 : }
1458 : else
1459 : {
1460 0 : U8_LOG_ANOMALY("GUI_TOOL_NAVIGATE released mouse button but not a diagram clicked before");
1461 : }
1462 : }
1463 : }
1464 0 : break;
1465 :
1466 0 : case GUI_TOOL_EDIT:
1467 : {
1468 0 : U8_TRACE_INFO("GUI_TOOL_EDIT");
1469 :
1470 0 : if ( gui_sketch_drag_state_is_dragging( &((*this_).drag_state) ) )
1471 : {
1472 : /* which object is selected? */
1473 : const data_full_id_t *const dragged_object
1474 0 : = gui_sketch_drag_state_get_dragged_object_ptr( &((*this_).drag_state) );
1475 : const data_id_t dragged_element
1476 0 : = data_full_id_get_primary_id( dragged_object );
1477 : const data_id_t dragged_classifier
1478 0 : = data_full_id_get_secondary_id( dragged_object );
1479 :
1480 : /* what is the target location? */
1481 0 : const gui_sketch_card_t *const target_card = gui_sketch_area_private_get_card_at_pos ( this_, x, y );
1482 0 : if ( NULL == target_card )
1483 : {
1484 0 : U8_TRACE_INFO_INT_INT("No card at",x,y);
1485 : }
1486 0 : else if ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( &dragged_element ) )
1487 : {
1488 0 : layout_order_t layout_order = gui_sketch_card_get_order_at_pos( target_card, dragged_classifier, x, y );
1489 0 : if ( LAYOUT_ORDER_TYPE_LIST == layout_order_get_order_type( &layout_order ) )
1490 : {
1491 0 : int32_t list_order = layout_order_get_first( &layout_order );
1492 0 : U8_TRACE_INFO_INT( "list_order", list_order );
1493 :
1494 : /* update db */
1495 : ctrl_classifier_controller_t *const classifier_control
1496 0 : = ctrl_controller_get_classifier_control_ptr ( (*this_).controller );
1497 : const u8_error_t mov_result
1498 0 : = ctrl_classifier_controller_update_classifier_list_order( classifier_control,
1499 : data_id_get_row_id( &dragged_classifier ),
1500 : list_order
1501 : );
1502 0 : if ( U8_ERROR_NONE != mov_result )
1503 : {
1504 0 : U8_LOG_ERROR( "changing order failed: ctrl_classifier_controller_update_classifier_list_order" );
1505 : }
1506 : }
1507 0 : else if ( LAYOUT_ORDER_TYPE_X_Y == layout_order_get_order_type( &layout_order ) )
1508 : {
1509 0 : int32_t x_order = layout_order_get_first( &layout_order );
1510 0 : int32_t y_order = layout_order_get_second( &layout_order );
1511 0 : U8_TRACE_INFO_INT_INT( "x-order/y-order", x_order, y_order );
1512 :
1513 : /* update db */
1514 : ctrl_classifier_controller_t *const classifier_control
1515 0 : = ctrl_controller_get_classifier_control_ptr ( (*this_).controller );
1516 : const u8_error_t mov_result
1517 0 : = ctrl_classifier_controller_update_classifier_x_order_y_order( classifier_control,
1518 : data_id_get_row_id( &dragged_classifier ),
1519 : x_order,
1520 : y_order
1521 : );
1522 0 : if ( U8_ERROR_NONE != mov_result )
1523 : {
1524 0 : U8_LOG_ERROR( "changing order failed: ctrl_classifier_controller_update_classifier_x_order_y_order" );
1525 : }
1526 : }
1527 : }
1528 0 : else if ( DATA_TABLE_RELATIONSHIP == data_id_get_table( &dragged_element ) )
1529 : {
1530 0 : layout_order_t layout_order = gui_sketch_card_get_order_at_pos( target_card, dragged_element, x, y );
1531 0 : if ( LAYOUT_ORDER_TYPE_LIST == layout_order_get_order_type( &layout_order ) )
1532 : {
1533 0 : int32_t list_order = layout_order_get_first( &layout_order );
1534 0 : U8_TRACE_INFO_INT( "list_order", list_order );
1535 :
1536 : /* update db */
1537 : ctrl_classifier_controller_t *const classifier_control
1538 0 : = ctrl_controller_get_classifier_control_ptr ( (*this_).controller );
1539 : const u8_error_t mov_result
1540 0 : = ctrl_classifier_controller_update_relationship_list_order( classifier_control,
1541 : data_id_get_row_id( &dragged_element ),
1542 : list_order
1543 : );
1544 0 : if ( U8_ERROR_NONE != mov_result )
1545 : {
1546 0 : U8_LOG_ERROR( "changing order failed: ctrl_classifier_controller_update_relationship_list_order" );
1547 : }
1548 : }
1549 : }
1550 0 : else if ( DATA_TABLE_FEATURE == data_id_get_table( &dragged_element ) )
1551 : {
1552 0 : layout_order_t layout_order = gui_sketch_card_get_order_at_pos( target_card, dragged_element, x, y );
1553 0 : if ( LAYOUT_ORDER_TYPE_LIST == layout_order_get_order_type( &layout_order ) )
1554 : {
1555 0 : int32_t list_order = layout_order_get_first( &layout_order );
1556 0 : U8_TRACE_INFO_INT( "list_order", list_order );
1557 :
1558 : /* update db */
1559 : ctrl_classifier_controller_t *const classifier_control
1560 0 : = ctrl_controller_get_classifier_control_ptr ( (*this_).controller );
1561 : const u8_error_t mov_result
1562 0 : = ctrl_classifier_controller_update_feature_list_order( classifier_control,
1563 : data_id_get_row_id( &dragged_element ),
1564 : list_order
1565 : );
1566 0 : if ( U8_ERROR_NONE != mov_result )
1567 : {
1568 0 : U8_LOG_ERROR( "changing order failed: ctrl_classifier_controller_update_feature_list_order" );
1569 : }
1570 : }
1571 : }
1572 : else
1573 : {
1574 0 : U8_TRACE_INFO("Dragged object is neither relationship nor classifier nor feature");
1575 : }
1576 : }
1577 : }
1578 0 : break;
1579 :
1580 0 : case GUI_TOOL_SEARCH:
1581 : {
1582 0 : U8_TRACE_INFO("GUI_TOOL_SEARCH");
1583 :
1584 0 : if ( gui_sketch_drag_state_is_waiting_for_move( &((*this_).drag_state) ) )
1585 : {
1586 : /* click on diagram without drag */
1587 : const data_full_id_t *const dragged_object
1588 0 : = gui_sketch_drag_state_get_dragged_object_ptr ( &((*this_).drag_state) );
1589 : const data_id_t dragged_diagram
1590 0 : = data_full_id_get_primary_id( dragged_object );
1591 0 : if ( DATA_TABLE_DIAGRAM == data_id_get_table( &dragged_diagram ) )
1592 : {
1593 : /* load/reload data to be drawn */
1594 0 : gui_sketch_area_show_diagram( this_, dragged_diagram );
1595 :
1596 : /* clear selected set but do not change focused object */
1597 0 : gui_marked_set_clear_selected_set( (*this_).marker );
1598 :
1599 : /* switch to edit mode */
1600 0 : gui_toolbox_set_selected_tool( (*this_).toolbox, GUI_TOOL_EDIT );
1601 : }
1602 : else
1603 : {
1604 0 : U8_TRACE_INFO("GUI_TOOL_SEARCH released mouse button but not a diagram clicked before");
1605 : }
1606 : }
1607 : }
1608 0 : break;
1609 :
1610 0 : case GUI_TOOL_CREATE:
1611 : {
1612 0 : U8_TRACE_INFO("GUI_TOOL_CREATE");
1613 :
1614 0 : if ( gui_sketch_drag_state_is_dragging ( &((*this_).drag_state) ) )
1615 : {
1616 : /* which object is selected? */
1617 : const data_full_id_t *const dragged_object
1618 0 : = gui_sketch_drag_state_get_dragged_object_ptr ( &((*this_).drag_state) );
1619 0 : data_full_id_trace( dragged_object );
1620 : const data_id_t dragged_element
1621 0 : = data_full_id_get_primary_id( dragged_object );
1622 : const data_id_t dragged_classifier
1623 0 : = data_full_id_get_secondary_id( dragged_object );
1624 :
1625 : /* which object is at the target location? */
1626 : data_full_id_t destination_object;
1627 : data_id_t diag_id;
1628 0 : gui_sketch_area_private_get_element_id_at_pos( this_,
1629 : x,
1630 : y,
1631 : false /* FILTER_LIFELINE */,
1632 : &destination_object,
1633 : &diag_id
1634 : );
1635 0 : data_full_id_trace( &destination_object );
1636 : const data_id_t destination_element
1637 0 : = data_full_id_get_primary_id( &destination_object );
1638 : const data_id_t destination_classifier
1639 0 : = data_full_id_get_secondary_id( &destination_object );
1640 :
1641 0 : gui_sketch_card_t *target_card = gui_sketch_area_private_get_card_at_pos( this_, x, y );
1642 0 : if ( data_id_is_valid( &dragged_classifier ) && data_id_is_valid( &destination_classifier ) && ( NULL != target_card ))
1643 : {
1644 0 : if ( ( DATA_TABLE_CLASSIFIER == data_id_get_table( &dragged_classifier ) )
1645 0 : && ( DATA_TABLE_CLASSIFIER == data_id_get_table( &destination_classifier ) ) )
1646 : {
1647 : /* get the diagram type */
1648 0 : const data_diagram_t *const target_diag = gui_sketch_card_get_diagram_ptr( target_card );
1649 0 : assert ( target_diag != NULL );
1650 0 : data_diagram_type_t diag_type = data_diagram_get_diagram_type( target_diag );
1651 :
1652 : /* determine source and destionation */
1653 : data_row_t new_from_classifier_id;
1654 : data_row_t new_to_classifier_id;
1655 : data_row_t new_from_feature_id;
1656 : data_row_t new_to_feature_id;
1657 : {
1658 0 : new_from_classifier_id = data_id_get_row_id( &dragged_classifier );
1659 0 : if ( DATA_TABLE_FEATURE == data_id_get_table( &dragged_element ) )
1660 : {
1661 0 : new_from_feature_id = data_id_get_row_id( &dragged_element );
1662 : }
1663 : else
1664 : {
1665 0 : new_from_feature_id = DATA_ROW_VOID;
1666 : }
1667 0 : new_to_classifier_id = data_id_get_row_id( &destination_classifier );
1668 0 : if ( DATA_TABLE_FEATURE == data_id_get_table( &destination_element ) )
1669 : {
1670 0 : new_to_feature_id = data_id_get_row_id( &destination_element );
1671 : }
1672 : else
1673 : {
1674 0 : new_to_feature_id = DATA_ROW_VOID;
1675 : }
1676 : }
1677 :
1678 : /* propose a list_order for the relationship */
1679 0 : int32_t list_order_proposal = 0;
1680 : {
1681 : /* propose a list order */
1682 : data_id_t fake_relationship;
1683 0 : data_id_init ( &fake_relationship, DATA_TABLE_RELATIONSHIP, DATA_ROW_VOID );
1684 : const layout_order_t layout_order
1685 0 : = gui_sketch_card_get_order_at_pos( target_card, fake_relationship, x, y );
1686 0 : if ( LAYOUT_ORDER_TYPE_LIST == layout_order_get_order_type( &layout_order ) )
1687 : {
1688 0 : list_order_proposal = layout_order_get_first( &layout_order );
1689 : }
1690 : else /* LAYOUT_ORDER_TYPE_NONE */
1691 : {
1692 0 : list_order_proposal = gui_sketch_card_get_highest_rel_list_order( target_card ) + 32768;
1693 : }
1694 : }
1695 :
1696 : data_row_t new_relationship_id;
1697 : const u8_error_t c_result
1698 0 : = gui_sketch_object_creator_create_relationship( &((*this_).object_creator),
1699 : diag_type,
1700 : new_from_classifier_id,
1701 : new_from_feature_id,
1702 : new_to_classifier_id,
1703 : new_to_feature_id,
1704 : list_order_proposal,
1705 : &new_relationship_id
1706 : );
1707 :
1708 0 : if ( U8_ERROR_NONE != c_result )
1709 : {
1710 0 : U8_LOG_ANOMALY_HEX("anomaly at gui_sketch_object_creator_create_relationship",c_result);
1711 : }
1712 : else
1713 : {
1714 : /* set focused object */
1715 0 : const data_full_id_t focused_id
1716 : = DATA_FULL_ID_SOLO( DATA_TABLE_RELATIONSHIP, new_relationship_id );
1717 0 : gui_marked_set_set_focused( (*this_).marker, focused_id, diag_id );
1718 0 : gui_marked_set_clear_selected_set( (*this_).marker );
1719 : }
1720 : }
1721 : }
1722 : }
1723 0 : else if ( gui_sketch_drag_state_is_waiting_for_move( &((*this_).drag_state) ) )
1724 : {
1725 : const data_full_id_t *const dragged_object
1726 0 : = gui_sketch_drag_state_get_dragged_object_ptr ( &((*this_).drag_state) );
1727 : const data_id_t dragged_classifier
1728 0 : = data_full_id_get_secondary_id( dragged_object );
1729 :
1730 : const gui_sketch_card_t *const target_card
1731 0 : = gui_sketch_area_private_get_card_at_pos( this_, x, y );
1732 0 : if ( data_id_is_valid( &dragged_classifier ) && ( NULL != target_card ) )
1733 0 : {
1734 : /* click on classifier without drag */
1735 : const layout_subelement_id_t subelement_id
1736 0 : = gui_sketch_card_get_element_at_pos( target_card, x, y, false /* FILTER_LIFELINE */ );
1737 : const data_full_id_t *const full_element
1738 0 : = layout_subelement_id_get_id_const( &subelement_id );
1739 0 : data_full_id_trace( full_element );
1740 : const data_id_t clicked_element
1741 0 : = data_full_id_get_primary_id( full_element );
1742 : const data_id_t clicked_classifier
1743 0 : = data_full_id_get_secondary_id( full_element );
1744 0 : const bool is_diagele
1745 0 : = ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( &clicked_element ) );
1746 0 : const bool is_outline
1747 0 : = ( LAYOUT_SUBELEMENT_KIND_OUTLINE == layout_subelement_id_get_kind( &subelement_id ) );
1748 :
1749 0 : const data_id_t diag_id = gui_sketch_card_get_diagram_id( target_card );
1750 0 : if ( is_diagele && is_outline && ( DATA_TABLE_CLASSIFIER == data_id_get_table( &clicked_classifier ) ) )
1751 0 : {
1752 : /* get the diagram type */
1753 : const data_diagram_t *const target_diag
1754 0 : = gui_sketch_card_get_diagram_const ( target_card );
1755 0 : assert ( target_diag != NULL );
1756 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type ( target_diag );
1757 :
1758 : /* determine id of classifier to which the clicked object belongs */
1759 0 : const data_row_t classifier_id = data_id_get_row_id( &clicked_classifier );
1760 :
1761 : /* propose a list_order for the feature */
1762 0 : const int32_t std_list_order_proposal
1763 0 : = gui_sketch_card_get_highest_feat_list_order( target_card, clicked_classifier ) + 32768;
1764 0 : int32_t port_list_order_proposal = 0;
1765 : {
1766 0 : data_feature_init_new( &((*this_).private_temp_fake_feature),
1767 : DATA_FEATURE_TYPE_PORT,
1768 : classifier_id, /* classifier */
1769 : "FAKE_FEATURE",
1770 : "port-type",
1771 : "to determine the list order",
1772 : 0 /* list_order */
1773 : );
1774 0 : port_list_order_proposal = gui_sketch_card_get_feature_order_at_pos( target_card,
1775 0 : &((*this_).private_temp_fake_feature),
1776 : x,
1777 : y
1778 : );
1779 0 : data_feature_destroy ( &((*this_).private_temp_fake_feature) );
1780 : }
1781 :
1782 : /* create a feature */
1783 : data_row_t new_feature_id;
1784 : const u8_error_t ctrl_err
1785 0 : = gui_sketch_object_creator_create_feature( &((*this_).object_creator),
1786 : diag_type,
1787 : classifier_id,
1788 : std_list_order_proposal,
1789 : port_list_order_proposal,
1790 : &new_feature_id
1791 : );
1792 :
1793 0 : if ( U8_ERROR_NONE != ctrl_err )
1794 : {
1795 0 : U8_LOG_ANOMALY_HEX("anomaly at gui_sketch_object_creator_create_feature",ctrl_err);
1796 : }
1797 : else
1798 : {
1799 : /* set focused object */
1800 0 : const data_full_id_t new_focused_id
1801 : = DATA_FULL_ID( DATA_TABLE_FEATURE, new_feature_id, classifier_id );
1802 0 : gui_marked_set_set_focused( (*this_).marker, new_focused_id, diag_id );
1803 0 : gui_marked_set_clear_selected_set( (*this_).marker );
1804 : }
1805 : }
1806 : else
1807 : {
1808 0 : U8_LOG_WARNING("unexpected state at gui_sketch_area_button_release_callback");
1809 : }
1810 : }
1811 : else
1812 : {
1813 0 : U8_LOG_WARNING("invalid clicked object at gui_sketch_area_button_release_callback");
1814 : }
1815 : }
1816 : else
1817 : {
1818 0 : U8_TRACE_INFO("according to drag-state, the button press was already handled at press-time");
1819 : }
1820 : }
1821 0 : break;
1822 :
1823 0 : default:
1824 : {
1825 0 : U8_LOG_ERROR("selected_tool is out of range");
1826 : }
1827 0 : break;
1828 : }
1829 :
1830 : /* stop dragging */
1831 0 : gui_sketch_drag_state_stop_dragging ( &((*this_).drag_state) );
1832 :
1833 : /* mark dirty rect */
1834 0 : gtk_widget_queue_draw( (*this_).drawing_area );
1835 :
1836 0 : U8_TRACE_END();
1837 0 : }
1838 :
1839 0 : gboolean gui_sketch_area_key_press_callback( GtkEventControllerKey* self,
1840 : guint keyval,
1841 : guint keycode,
1842 : GdkModifierType state,
1843 : gpointer user_data )
1844 : {
1845 0 : U8_TRACE_BEGIN();
1846 0 : U8_TRACE_TIMESTAMP();
1847 0 : gui_sketch_area_t *this_ = user_data;
1848 0 : assert( NULL != this_ );
1849 :
1850 0 : const gboolean result_event_handled = gui_sketch_area_key_press( this_, 0 != (GDK_CONTROL_MASK&state), keyval );
1851 :
1852 0 : U8_TRACE_END();
1853 0 : return result_event_handled;
1854 : }
1855 :
1856 0 : bool gui_sketch_area_key_press( gui_sketch_area_t *this_, bool ctrl_state, guint keyval )
1857 : {
1858 0 : U8_TRACE_BEGIN();
1859 0 : gboolean result_event_handled = false;
1860 :
1861 : /* keys that have to be handled locally in the gui_sketch_area */
1862 : /* becasue handling them globally would interfere with text entry fields */
1863 0 : if ( ctrl_state )
1864 : {
1865 0 : if ( keyval == GDK_KEY_x )
1866 : {
1867 0 : U8_TRACE_INFO ( "key pressed: Ctrl-X" );
1868 0 : gui_toolbox_cut( (*this_).toolbox );
1869 0 : result_event_handled = true;
1870 : }
1871 0 : else if ( keyval == GDK_KEY_c )
1872 : {
1873 0 : U8_TRACE_INFO ( "key pressed: Ctrl-C" );
1874 0 : gui_toolbox_copy( (*this_).toolbox );
1875 0 : result_event_handled = true;
1876 : }
1877 0 : else if ( keyval == GDK_KEY_v )
1878 : {
1879 0 : U8_TRACE_INFO ( "key pressed: Ctrl-V" );
1880 0 : gui_toolbox_paste( (*this_).toolbox );
1881 0 : result_event_handled = true;
1882 : }
1883 : /* other keys are out of scope */
1884 : }
1885 : else
1886 : {
1887 0 : if ( keyval == GDK_KEY_Delete )
1888 : {
1889 0 : U8_TRACE_INFO ( "key pressed: DEL" );
1890 0 : gui_toolbox_delete( (*this_).toolbox );
1891 0 : result_event_handled = true;
1892 : }
1893 : /* other keys are out of scope */
1894 : }
1895 :
1896 0 : U8_TRACE_END();
1897 0 : return result_event_handled;
1898 : }
1899 :
1900 0 : void gui_sketch_area_data_changed_callback( GtkWidget *widget, data_change_message_t *msg, gpointer data )
1901 : {
1902 0 : U8_TRACE_BEGIN();
1903 0 : assert( NULL != msg );
1904 0 : gui_sketch_area_t *this_ = data;
1905 0 : assert( NULL != this_ );
1906 0 : assert ( NULL != widget );
1907 :
1908 0 : const data_change_event_type_t evt_type = data_change_message_get_event ( msg );
1909 :
1910 0 : if ( evt_type == DATA_CHANGE_EVENT_TYPE_DB_CLOSED )
1911 : {
1912 : /* abort dragging */
1913 0 : gui_sketch_drag_state_reinit( &((*this_).drag_state) );
1914 :
1915 : /* do not keep search results */
1916 0 : gui_sketch_result_list_invalidate_data( &((*this_).result_list) );
1917 :
1918 : /* do not keep selected or focued objects */
1919 0 : gui_marked_set_reset( (*this_).marker );
1920 :
1921 : /* reset the currently visible diagram */
1922 0 : gui_sketch_request_reinit( &((*this_).request) );
1923 : }
1924 :
1925 0 : if ( evt_type == DATA_CHANGE_EVENT_TYPE_DB_OPENED )
1926 : {
1927 : /* go to navigation mode, show root */
1928 0 : gui_sketch_request_reinit( &((*this_).request) );
1929 0 : gui_toolbox_set_selected_tool( (*this_).toolbox, GUI_TOOL_NAVIGATE );
1930 0 : gui_sketch_area_show_diagram( this_, DATA_ID_VOID );
1931 : }
1932 :
1933 : /* load/reload data to be drawn */
1934 0 : gui_sketch_area_private_refocus_and_reload_data( this_ );
1935 :
1936 : /* mark dirty rect */
1937 0 : gtk_widget_queue_draw( (*this_).drawing_area );
1938 :
1939 0 : U8_TRACE_END();
1940 0 : }
1941 :
1942 0 : void gui_sketch_area_tool_changed_callback( GtkWidget *widget, gui_tool_t tool, gpointer data )
1943 : {
1944 0 : U8_TRACE_BEGIN();
1945 0 : gui_sketch_area_t *this_ = data;
1946 0 : assert( NULL != this_ );
1947 0 : assert ( NULL != widget );
1948 :
1949 : /* info: This function is called once for activating a tool and once for deactiaving it! */
1950 0 : if ( gui_sketch_request_get_tool_mode( &((*this_).request) ) != tool )
1951 : {
1952 0 : gui_sketch_request_set_tool_mode( &((*this_).request), tool );
1953 :
1954 : /* load/reload data to be drawn */
1955 0 : gui_sketch_area_private_refocus_and_reload_data( this_ );
1956 :
1957 : /* mark dirty rect */
1958 0 : gtk_widget_queue_draw( (*this_).drawing_area );
1959 : }
1960 : else
1961 : {
1962 : /* either the already selected tool is selected again or it is unselected */
1963 : /* or the clear selection button was pressed */
1964 :
1965 : /* mark dirty rect */
1966 0 : gtk_widget_queue_draw( (*this_).drawing_area );
1967 : }
1968 :
1969 0 : U8_TRACE_END();
1970 0 : }
1971 :
1972 :
1973 : /*
1974 : Copyright 2016-2025 Andreas Warnke
1975 :
1976 : Licensed under the Apache License, Version 2.0 (the "License");
1977 : you may not use this file except in compliance with the License.
1978 : You may obtain a copy of the License at
1979 :
1980 : http://www.apache.org/licenses/LICENSE-2.0
1981 :
1982 : Unless required by applicable law or agreed to in writing, software
1983 : distributed under the License is distributed on an "AS IS" BASIS,
1984 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1985 : See the License for the specific language governing permissions and
1986 : limitations under the License.
1987 : */
|