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