Line data Source code
1 : /* File: gui_sketch_card.c; Copyright and License: see below */
2 :
3 : #include "sketch/gui_sketch_card.h"
4 : #include "pencil_diagram_maker.h"
5 : #include "geometry/geometry_rectangle.h"
6 : #include "u8/u8_trace.h"
7 : #include "u8/u8_log.h"
8 : #include "gui_gdk.h"
9 :
10 0 : void gui_sketch_card_init( gui_sketch_card_t *this_ )
11 : {
12 0 : U8_TRACE_BEGIN();
13 :
14 0 : (*this_).visible = false;
15 0 : (*this_).dirty_elements_layout = false;
16 0 : shape_int_rectangle_init( &((*this_).bounds), 0, 0, 0, 0 );
17 0 : data_visible_set_init( &((*this_).painter_input_data) );
18 0 : data_profile_part_init( &((*this_).profile) );
19 0 : pencil_diagram_maker_init( &((*this_).painter), &((*this_).painter_input_data), &((*this_).profile) );
20 0 : gui_sketch_marker_init( &((*this_).sketch_marker), false );
21 0 : gui_sketch_style_init( &((*this_).sketch_style) );
22 :
23 0 : U8_TRACE_END();
24 0 : }
25 :
26 0 : void gui_sketch_card_destroy( gui_sketch_card_t *this_ )
27 : {
28 0 : U8_TRACE_BEGIN();
29 :
30 0 : gui_sketch_style_destroy( &((*this_).sketch_style) );
31 0 : gui_sketch_marker_destroy( &((*this_).sketch_marker) );
32 0 : pencil_diagram_maker_destroy( &((*this_).painter) );
33 0 : data_profile_part_destroy( &((*this_).profile) );
34 0 : data_visible_set_destroy( &((*this_).painter_input_data) );
35 0 : shape_int_rectangle_destroy(&((*this_).bounds));
36 :
37 0 : U8_TRACE_END();
38 0 : }
39 :
40 0 : layout_subelement_id_t gui_sketch_card_get_element_at_pos( const gui_sketch_card_t *this_,
41 : int32_t x,
42 : int32_t y,
43 : bool filter_lifelines )
44 : {
45 0 : U8_TRACE_BEGIN();
46 :
47 : layout_subelement_id_t result;
48 0 : layout_subelement_id_init_void( &result );
49 :
50 : const layout_visible_set_t *const layout
51 0 : = pencil_diagram_maker_get_layout_data_const( &((*this_).painter) );
52 : const layout_diagram_t *const the_diagram
53 0 : = layout_visible_set_get_diagram_const( layout );
54 : const data_diagram_t *const diagram_data
55 0 : = layout_diagram_get_data_const ( the_diagram );
56 :
57 : /* get bounding box */
58 : const geometry_rectangle_t *diagram_bounds;
59 0 : diagram_bounds = layout_diagram_get_bounds_const( the_diagram );
60 :
61 : const bool pos_on_bounds
62 0 : = geometry_rectangle_contains( diagram_bounds, (double) x, (double) y );
63 0 : if ( pos_on_bounds )
64 : {
65 : /* check the relationship shapes */
66 : {
67 0 : result = gui_sketch_card_private_get_relationship_at_pos( this_,
68 : x,
69 : y
70 : );
71 : }
72 :
73 : /* determine a feature at the given position */
74 0 : if ( ! layout_subelement_id_is_valid( &result ) )
75 : {
76 0 : result = gui_sketch_card_private_get_feature_at_pos( this_,
77 : x,
78 : y,
79 : filter_lifelines
80 : );
81 : }
82 :
83 : /* determine a classifier at the given position */
84 0 : if ( ! layout_subelement_id_is_valid( &result ) )
85 : {
86 0 : result = gui_sketch_card_private_get_classifier_at_pos( this_,
87 : x,
88 : y
89 : );
90 : }
91 :
92 : /* fallback: return the diagram */
93 0 : if ( ! layout_subelement_id_is_valid( &result ) )
94 : {
95 : const geometry_rectangle_t *const diagram_space
96 0 : = layout_diagram_get_draw_area_const( the_diagram );
97 : const geometry_rectangle_t *const diagram_label
98 0 : = layout_diagram_get_label_box_const( the_diagram );
99 : const bool pos_on_space
100 0 : = geometry_rectangle_contains( diagram_space, (double) x, (double) y );
101 : const bool pos_on_label
102 0 : = geometry_rectangle_contains( diagram_label, (double) x, (double) y );
103 0 : const data_id_t diagram_id = data_diagram_get_data_id( diagram_data );
104 0 : const layout_subelement_kind_t kind
105 : = ( pos_on_space
106 : ? LAYOUT_SUBELEMENT_KIND_SPACE
107 0 : : ( pos_on_label ? LAYOUT_SUBELEMENT_KIND_LABEL : LAYOUT_SUBELEMENT_KIND_OUTLINE ) );
108 0 : layout_subelement_id_reinit_solo( &result, kind, &diagram_id );
109 : }
110 : }
111 : else
112 : {
113 0 : U8_TRACE_INFO( "given location outside diagram or no diagram chosen" );
114 : }
115 :
116 0 : U8_TRACE_END();
117 0 : return result;
118 : }
119 :
120 0 : layout_subelement_id_t gui_sketch_card_private_get_classifier_at_pos( const gui_sketch_card_t *this_,
121 : int32_t x,
122 : int32_t y )
123 : {
124 0 : U8_TRACE_BEGIN();
125 : layout_subelement_id_t result;
126 0 : layout_subelement_id_init_void( &result );
127 :
128 : /* get draw area */
129 0 : const layout_visible_set_t *const layout = pencil_diagram_maker_get_layout_data_const( &((*this_).painter) );
130 : const layout_diagram_t *const the_diagram
131 0 : = layout_visible_set_get_diagram_const( layout );
132 : const geometry_rectangle_t *const diagram_draw_area
133 0 : = layout_diagram_get_draw_area_const( the_diagram );
134 :
135 0 : if ( geometry_rectangle_contains( diagram_draw_area, (double) x, (double) y ) )
136 : {
137 : /* iterate over all classifiers */
138 : const uint32_t count
139 0 : = layout_visible_set_get_visible_classifier_count ( layout );
140 0 : double surrounding_classifier_area = geometry_rectangle_get_area( diagram_draw_area );
141 :
142 0 : for ( uint32_t index = 0; index < count; index ++ )
143 : {
144 : const layout_visible_classifier_t *const visible_classifier
145 0 : = layout_visible_set_get_visible_classifier_const ( layout, index );
146 : const geometry_rectangle_t *const classifier_symbol_box
147 0 : = layout_visible_classifier_get_symbol_box_const ( visible_classifier );
148 : const geometry_rectangle_t *const classifier_space
149 0 : = layout_visible_classifier_get_space_const ( visible_classifier );
150 : const geometry_rectangle_t *const classifier_label_box
151 0 : = layout_visible_classifier_get_label_box_const( visible_classifier );
152 :
153 : /* surrounding classifier is found. select it if it is the smallest found area */
154 0 : const double current_classifier_area = geometry_rectangle_get_area( classifier_space );
155 0 : const bool current_classifier_is_contained = ( current_classifier_area < surrounding_classifier_area );
156 : const bool pos_on_symbol
157 0 : = geometry_rectangle_contains( classifier_symbol_box, (double) x, (double) y );
158 : const bool pos_on_label
159 0 : = geometry_rectangle_contains( classifier_label_box, (double) x, (double) y );
160 0 : if (( pos_on_symbol || pos_on_label ) && current_classifier_is_contained )
161 : {
162 0 : surrounding_classifier_area = current_classifier_area;
163 :
164 : const bool pos_on_space
165 0 : = geometry_rectangle_contains( classifier_space, (double) x, (double) y );
166 0 : if ( pos_on_space )
167 : {
168 : data_full_id_t found_id;
169 0 : data_full_id_init_by_table_and_id( &found_id,
170 : DATA_TABLE_DIAGRAMELEMENT,
171 : layout_visible_classifier_get_diagramelement_id( visible_classifier ),
172 : DATA_TABLE_CLASSIFIER,
173 : layout_visible_classifier_get_classifier_id( visible_classifier )
174 : );
175 0 : layout_subelement_id_reinit( &result, LAYOUT_SUBELEMENT_KIND_SPACE, &found_id );
176 : }
177 : else
178 : {
179 : /* classifier is found */
180 : data_full_id_t found_id;
181 0 : data_full_id_init_by_table_and_id( &found_id,
182 : DATA_TABLE_DIAGRAMELEMENT,
183 : layout_visible_classifier_get_diagramelement_id( visible_classifier ),
184 : DATA_TABLE_CLASSIFIER,
185 : layout_visible_classifier_get_classifier_id( visible_classifier )
186 : );
187 0 : const layout_subelement_kind_t kind
188 0 : = ( pos_on_label ? LAYOUT_SUBELEMENT_KIND_LABEL : LAYOUT_SUBELEMENT_KIND_OUTLINE );
189 0 : layout_subelement_id_reinit( &result, kind, &found_id );
190 : }
191 : }
192 : }
193 : }
194 :
195 0 : U8_TRACE_END();
196 0 : return result;
197 : }
198 :
199 0 : layout_subelement_id_t gui_sketch_card_private_get_feature_at_pos( const gui_sketch_card_t *this_,
200 : int32_t x,
201 : int32_t y,
202 : bool filter_lifelines )
203 : {
204 0 : U8_TRACE_BEGIN();
205 : layout_subelement_id_t result;
206 0 : layout_subelement_id_init_void( &result );
207 :
208 : /* check all contained features */
209 0 : const layout_visible_set_t *const layout = pencil_diagram_maker_get_layout_data_const( &((*this_).painter) );
210 0 : const uint32_t f_count = layout_visible_set_get_feature_count( layout );
211 0 : for ( uint32_t f_idx = 0; f_idx < f_count; f_idx ++ )
212 : {
213 : const layout_feature_t *const the_feature
214 0 : = layout_visible_set_get_feature_const ( layout, f_idx );
215 : const geometry_rectangle_t *const feature_symbol_box
216 0 : = layout_feature_get_symbol_box_const ( the_feature );
217 : const geometry_rectangle_t *const feature_label_box
218 0 : = layout_feature_get_label_box_const( the_feature );
219 :
220 : const bool pos_on_symbol
221 0 : = geometry_rectangle_contains( feature_symbol_box, (double) x, (double) y );
222 : const bool pos_on_label
223 0 : = geometry_rectangle_contains( feature_label_box, (double) x, (double) y );
224 0 : if ( pos_on_symbol || pos_on_label )
225 : {
226 : /* feature is found */
227 : const data_feature_t *const data_feature
228 0 : = layout_feature_get_data_const ( the_feature );
229 : const layout_visible_classifier_t *const layout_classifier
230 0 : = layout_feature_get_classifier_const ( the_feature );
231 : data_full_id_t found_id;
232 0 : if (( filter_lifelines )
233 0 : &&( DATA_FEATURE_TYPE_LIFELINE == data_feature_get_main_type( data_feature ) ))
234 : {
235 0 : data_full_id_init_by_table_and_id( &found_id,
236 : DATA_TABLE_DIAGRAMELEMENT,
237 : layout_visible_classifier_get_diagramelement_id( layout_classifier ),
238 : DATA_TABLE_CLASSIFIER,
239 : layout_visible_classifier_get_classifier_id( layout_classifier )
240 : );
241 : }
242 : else
243 : {
244 0 : data_full_id_init_by_table_and_id( &found_id,
245 : DATA_TABLE_FEATURE,
246 : layout_feature_get_feature_id( the_feature ), /* feature or lifeline */
247 : DATA_TABLE_CLASSIFIER,
248 : data_feature_get_classifier_row_id( data_feature )
249 : );
250 : }
251 :
252 0 : const layout_subelement_kind_t kind
253 0 : = ( pos_on_label ? LAYOUT_SUBELEMENT_KIND_LABEL : LAYOUT_SUBELEMENT_KIND_OUTLINE );
254 0 : layout_subelement_id_reinit( &result, kind, &found_id );
255 : }
256 : }
257 :
258 0 : U8_TRACE_END();
259 0 : return result;
260 : }
261 :
262 0 : layout_subelement_id_t gui_sketch_card_private_get_relationship_at_pos( const gui_sketch_card_t *this_,
263 : int32_t x,
264 : int32_t y )
265 : {
266 0 : U8_TRACE_BEGIN();
267 : layout_subelement_id_t result;
268 0 : layout_subelement_id_init_void( &result );
269 0 : const int32_t snap_distance = gui_sketch_style_get_snap_to_relationship( &((*this_).sketch_style) );
270 :
271 0 : const layout_visible_set_t *const layout = pencil_diagram_maker_get_layout_data_const( &((*this_).painter) );
272 : const uint32_t count_relations
273 0 : = layout_visible_set_get_relationship_count ( layout );
274 0 : uint32_t matching_relations_found = 0;
275 0 : for ( uint32_t rel_index = 0; rel_index < count_relations; rel_index ++ )
276 : {
277 : const layout_relationship_t *const the_relationship
278 0 : = layout_visible_set_get_relationship_const( layout, rel_index );
279 : const geometry_connector_t *const relationship_shape
280 0 : = layout_relationship_get_shape_const( the_relationship );
281 : const geometry_rectangle_t *const rel_label_box
282 0 : = layout_relationship_get_label_box_const( the_relationship );
283 :
284 : const bool pos_on_shape
285 0 : = geometry_connector_is_close( relationship_shape, (double) x, (double) y, 0.000001 + (double) snap_distance );
286 : const bool pos_on_label
287 0 : = geometry_rectangle_contains( rel_label_box, (double) x, (double) y );
288 0 : if ( pos_on_shape || pos_on_label )
289 : {
290 : /* ensure that every relation at that location can be selected by small mouse movements */
291 0 : if ( ((uint32_t)(x+y))%(matching_relations_found+1) == 0 )
292 : {
293 : const data_relationship_t *const relation_data
294 0 : = layout_relationship_get_data_const( the_relationship );
295 0 : const data_id_t relation_id = data_relationship_get_data_id( relation_data );
296 0 : const layout_subelement_kind_t kind
297 0 : = ( pos_on_label ? LAYOUT_SUBELEMENT_KIND_LABEL : LAYOUT_SUBELEMENT_KIND_OUTLINE );
298 0 : layout_subelement_id_reinit_solo( &result, kind, &relation_id );
299 : }
300 0 : matching_relations_found ++;
301 : }
302 : }
303 :
304 0 : U8_TRACE_END();
305 0 : return result;
306 : }
307 :
308 0 : void gui_sketch_card_draw( gui_sketch_card_t *this_, gui_marked_set_t *marker, cairo_t *cr )
309 : {
310 0 : U8_TRACE_BEGIN();
311 0 : assert( NULL != cr );
312 :
313 0 : if ( (*this_).visible )
314 : {
315 : /* get marked ids */
316 0 : const data_id_t mark_focused = gui_marked_set_get_focused_obj( marker );
317 0 : const data_id_t mark_highlighted = gui_marked_set_get_highlighted( marker );
318 0 : const data_small_set_t *mark_selected_set = gui_marked_set_get_selected_set_const( marker );
319 :
320 : /* draw highlighting */
321 0 : const data_diagram_t *const diag = data_visible_set_get_diagram_const ( &((*this_).painter_input_data) );
322 0 : const data_id_t diag_id = data_diagram_get_data_id( diag );
323 0 : gui_sketch_marker_prepare_draw( &((*this_).sketch_marker),
324 : diag_id,
325 : marker,
326 : (*this_).bounds,
327 : cr
328 : );
329 :
330 : /* draw the current diagram */
331 0 : pencil_diagram_maker_draw ( &((*this_).painter),
332 : mark_focused,
333 : mark_highlighted,
334 : mark_selected_set,
335 : cr
336 : );
337 : }
338 :
339 0 : U8_TRACE_END();
340 0 : }
341 :
342 0 : void gui_sketch_card_move_object_to_order( gui_sketch_card_t *this_,
343 : data_id_t obj_id,
344 : const layout_order_t *order )
345 : {
346 0 : U8_TRACE_BEGIN();
347 0 : assert( NULL != order );
348 :
349 0 : const data_table_t table = data_id_get_table ( &obj_id );
350 0 : const data_row_t row_id = data_id_get_row_id ( &obj_id );
351 :
352 0 : layout_order_type_t order_type = layout_order_get_order_type( order );
353 0 : switch ( order_type )
354 : {
355 0 : case LAYOUT_ORDER_TYPE_X_Y:
356 : {
357 0 : switch ( table )
358 : {
359 0 : case DATA_TABLE_CLASSIFIER:
360 : {
361 0 : const int32_t x_order = layout_order_get_first( order );
362 0 : const int32_t y_order = layout_order_get_second( order );
363 :
364 : data_classifier_t *const move_me
365 0 : = data_visible_set_get_classifier_by_id_ptr( &((*this_).painter_input_data), row_id );
366 0 : if ( move_me == NULL )
367 : {
368 0 : U8_LOG_WARNING( "pencil input data does not contain the object to be moved" );
369 0 : data_id_trace( &obj_id );
370 : }
371 : else
372 : {
373 0 : data_classifier_set_x_order( move_me, x_order );
374 0 : data_classifier_set_y_order( move_me, y_order );
375 :
376 : /* success */
377 0 : (*this_).dirty_elements_layout = true;
378 : }
379 : }
380 0 : break;
381 :
382 0 : case DATA_TABLE_FEATURE:
383 : {
384 0 : U8_LOG_WARNING( "object to be x/y-moved has no x/y coordinates: feature" );
385 : }
386 0 : break;
387 :
388 0 : case DATA_TABLE_RELATIONSHIP:
389 : {
390 0 : U8_LOG_WARNING( "object to be x/y-moved has no x/y coordinates: relationship" );
391 : }
392 0 : break;
393 :
394 0 : case DATA_TABLE_DIAGRAMELEMENT:
395 : {
396 0 : U8_LOG_WARNING( "not implemented to move diagramelements. use the classifier instead." );
397 : }
398 0 : break;
399 :
400 0 : case DATA_TABLE_DIAGRAM:
401 : {
402 : /* pencil cannot move diagrams */
403 0 : U8_LOG_WARNING( "object to be x/y-moved has unexpected type: diagram" );
404 : }
405 0 : break;
406 :
407 0 : default:
408 : {
409 0 : U8_LOG_WARNING( "object to be x/y-moved has illegal type" );
410 : }
411 0 : break;
412 : }
413 : }
414 0 : break;
415 :
416 0 : case LAYOUT_ORDER_TYPE_LIST:
417 : {
418 0 : switch ( table )
419 : {
420 0 : case DATA_TABLE_CLASSIFIER:
421 : {
422 0 : const int32_t list_order = layout_order_get_first( order );
423 :
424 0 : data_classifier_t *const move_me = data_visible_set_get_classifier_by_id_ptr( &((*this_).painter_input_data), row_id );
425 0 : if ( move_me == NULL )
426 : {
427 0 : U8_LOG_WARNING( "pencil input data does not contain the classifier to be moved" );
428 0 : data_id_trace( &obj_id );
429 : }
430 : else
431 : {
432 0 : data_classifier_set_list_order( move_me, list_order );
433 :
434 : /* success */
435 0 : (*this_).dirty_elements_layout = true;
436 : }
437 : }
438 0 : break;
439 :
440 0 : case DATA_TABLE_FEATURE:
441 : {
442 0 : const int32_t list_order = layout_order_get_first( order );
443 :
444 0 : data_feature_t *const move_me = data_visible_set_get_feature_by_id_ptr( &((*this_).painter_input_data), row_id );
445 0 : if ( move_me == NULL )
446 : {
447 0 : U8_LOG_WARNING( "pencil input data does not contain the feature to be moved" );
448 0 : data_id_trace( &obj_id );
449 : }
450 : else
451 : {
452 0 : data_feature_set_list_order( move_me, list_order );
453 :
454 : /* success */
455 0 : (*this_).dirty_elements_layout = true;
456 : }
457 : }
458 0 : break;
459 :
460 0 : case DATA_TABLE_RELATIONSHIP:
461 : {
462 0 : const int32_t list_order = layout_order_get_first( order );
463 :
464 0 : data_relationship_t *const move_me = data_visible_set_get_relationship_by_id_ptr( &((*this_).painter_input_data), row_id );
465 0 : if ( move_me == NULL )
466 : {
467 0 : U8_LOG_WARNING( "pencil input data does not contain the relationship to be moved" );
468 0 : data_id_trace( &obj_id );
469 : }
470 : else
471 : {
472 0 : data_relationship_set_list_order( move_me, list_order );
473 :
474 : /* success */
475 0 : (*this_).dirty_elements_layout = true;
476 : }
477 : }
478 0 : break;
479 :
480 0 : case DATA_TABLE_DIAGRAMELEMENT:
481 : {
482 0 : U8_LOG_WARNING( "not implemented to move diagramelements. use the classifier instead." );
483 : }
484 0 : break;
485 :
486 0 : case DATA_TABLE_DIAGRAM:
487 : {
488 : /* pencil cannot move diagrams */
489 0 : U8_LOG_WARNING( "object to be x/y-moved has unexpected type: diagram" );
490 : }
491 0 : break;
492 :
493 0 : default:
494 : {
495 0 : U8_LOG_WARNING( "object to be x/y-moved has illegal type" );
496 : }
497 0 : break;
498 : }
499 : }
500 0 : break;
501 :
502 0 : case LAYOUT_ORDER_TYPE_NONE:
503 : default:
504 : {
505 : /* nothing to do */
506 : /* no error */
507 0 : U8_LOG_ANOMALY( "object to be moved has no movement data" );
508 : }
509 0 : break;
510 : }
511 :
512 0 : U8_TRACE_END();
513 0 : }
514 :
515 :
516 : /*
517 : Copyright 2016-2025 Andreas Warnke
518 :
519 : Licensed under the Apache License, Version 2.0 (the "License");
520 : you may not use this file except in compliance with the License.
521 : You may obtain a copy of the License at
522 :
523 : http://www.apache.org/licenses/LICENSE-2.0
524 :
525 : Unless required by applicable law or agreed to in writing, software
526 : distributed under the License is distributed on an "AS IS" BASIS,
527 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
528 : See the License for the specific language governing permissions and
529 : limitations under the License.
530 : */
|