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