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