Line data Source code
1 : /* File: gui_sketch_card_painter.c; Copyright and License: see below */
2 :
3 : #include "sketch/gui_sketch_card_painter.h"
4 : #include "u8/u8_trace.h"
5 : #include "gui_gtk.h"
6 : #include <assert.h>
7 :
8 0 : void gui_sketch_card_painter_init( gui_sketch_card_painter_t *this_,
9 : gui_resources_t *resources,
10 : gui_sketch_texture_t *texture_downloader )
11 : {
12 0 : U8_TRACE_BEGIN();
13 0 : assert( resources != NULL );
14 0 : assert( texture_downloader != NULL );
15 :
16 0 : gui_sketch_style_init( &((*this_).sketch_style) );
17 0 : (*this_).resources = resources;
18 0 : (*this_).texture_downloader = texture_downloader;
19 :
20 0 : U8_TRACE_END();
21 0 : }
22 :
23 0 : void gui_sketch_card_painter_destroy( gui_sketch_card_painter_t *this_ )
24 : {
25 0 : U8_TRACE_BEGIN();
26 :
27 0 : (*this_).resources = NULL;
28 0 : (*this_).texture_downloader = NULL;
29 0 : gui_sketch_style_destroy( &((*this_).sketch_style) );
30 :
31 0 : U8_TRACE_END();
32 0 : }
33 :
34 0 : void gui_sketch_card_painter_draw_overlay( gui_sketch_card_painter_t *this_,
35 : gui_tool_t selected_tool,
36 : const gui_sketch_drag_state_t *drag_state,
37 : const gui_sketch_card_t *card_under_mouse,
38 : gui_marked_set_t *marked_objects,
39 : cairo_t *cr )
40 : {
41 0 : U8_TRACE_BEGIN();
42 0 : assert( NULL != drag_state );
43 0 : assert( NULL != cr );
44 :
45 0 : switch ( selected_tool )
46 : {
47 0 : case GUI_TOOL_NAVIGATE:
48 : {
49 0 : gui_sketch_card_painter_private_draw_nav_mode( this_, drag_state, card_under_mouse, cr );
50 : }
51 0 : break;
52 :
53 0 : case GUI_TOOL_EDIT:
54 : {
55 0 : gui_sketch_card_painter_private_draw_edit_mode( this_, drag_state, card_under_mouse, cr );
56 : }
57 0 : break;
58 :
59 0 : case GUI_TOOL_CREATE:
60 : {
61 0 : const data_id_t highlighted = gui_marked_set_get_highlighted( marked_objects );
62 0 : const layout_subelement_kind_t highlighted_kind = gui_marked_set_get_highlighted_kind( marked_objects );
63 0 : gui_sketch_card_painter_private_draw_create_mode( this_,
64 : drag_state,
65 : card_under_mouse,
66 : data_id_get_table(&highlighted),
67 : highlighted_kind,
68 : cr
69 : );
70 : }
71 0 : break;
72 :
73 0 : case GUI_TOOL_SEARCH:
74 : {
75 : }
76 0 : break;
77 :
78 0 : default:
79 : {
80 0 : U8_LOG_ERROR("selected_tool is out of range");
81 : }
82 0 : break;
83 : }
84 :
85 0 : U8_TRACE_END();
86 0 : }
87 :
88 0 : void gui_sketch_card_painter_private_draw_nav_mode( gui_sketch_card_painter_t *this_,
89 : const gui_sketch_drag_state_t *drag_state,
90 : const gui_sketch_card_t *card_under_mouse,
91 : cairo_t *cr )
92 : {
93 0 : U8_TRACE_BEGIN();
94 0 : assert( NULL != drag_state );
95 0 : assert( NULL != cr );
96 :
97 0 : if ( gui_sketch_drag_state_is_waiting_for_move ( drag_state ) )
98 : {
99 0 : const data_full_id_t *const dragged_object = gui_sketch_drag_state_get_dragged_object_const( drag_state );
100 0 : const data_id_t *const dragged_primary = data_full_id_get_primary_id_const( dragged_object );
101 0 : const bool dragging_diagram = ( DATA_TABLE_DIAGRAM == data_id_get_table( dragged_primary ) );
102 0 : if ( dragging_diagram )
103 : {
104 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
105 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
106 0 : GdkTexture *icon = gui_resources_get_sketch_move_v( (*this_).resources );
107 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
108 : }
109 : }
110 :
111 0 : U8_TRACE_END();
112 0 : }
113 :
114 0 : void gui_sketch_card_painter_private_draw_edit_mode( gui_sketch_card_painter_t *this_,
115 : const gui_sketch_drag_state_t *drag_state,
116 : const gui_sketch_card_t *card_under_mouse,
117 : cairo_t *cr )
118 : {
119 0 : U8_TRACE_BEGIN();
120 0 : assert( NULL != drag_state );
121 0 : assert( NULL != cr );
122 :
123 0 : if ( gui_sketch_drag_state_is_dragging ( drag_state ) )
124 : {
125 0 : const data_full_id_t *const dragged_object = gui_sketch_drag_state_get_dragged_object_const( drag_state );
126 0 : const data_id_t *const dragged_primary = data_full_id_get_primary_id_const( dragged_object );
127 0 : const bool dragging_classifier = ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( dragged_primary ) );
128 :
129 0 : if ( NULL != card_under_mouse )
130 : {
131 0 : const data_diagram_t *diag = gui_sketch_card_get_diagram_const ( card_under_mouse );
132 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type( diag );
133 0 : const bool draw_2d_diagram
134 : = (( diag_type != DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM )
135 0 : && ( diag_type != DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM )
136 0 : && ( diag_type != DATA_DIAGRAM_TYPE_LIST ));
137 :
138 0 : if ( draw_2d_diagram && dragging_classifier )
139 : {
140 : /* draw new grid lines that visualize the order of classifiers */
141 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
142 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
143 0 : gui_sketch_card_painter_private_visualize_order( this_, card_under_mouse, to_x, to_y, cr );
144 : }
145 : }
146 : }
147 :
148 0 : if ( gui_sketch_drag_state_is_waiting_for_move ( drag_state ) )
149 : {
150 0 : const data_full_id_t *const dragged_object = gui_sketch_drag_state_get_dragged_object_const( drag_state );
151 0 : const data_id_t *const dragged_primary = data_full_id_get_primary_id_const( dragged_object );
152 0 : const bool dragging_classifier = ( DATA_TABLE_DIAGRAMELEMENT == data_id_get_table( dragged_primary ) );
153 0 : const bool dragging_feature = ( DATA_TABLE_FEATURE == data_id_get_table( dragged_primary ) );
154 0 : const bool dragging_relationship = ( DATA_TABLE_RELATIONSHIP == data_id_get_table( dragged_primary ) );
155 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
156 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
157 0 : if ( dragging_classifier || dragging_feature )
158 0 : {
159 0 : GdkTexture *icon = gui_resources_get_sketch_move_2d( (*this_).resources );
160 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
161 : }
162 0 : else if ( dragging_relationship )
163 : {
164 0 : const data_diagram_t *diag = gui_sketch_card_get_diagram_const ( card_under_mouse );
165 0 : if ( NULL != card_under_mouse )
166 : {
167 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type( diag );
168 0 : if ( diag_type == DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM )
169 : {
170 0 : GdkTexture *icon = gui_resources_get_sketch_move_v( (*this_).resources );
171 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
172 : }
173 0 : else if ( diag_type == DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM )
174 : {
175 0 : GdkTexture *icon = gui_resources_get_sketch_move_h( (*this_).resources );
176 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
177 : }
178 : }
179 : }
180 : }
181 :
182 0 : U8_TRACE_END();
183 0 : }
184 :
185 0 : void gui_sketch_card_painter_private_draw_create_mode( gui_sketch_card_painter_t *this_,
186 : const gui_sketch_drag_state_t *drag_state,
187 : const gui_sketch_card_t *card_under_mouse,
188 : data_table_t highlighted_table,
189 : layout_subelement_kind_t highlighted_kind,
190 : cairo_t *cr )
191 : {
192 0 : U8_TRACE_BEGIN();
193 0 : assert( NULL != drag_state );
194 0 : assert( NULL != cr );
195 :
196 0 : if ( gui_sketch_drag_state_is_dragging ( drag_state ) )
197 : {
198 : /* draw a line */
199 0 : const int32_t from_x = gui_sketch_drag_state_get_from_x ( drag_state );
200 0 : const int32_t from_y = gui_sketch_drag_state_get_from_y ( drag_state );
201 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
202 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
203 0 : gui_sketch_card_painter_private_draw_arrow( this_, from_x, from_y, to_x, to_y, cr );
204 : }
205 : else /* ! gui_sketch_drag_state_is_dragging() */
206 : {
207 0 : if ( NULL != card_under_mouse )
208 : {
209 0 : const bool draw_new_classifier
210 0 : = (( highlighted_table == DATA_TABLE_CLASSIFIER )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_SPACE ))
211 0 : || (( highlighted_table == DATA_TABLE_DIAGRAMELEMENT )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_SPACE ))
212 0 : || (( highlighted_table == DATA_TABLE_DIAGRAM )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_SPACE ));
213 0 : const bool draw_new_feature
214 0 : = (( highlighted_table == DATA_TABLE_CLASSIFIER )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_OUTLINE ))
215 0 : || (( highlighted_table == DATA_TABLE_DIAGRAMELEMENT )&&( highlighted_kind == LAYOUT_SUBELEMENT_KIND_OUTLINE ));
216 0 : const bool draw_new_relationship
217 0 : = (( highlighted_table == DATA_TABLE_CLASSIFIER )&&( highlighted_kind != LAYOUT_SUBELEMENT_KIND_SPACE ))
218 0 : || (( highlighted_table == DATA_TABLE_DIAGRAMELEMENT )&&( highlighted_kind != LAYOUT_SUBELEMENT_KIND_SPACE ))
219 0 : || ( highlighted_table == DATA_TABLE_FEATURE );
220 :
221 0 : const data_diagram_t *diag = gui_sketch_card_get_diagram_const ( card_under_mouse );
222 0 : const data_diagram_type_t diag_type = data_diagram_get_diagram_type( diag );
223 0 : const bool draw_2d_diagram
224 : = (( diag_type != DATA_DIAGRAM_TYPE_UML_SEQUENCE_DIAGRAM )
225 0 : && ( diag_type != DATA_DIAGRAM_TYPE_UML_TIMING_DIAGRAM )
226 0 : && ( diag_type != DATA_DIAGRAM_TYPE_LIST ));
227 :
228 : /* get coordinates */
229 0 : const int32_t to_x = gui_sketch_drag_state_get_to_x ( drag_state );
230 0 : const int32_t to_y = gui_sketch_drag_state_get_to_y ( drag_state );
231 :
232 : /* draw grid */
233 0 : if (( draw_new_classifier )&&( draw_2d_diagram ))
234 : {
235 : /* draw new grid lines that visualize the order of classifiers */
236 0 : gui_sketch_card_painter_private_visualize_order( this_, card_under_mouse, to_x, to_y, cr );
237 : }
238 :
239 : /* draw boxes and arrow icons */
240 0 : if ( draw_new_feature )
241 : {
242 0 : assert( draw_new_relationship );
243 0 : assert( ! draw_new_classifier );
244 0 : GdkTexture *icon = gui_resources_get_sketch_refine( (*this_).resources );
245 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
246 : }
247 0 : else if ( draw_new_relationship )
248 : {
249 0 : assert( ! draw_new_classifier );
250 0 : GdkTexture *icon = gui_resources_get_sketch_relate( (*this_).resources );
251 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
252 : }
253 0 : else if ( draw_new_classifier )
254 : {
255 0 : GdkTexture *icon = gui_resources_get_sketch_create( (*this_).resources );
256 0 : gui_sketch_texture_draw( (*this_).texture_downloader, icon, to_x-16, to_y-16-2, cr ); /* 2 is extra gap*/
257 : }
258 : else
259 : {
260 : /* draw nothing */
261 : }
262 : }
263 : }
264 :
265 0 : U8_TRACE_END();
266 0 : }
267 :
268 0 : void gui_sketch_card_painter_private_draw_arrow( gui_sketch_card_painter_t *this_,
269 : int32_t from_x,
270 : int32_t from_y,
271 : int32_t to_x,
272 : int32_t to_y,
273 : cairo_t *cr )
274 : {
275 0 : U8_TRACE_BEGIN();
276 0 : assert( NULL != cr );
277 :
278 0 : const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
279 0 : cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
280 0 : cairo_move_to ( cr, from_x, from_y );
281 0 : cairo_line_to ( cr, to_x, to_y );
282 0 : cairo_stroke (cr);
283 :
284 : /* draw the arrow tip */
285 : int clock_direction; /* the gdk coordinate system if bottom up - and out clock is also mirrored! */
286 0 : const int32_t x_dist = to_x - from_x;
287 0 : const int32_t y_dist = to_y - from_y;
288 0 : if ( x_dist == 0 )
289 : {
290 0 : clock_direction = ( y_dist < 0 ) ? 6 : 0;
291 : }
292 : else
293 : {
294 0 : int32_t y_x_deci_ratio = ( y_dist * 10 ) / x_dist;
295 0 : if ( y_x_deci_ratio < 3 /* = 0.26 = tan(15 deg) */ )
296 : {
297 0 : if ( y_x_deci_ratio < -10 /* = -1.00 = tan(-45 deg) */ )
298 : {
299 0 : if ( y_x_deci_ratio < -37 /* = -3.73 = tan(-75 deg) */ )
300 : {
301 0 : clock_direction = 6;
302 : }
303 : else
304 : {
305 0 : clock_direction = 5;
306 : }
307 : }
308 : else
309 : {
310 0 : if ( y_x_deci_ratio < -2 /* = -0.26 = tan(-15 deg) */ )
311 : {
312 0 : clock_direction = 4;
313 : }
314 : else
315 : {
316 0 : clock_direction = 3;
317 : }
318 : }
319 : }
320 : else
321 : {
322 0 : if ( y_x_deci_ratio < 10 /* = 1.00 = tan(45 deg) */ )
323 : {
324 0 : clock_direction = 2;
325 : }
326 : else
327 : {
328 0 : if ( y_x_deci_ratio < 38 /* = 3.73 = tan(75 deg) */ )
329 : {
330 0 : clock_direction = 1;
331 : }
332 : else
333 : {
334 0 : clock_direction = 0;
335 : }
336 : }
337 : }
338 0 : if ( x_dist < 0 )
339 : {
340 0 : clock_direction = (clock_direction+6)%12;
341 : }
342 : }
343 : const static int32_t DX[12][2]={{-6,+6},{-9, 0},{-11,-4},{-9,-9},{-4,-11},{ 0,-9},{+6,-6},{+9, 0},{+11,+4},{+9,+9},{+4,+11},{ 0,+9}};
344 : const static int32_t DY[12][2]={{-9,-9},{-4,-11},{ 0,-9},{+6,-6},{+9, 0},{+11,+4},{+9,+9},{+4,+11},{ 0,+9},{+6,-6},{-9, 0},{-11,-4}};
345 0 : cairo_move_to ( cr, to_x + DX[clock_direction][0], to_y + DY[clock_direction][0] );
346 0 : cairo_line_to ( cr, to_x, to_y );
347 0 : cairo_line_to ( cr, to_x + DX[clock_direction][1], to_y + DY[clock_direction][1] );
348 0 : cairo_stroke (cr);
349 :
350 0 : U8_TRACE_END();
351 0 : }
352 :
353 0 : void gui_sketch_card_painter_private_visualize_order( gui_sketch_card_painter_t *this_,
354 : const gui_sketch_card_t *card_under_mouse,
355 : int32_t x,
356 : int32_t y,
357 : cairo_t *cr )
358 : {
359 0 : U8_TRACE_BEGIN();
360 0 : assert( NULL != card_under_mouse );
361 0 : assert( NULL != cr );
362 :
363 : /* fetch input data: subwidget */
364 0 : const shape_int_rectangle_t bounds = gui_sketch_card_get_bounds( card_under_mouse );
365 0 : const int32_t left = shape_int_rectangle_get_left(&bounds);
366 0 : const int32_t top = shape_int_rectangle_get_top(&bounds);
367 0 : const int32_t right = shape_int_rectangle_get_right(&bounds);
368 0 : const int32_t bottom = shape_int_rectangle_get_bottom(&bounds);
369 :
370 : /* fetch input data: grid */
371 0 : const geometry_grid_t *const grid = gui_sketch_card_get_grid_const( card_under_mouse);
372 0 : const geometry_grid_kind_t grid_kind = geometry_grid_get_kind( grid );
373 0 : const double snap_interval = gui_sketch_style_get_snap_to_grid( &((*this_).sketch_style) );
374 0 : const geometry_non_linear_scale_t *const x_scale = geometry_grid_get_x_scale_const( grid );
375 0 : const int32_t order_at_x = geometry_non_linear_scale_get_order( x_scale, (double) x, snap_interval );
376 0 : const geometry_non_linear_scale_t *const y_scale = geometry_grid_get_y_scale_const( grid );
377 0 : const int32_t order_at_y = geometry_non_linear_scale_get_order( y_scale, (double) y, snap_interval );
378 :
379 : /* define own distances */
380 0 : const double gap = 3.0;
381 0 : const double curve_space = 3.0 * gap;
382 :
383 : /* fetch input data: visible set */
384 0 : const layout_visible_set_t *const layout_data = gui_sketch_card_get_visible_set( card_under_mouse );
385 0 : const uint32_t classifiers = layout_visible_set_get_visible_classifier_count( layout_data );
386 :
387 : /* prepare draw */
388 0 : const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
389 0 : cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
390 :
391 : /* to draw x, follow y from top to bottom */
392 0 : if (( grid_kind == GEOMETRY_GRID_KIND_X )||( grid_kind == GEOMETRY_GRID_KIND_XY ))
393 : {
394 : /* order visible set by top y coordinates of classifiers */
395 : universal_array_index_sorter_t sorted;
396 0 : universal_array_index_sorter_init( &sorted );
397 0 : for ( uint32_t idx = 0; idx < classifiers; idx ++ )
398 : {
399 : const layout_visible_classifier_t *const classifier
400 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
401 :
402 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
403 0 : const int64_t weight = geometry_rectangle_get_top( envelope );
404 0 : const u8_error_t insert_error = universal_array_index_sorter_insert( &sorted, idx, weight );
405 0 : if ( U8_ERROR_NONE != insert_error )
406 : {
407 0 : U8_LOG_WARNING( "not all rectangles are considered for the top-to-bottom line" );
408 : }
409 : }
410 :
411 : /* process rectangles top to bottom */
412 : /* local variables represent 3 points: */
413 : /* current_pos_x / current_pos_y : The position where the already drawn line ends */
414 : /* planned_pos_x / planned_pos_y : The position that would be the next unless the next loop finds an earlier position */
415 : /* draw_to_pos_x / draw_to_pos_y : While drawing, this is a temporary value storing the next current_pos position */
416 0 : double current_pos_x = x;
417 0 : double current_pos_y = top;
418 0 : double planned_pos_x = x;
419 0 : double planned_pos_y = top; /* the planned next y - may be superseded by the next classifier */
420 0 : cairo_move_to( cr, x, top );
421 0 : const uint32_t count_sorted = universal_array_index_sorter_get_count( &sorted );
422 0 : for ( uint32_t idx_of_idx = 0; idx_of_idx < count_sorted; idx_of_idx ++ )
423 : {
424 0 : const uint32_t idx = universal_array_index_sorter_get_array_index( &sorted, idx_of_idx );
425 : const layout_visible_classifier_t *const classifier
426 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
427 :
428 : /* skip classifiers which contain others, these are not positioned according to their order value */
429 0 : const uint32_t children = layout_visible_set_count_descendants( layout_data, classifier );
430 0 : if ( children == 0 )
431 : {
432 0 : const data_visible_classifier_t *const vis_data = layout_visible_classifier_get_data_const( classifier );
433 0 : const data_classifier_t *const c_data = data_visible_classifier_get_classifier_const( vis_data );
434 0 : const int32_t c_x_order = data_classifier_get_x_order( c_data );
435 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
436 0 : const double envelope_left = geometry_rectangle_get_left( envelope );
437 0 : const double envelope_right = geometry_rectangle_get_right( envelope );
438 0 : const double envelope_top = geometry_rectangle_get_top( envelope );
439 0 : const double envelope_bottom = geometry_rectangle_get_bottom( envelope );
440 : /* if line passes through the classifier, center on symbol, not on envelope */
441 0 : const geometry_rectangle_t *const symbol = layout_visible_classifier_get_symbol_box_const( classifier );
442 0 : const double symbol_center_x = geometry_rectangle_get_center_x( symbol );
443 :
444 0 : const bool pass_through_center_x = ( c_x_order == order_at_x );
445 0 : const bool pass_on_left = ( c_x_order > order_at_x ) && ( envelope_left < x );
446 0 : const bool pass_on_right = ( c_x_order < order_at_x ) && ( envelope_right > x );
447 0 : const bool ignored = !( pass_through_center_x || pass_on_left || pass_on_right );
448 0 : const double draw_to_pos_y = envelope_top - gap;
449 0 : double draw_to_pos_x = x;
450 0 : if ( pass_through_center_x )
451 : {
452 0 : draw_to_pos_x = symbol_center_x;
453 : }
454 0 : if ( pass_on_left )
455 : {
456 0 : draw_to_pos_x = envelope_left - gap;
457 : }
458 0 : if ( pass_on_right )
459 : {
460 0 : draw_to_pos_x = envelope_right + gap;
461 : }
462 0 : if ( ! ignored )
463 : {
464 : /* if planned_pos_x not at x and enough space to draw */
465 : /* 1) a return to x and 2) a comeback to new draw_to_pos_x */
466 0 : if ( (( planned_pos_x < ( x - 0.001 ) )||( planned_pos_x > ( x + 0.001 ) ))
467 0 : && ( planned_pos_y + ( 2 * curve_space ) < draw_to_pos_y ) )
468 : {
469 0 : cairo_line_to( cr, current_pos_x, planned_pos_y );
470 0 : cairo_curve_to( cr,
471 : current_pos_x,
472 : planned_pos_y + curve_space,
473 : x,
474 : planned_pos_y,
475 : x,
476 : planned_pos_y + curve_space
477 : );
478 0 : current_pos_x = x;
479 0 : current_pos_y = planned_pos_y + curve_space;
480 : }
481 :
482 : /* if there is enough space to draw a line before drawing a curve to draw_to_pos_x */
483 0 : if ( ( current_pos_y + curve_space ) < draw_to_pos_y )
484 : {
485 0 : cairo_line_to( cr, current_pos_x, draw_to_pos_y - curve_space );
486 0 : current_pos_y = draw_to_pos_y - curve_space;
487 : }
488 :
489 : /* draw begin of current pass-by */
490 0 : cairo_curve_to( cr,
491 : current_pos_x,
492 : draw_to_pos_y,
493 : draw_to_pos_x,
494 : current_pos_y,
495 : draw_to_pos_x,
496 : draw_to_pos_y
497 : );
498 : /* update variables for next loop */
499 0 : current_pos_x = draw_to_pos_x;
500 0 : current_pos_y = draw_to_pos_y;
501 0 : planned_pos_x = current_pos_x;
502 0 : planned_pos_y = envelope_bottom + gap;
503 : }
504 : }
505 : }
506 : /* draw end of last pass-by (if any) */
507 0 : if ( (( planned_pos_x < ( x - 0.001 ) )||( planned_pos_x > ( x + 0.001 ) ))
508 0 : && ( planned_pos_y + ( 1 * curve_space ) < bottom ) )
509 : {
510 0 : cairo_line_to( cr, current_pos_x, planned_pos_y );
511 0 : cairo_curve_to( cr,
512 : current_pos_x,
513 : planned_pos_y + curve_space,
514 : x,
515 : planned_pos_y,
516 : x,
517 : planned_pos_y + curve_space
518 : );
519 0 : current_pos_x = x;
520 0 : current_pos_y = planned_pos_y + curve_space;
521 : }
522 : /* draw to end point and stroke the drawn path */
523 0 : cairo_line_to( cr, current_pos_x, bottom );
524 0 : if ( ! geometry_non_linear_scale_is_order_on_grid( x_scale, order_at_x ) )
525 : {
526 : double dashes[2];
527 0 : dashes[0] = 2.0 * gap; /* on segment */
528 0 : dashes[1] = 4.0 * gap; /* off segment */
529 0 : cairo_set_dash ( cr, dashes, 2, 0.0 );
530 : }
531 0 : cairo_stroke( cr );
532 0 : cairo_set_dash( cr, NULL, 0, 0.0 );
533 : }
534 :
535 : /* to draw y, follow x from left to right */
536 0 : if (( grid_kind == GEOMETRY_GRID_KIND_Y )||( grid_kind == GEOMETRY_GRID_KIND_XY ))
537 : {
538 : /* order visible set by left x coordinates of classifiers */
539 : universal_array_index_sorter_t sorted;
540 0 : universal_array_index_sorter_init( &sorted );
541 0 : for ( uint32_t idx = 0; idx < classifiers; idx ++ )
542 : {
543 : const layout_visible_classifier_t *const classifier
544 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
545 :
546 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
547 0 : const int64_t weight = geometry_rectangle_get_left( envelope );
548 0 : const u8_error_t insert_error = universal_array_index_sorter_insert( &sorted, idx, weight );
549 0 : if ( U8_ERROR_NONE != insert_error )
550 : {
551 0 : U8_LOG_WARNING( "not all rectangles are considered for the left-to-right line" );
552 : }
553 : }
554 :
555 : /* process rectangles left to right */
556 : /* local variables represent 3 points: */
557 : /* current_pos_x / current_pos_y : The position where the already drawn line ends */
558 : /* planned_pos_x / planned_pos_y : The position that would be the next unless the next loop finds an earlier position */
559 : /* draw_to_pos_x / draw_to_pos_y : While drawing, this is a temporary value storing the next current_pos position */
560 0 : double current_pos_x = left;
561 0 : double current_pos_y = y;
562 0 : double planned_pos_x = left; /* the planned next x - may be superseded by the next classifier */
563 0 : double planned_pos_y = y;
564 0 : cairo_move_to( cr, left, y );
565 0 : const uint32_t count_sorted = universal_array_index_sorter_get_count( &sorted );
566 0 : for ( uint32_t idx_of_idx = 0; idx_of_idx < count_sorted; idx_of_idx ++ )
567 : {
568 0 : const uint32_t idx = universal_array_index_sorter_get_array_index( &sorted, idx_of_idx );
569 : const layout_visible_classifier_t *const classifier
570 0 : = layout_visible_set_get_visible_classifier_const( layout_data, idx );
571 :
572 : /* skip classifiers which contain others, these are not positioned according to their order value */
573 0 : const uint32_t children = layout_visible_set_count_descendants( layout_data, classifier );
574 0 : if ( children == 0 )
575 : {
576 0 : const data_visible_classifier_t *const vis_data = layout_visible_classifier_get_data_const( classifier );
577 0 : const data_classifier_t *const c_data = data_visible_classifier_get_classifier_const( vis_data );
578 0 : const int32_t c_y_order = data_classifier_get_y_order( c_data );
579 0 : const geometry_rectangle_t *const envelope = layout_visible_classifier_get_envelope_box_const( classifier );
580 0 : const double envelope_top = geometry_rectangle_get_top( envelope );
581 0 : const double envelope_bottom = geometry_rectangle_get_bottom( envelope );
582 0 : const double envelope_left = geometry_rectangle_get_left( envelope );
583 0 : const double envelope_right = geometry_rectangle_get_right( envelope );
584 : /* if line passes through the classifier, center on symbol, not on envelope */
585 0 : const geometry_rectangle_t *const symbol = layout_visible_classifier_get_symbol_box_const( classifier );
586 0 : const double symbol_center_y = geometry_rectangle_get_center_y( symbol );
587 :
588 0 : const bool pass_through_center_y = ( c_y_order == order_at_y );
589 0 : const bool pass_on_top = ( c_y_order > order_at_y ) && ( envelope_top < y );
590 0 : const bool pass_on_bottom = ( c_y_order < order_at_y ) && ( envelope_bottom > y );
591 0 : const bool ignored = !( pass_through_center_y || pass_on_top || pass_on_bottom );
592 0 : const double draw_to_pos_x = envelope_left - gap;
593 0 : double draw_to_pos_y = y;
594 0 : if ( pass_through_center_y )
595 : {
596 0 : draw_to_pos_y = symbol_center_y;
597 : }
598 0 : if ( pass_on_top )
599 : {
600 0 : draw_to_pos_y = envelope_top - gap;
601 : }
602 0 : if ( pass_on_bottom )
603 : {
604 0 : draw_to_pos_y = envelope_bottom + gap;
605 : }
606 0 : if ( ! ignored )
607 : {
608 : /* if planned_pos_y not at y and enough space to draw */
609 : /* 1) a return to y and 2) a comeback to new draw_to_pos_y */
610 0 : if ( (( planned_pos_y < ( y - 0.001 ) )||( planned_pos_y > ( y + 0.001 ) ))
611 0 : && ( planned_pos_x + ( 2 * curve_space ) < draw_to_pos_x ) )
612 : {
613 0 : cairo_line_to( cr, planned_pos_x, current_pos_y );
614 0 : cairo_curve_to( cr,
615 : planned_pos_x + curve_space,
616 : current_pos_y,
617 : planned_pos_x,
618 : y,
619 : planned_pos_x + curve_space,
620 : y
621 : );
622 0 : current_pos_x = planned_pos_x + curve_space;
623 0 : current_pos_y = y;
624 : }
625 :
626 : /* if there is enough space to draw a line before drawing a curve to draw_to_pos_x */
627 0 : if ( ( current_pos_x + curve_space ) < draw_to_pos_x )
628 : {
629 0 : cairo_line_to( cr, draw_to_pos_x - curve_space, current_pos_y );
630 0 : current_pos_x = draw_to_pos_x - curve_space;
631 : }
632 :
633 : /* draw begin of current pass-by */
634 0 : cairo_curve_to( cr,
635 : draw_to_pos_x,
636 : current_pos_y,
637 : current_pos_x,
638 : draw_to_pos_y,
639 : draw_to_pos_x,
640 : draw_to_pos_y
641 : );
642 : /* update variables for next loop */
643 0 : current_pos_x = draw_to_pos_x;
644 0 : current_pos_y = draw_to_pos_y;
645 0 : planned_pos_x = envelope_right + gap;
646 0 : planned_pos_y = current_pos_y;
647 : }
648 : }
649 : }
650 : /* draw end of last pass-by (if any) */
651 0 : if ( (( planned_pos_y < ( y - 0.001 ) )||( planned_pos_y > ( y + 0.001 ) ))
652 0 : && ( planned_pos_x + ( 1 * curve_space ) < right ) )
653 : {
654 0 : cairo_line_to( cr, planned_pos_x, current_pos_y );
655 0 : cairo_curve_to( cr,
656 : planned_pos_x + curve_space,
657 : current_pos_y,
658 : planned_pos_x,
659 : y,
660 : planned_pos_x + curve_space,
661 : y
662 : );
663 0 : current_pos_x = planned_pos_x + curve_space;
664 0 : current_pos_y = y;
665 : }
666 : /* draw to end point and stroke the drawn path */
667 0 : cairo_line_to( cr, right, current_pos_y );
668 0 : if ( ! geometry_non_linear_scale_is_order_on_grid( y_scale, order_at_y ) )
669 : {
670 : double dashes[2];
671 0 : dashes[0] = 2.0 * gap; /* on segment */
672 0 : dashes[1] = 4.0 * gap; /* off segment */
673 0 : cairo_set_dash ( cr, dashes, 2, 0.0 );
674 : }
675 0 : cairo_stroke( cr );
676 0 : cairo_set_dash( cr, NULL, 0, 0.0 );
677 : }
678 :
679 0 : U8_TRACE_END();
680 0 : }
681 :
682 0 : void gui_sketch_card_painter_private_draw_element_space( const gui_sketch_card_painter_t *this_,
683 : const layout_subelement_id_t *subelement,
684 : const layout_visible_set_t *layouted_set,
685 : cairo_t *cr )
686 : {
687 0 : assert( NULL != subelement );
688 0 : assert( NULL != layouted_set );
689 0 : assert( NULL != cr );
690 0 : const layout_diagram_t *const layout_diag = layout_visible_set_get_diagram_const( layouted_set );
691 0 : const double label_border = 2.0; /* additional visible border that just draws a box nicely, without effect for selecting */
692 0 : const double connector_border = (double) gui_sketch_style_get_snap_to_relationship( &((*this_).sketch_style) );
693 :
694 : geometry_rectangle_t highlight;
695 0 : geometry_rectangle_init_empty( &highlight );
696 : data_id_t search_id;
697 0 : data_id_copy( &search_id, data_full_id_get_primary_id_const( layout_subelement_id_get_id_const( subelement ) ) );
698 0 : const layout_subelement_kind_t kind = layout_subelement_id_get_kind( subelement );
699 :
700 : /* check diagram */
701 0 : const data_diagram_t *const diag_data = layout_diagram_get_data_const( layout_diag );
702 0 : const data_id_t diag_id = data_diagram_get_data_id( diag_data );
703 0 : if ( data_id_equals( &search_id, &diag_id ) )
704 : {
705 0 : switch ( kind )
706 : {
707 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
708 : {
709 0 : geometry_rectangle_replace( &highlight, layout_diagram_get_label_box_const( layout_diag ) );
710 0 : geometry_rectangle_expand_4dir( &highlight, 4.0 /* delta_width */, 4.0 /* delta_height */ );
711 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
712 : }
713 0 : break;
714 :
715 0 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
716 : {
717 : geometry_rectangle_t empty_space;
718 0 : geometry_rectangle_replace( &highlight, layout_diagram_get_bounds_const( layout_diag ) );
719 0 : geometry_rectangle_copy( &empty_space, layout_diagram_get_draw_area_const( layout_diag ) ); /* no highlight in space */
720 0 : gui_sketch_card_painter_private_draw_border( this_, &highlight, &empty_space, cr );
721 : }
722 0 : break;
723 :
724 0 : default:
725 : case LAYOUT_SUBELEMENT_KIND_SPACE:
726 : {
727 0 : geometry_rectangle_replace( &highlight, layout_diagram_get_draw_area_const( layout_diag ) );
728 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
729 : }
730 0 : break;
731 : }
732 : }
733 :
734 : /* iterate over all classifiers */
735 0 : const uint32_t c_count = layout_visible_set_get_visible_classifier_count( layouted_set );
736 0 : for ( uint32_t c_index = 0; c_index < c_count; c_index ++ )
737 : {
738 : const layout_visible_classifier_t *const visible_classifier
739 0 : = layout_visible_set_get_visible_classifier_const ( layouted_set, c_index );
740 0 : const data_diagramelement_t *diagele = layout_visible_classifier_get_diagramelement_const( visible_classifier );
741 0 : const data_id_t diagele_id = data_diagramelement_get_data_id( diagele );
742 0 : if ( data_id_equals( &search_id, &diagele_id ) )
743 : {
744 0 : switch ( kind )
745 : {
746 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
747 : {
748 0 : geometry_rectangle_replace( &highlight, layout_visible_classifier_get_label_box_const( visible_classifier ) );
749 0 : geometry_rectangle_expand_4dir( &highlight, 4.0 /* delta_width */, 4.0 /* delta_height */ );
750 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
751 : }
752 0 : break;
753 :
754 0 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
755 : {
756 : geometry_rectangle_t empty_space;
757 0 : geometry_rectangle_replace( &highlight, layout_visible_classifier_get_symbol_box_const( visible_classifier ) );
758 0 : geometry_rectangle_copy( &empty_space, layout_visible_classifier_get_space_const( visible_classifier ) ); /* no highlight in space */
759 0 : gui_sketch_card_painter_private_draw_border( this_, &highlight, &empty_space, cr );
760 : }
761 0 : break;
762 :
763 0 : default:
764 : case LAYOUT_SUBELEMENT_KIND_SPACE:
765 : {
766 0 : geometry_rectangle_replace( &highlight, layout_visible_classifier_get_space_const( visible_classifier ) );
767 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
768 : }
769 0 : break;
770 : }
771 : }
772 : }
773 :
774 : /* check all contained features */
775 0 : const uint32_t f_count = layout_visible_set_get_feature_count( layouted_set );
776 0 : for ( uint32_t f_index = 0; f_index < f_count; f_index ++ )
777 : {
778 : const layout_feature_t *const the_feature
779 0 : = layout_visible_set_get_feature_const ( layouted_set, f_index );
780 0 : const data_feature_t *feature = layout_feature_get_data_const( the_feature );
781 0 : const data_id_t feature_id = data_feature_get_data_id( feature );
782 0 : if ( data_id_equals( &search_id, &feature_id ) )
783 : {
784 0 : switch ( kind )
785 : {
786 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
787 : {
788 0 : geometry_rectangle_replace( &highlight, layout_feature_get_label_box_const( the_feature ) );
789 0 : geometry_rectangle_expand_4dir( &highlight, 2.0 * label_border, 2.0 * label_border );
790 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
791 : }
792 0 : break;
793 :
794 0 : default:
795 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
796 : {
797 0 : geometry_rectangle_replace( &highlight, layout_feature_get_symbol_box_const( the_feature ) );
798 0 : geometry_rectangle_expand_4dir( &highlight, 2.0 * label_border, 2.0 * label_border );
799 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
800 : }
801 0 : break;
802 : }
803 : }
804 : }
805 :
806 0 : const uint32_t count_relations = layout_visible_set_get_relationship_count ( layouted_set );
807 0 : for ( uint32_t rel_index = 0; rel_index < count_relations; rel_index ++ )
808 : {
809 : const layout_relationship_t *const the_relationship
810 0 : = layout_visible_set_get_relationship_const( layouted_set, rel_index );
811 0 : const data_relationship_t *const relationship = layout_relationship_get_data_const( the_relationship );
812 0 : const data_id_t relationship_id = data_relationship_get_data_id( relationship );
813 0 : if ( data_id_equals( &search_id, &relationship_id ) )
814 : {
815 0 : switch ( kind )
816 : {
817 0 : case LAYOUT_SUBELEMENT_KIND_LABEL:
818 : {
819 0 : geometry_rectangle_replace( &highlight, layout_relationship_get_label_box_const( the_relationship ) );
820 0 : geometry_rectangle_expand_4dir( &highlight, 2.0 * label_border, 2.0 * label_border );
821 0 : gui_sketch_card_painter_private_draw_rect( this_, &highlight, cr );
822 : }
823 0 : break;
824 :
825 0 : default:
826 : case LAYOUT_SUBELEMENT_KIND_OUTLINE:
827 : {
828 : const geometry_connector_t *const relationship_shape
829 0 : = layout_relationship_get_shape_const( the_relationship );
830 : {
831 : geometry_rectangle_t segment_src;
832 0 : segment_src = geometry_connector_get_segment_bounds( relationship_shape,
833 : GEOMETRY_CONNECTOR_SEGMENT_SOURCE
834 : );
835 0 : geometry_rectangle_expand_4dir( &segment_src, 2.0 * connector_border, 2.0 * connector_border );
836 0 : gui_sketch_card_painter_private_draw_rect( this_, &segment_src, cr );
837 : }
838 : {
839 : geometry_rectangle_t segment_dst;
840 0 : segment_dst = geometry_connector_get_segment_bounds( relationship_shape,
841 : GEOMETRY_CONNECTOR_SEGMENT_DESTINATION
842 : );
843 0 : geometry_rectangle_expand_4dir( &segment_dst, 2.0 * connector_border, 2.0 * connector_border );
844 0 : gui_sketch_card_painter_private_draw_rect( this_, &segment_dst, cr );
845 : }
846 : {
847 : geometry_rectangle_t segment_main;
848 0 : segment_main = geometry_connector_get_segment_bounds( relationship_shape,
849 : GEOMETRY_CONNECTOR_SEGMENT_MAIN
850 : );
851 0 : geometry_rectangle_expand_4dir( &segment_main, 2.0 * connector_border, 2.0 * connector_border );
852 0 : gui_sketch_card_painter_private_draw_rect( this_, &segment_main, cr );
853 : }
854 : }
855 0 : break;
856 : }
857 : }
858 : }
859 0 : }
860 :
861 0 : void gui_sketch_card_painter_draw_paper( gui_sketch_card_painter_t *this_,
862 : gui_tool_t selected_tool,
863 : const gui_sketch_drag_state_t *drag_state,
864 : const gui_sketch_card_t *card,
865 : cairo_t *cr )
866 : {
867 0 : U8_TRACE_BEGIN();
868 0 : assert( NULL != drag_state );
869 0 : assert( NULL != card );
870 0 : assert( NULL != cr );
871 :
872 0 : if ( gui_sketch_card_is_visible(card) )
873 : {
874 0 : const bool create_tool = ( selected_tool == GUI_TOOL_CREATE );
875 :
876 0 : shape_int_rectangle_t bounds = gui_sketch_card_get_bounds( card );
877 0 : const int32_t left = shape_int_rectangle_get_left( &bounds );
878 0 : const int32_t top = shape_int_rectangle_get_top( &bounds );
879 0 : const uint32_t width = shape_int_rectangle_get_width( &bounds );
880 0 : const uint32_t height = shape_int_rectangle_get_height( &bounds );
881 :
882 : /* draw paper */
883 0 : if ( create_tool )
884 : {
885 : static const double GRAY_R = 0.875;
886 : static const double GRAY_G = 0.875;
887 : static const double GRAY_B = 0.875;
888 : static const double GRAY_A = 1.0;
889 0 : cairo_set_source_rgba( cr, GRAY_R, GRAY_G, GRAY_B, GRAY_A );
890 : }
891 : else
892 : {
893 : static const double WHITE_R = 1.0;
894 : static const double WHITE_G = 1.0;
895 : static const double WHITE_B = 1.0;
896 : static const double WHITE_A = 1.0;
897 0 : cairo_set_source_rgba( cr, WHITE_R, WHITE_G, WHITE_B, WHITE_A );
898 : }
899 0 : cairo_rectangle ( cr, left, top, width, height );
900 0 : cairo_fill (cr);
901 :
902 0 : const layout_visible_set_t *const layout = gui_sketch_card_get_visible_set( card );
903 0 : if ( layout_visible_set_is_valid( layout ) && create_tool )
904 : {
905 0 : const int32_t mouse_x = gui_sketch_drag_state_get_to_x( drag_state );
906 0 : const int32_t mouse_y = gui_sketch_drag_state_get_to_y( drag_state );
907 :
908 : const layout_subelement_id_t subelement_to_highlight
909 0 : = gui_sketch_card_get_element_at_pos( card, mouse_x, mouse_y, !create_tool /* filter_lifelines */ );
910 :
911 0 : gui_sketch_card_painter_private_draw_element_space( this_, &subelement_to_highlight, layout, cr );
912 : }
913 : }
914 :
915 0 : U8_TRACE_END();
916 0 : }
917 :
918 :
919 : /*
920 : Copyright 2017-2025 Andreas Warnke
921 :
922 : Licensed under the Apache License, Version 2.0 (the "License");
923 : you may not use this file except in compliance with the License.
924 : You may obtain a copy of the License at
925 :
926 : http://www.apache.org/licenses/LICENSE-2.0
927 :
928 : Unless required by applicable law or agreed to in writing, software
929 : distributed under the License is distributed on an "AS IS" BASIS,
930 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
931 : See the License for the specific language governing permissions and
932 : limitations under the License.
933 : */
|