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