Line data Source code
1 : /* File: pencil_feature_painter.c; Copyright and License: see below */
2 :
3 : #include "pencil_feature_painter.h"
4 : #include "layout/layout_visible_set.h"
5 : #include "u8/u8_trace.h"
6 : #include <pango/pangocairo.h>
7 : #include <stdio.h>
8 : #include <stdlib.h>
9 : #include <assert.h>
10 :
11 : /*! where to place the control points of a bezier curve to get a good approximation for a 90 degree curve */
12 : const static double BEZIER_CTRL_POINT_FOR_90_DEGREE_CIRCLE = 0.552284749831;
13 : const static double SINE_OF_45_DEGREE = 0.707106781187;
14 :
15 0 : void pencil_feature_painter_init( pencil_feature_painter_t *this_ )
16 : {
17 0 : U8_TRACE_BEGIN();
18 :
19 0 : pencil_marker_init( &((*this_).marker) );
20 0 : draw_feature_label_init( &((*this_).draw_feature_label) );
21 :
22 0 : U8_TRACE_END();
23 0 : }
24 :
25 0 : void pencil_feature_painter_destroy( pencil_feature_painter_t *this_ )
26 : {
27 0 : U8_TRACE_BEGIN();
28 :
29 0 : draw_feature_label_destroy( &((*this_).draw_feature_label) );
30 0 : pencil_marker_destroy( &((*this_).marker) );
31 :
32 0 : U8_TRACE_END();
33 0 : }
34 :
35 0 : void pencil_feature_painter_draw( pencil_feature_painter_t *this_,
36 : const layout_feature_t *layouted_feature,
37 : bool mark_focused,
38 : bool mark_highlighted,
39 : bool mark_selected,
40 : bool gray_out,
41 : const data_profile_part_t *profile,
42 : const pencil_size_t *pencil_size,
43 : PangoLayout *layout,
44 : cairo_t *cr )
45 : {
46 0 : U8_TRACE_BEGIN();
47 0 : assert( NULL != profile );
48 0 : assert( NULL != pencil_size );
49 0 : assert( NULL != layouted_feature );
50 0 : assert( NULL != layout );
51 0 : assert( NULL != cr );
52 :
53 0 : const data_feature_t *the_feature = layout_feature_get_data_const( layouted_feature );
54 0 : const geometry_rectangle_t *const feature_symbol_box = layout_feature_get_symbol_box_const( layouted_feature );
55 :
56 0 : if ( data_feature_is_valid( the_feature ) )
57 : {
58 0 : U8_TRACE_INFO_INT("drawing feature id", data_feature_get_row_id( the_feature ) );
59 :
60 : /* select color */
61 : GdkRGBA foreground_color;
62 : {
63 0 : if ( mark_highlighted )
64 : {
65 0 : foreground_color = pencil_size_get_highlight_color( pencil_size );
66 : }
67 0 : else if ( gray_out )
68 : {
69 0 : foreground_color = pencil_size_get_gray_out_color( pencil_size );
70 : }
71 : else
72 : {
73 0 : foreground_color = pencil_size_get_standard_color( pencil_size );
74 : }
75 0 : cairo_set_source_rgba( cr, foreground_color.red, foreground_color.green, foreground_color.blue, foreground_color.alpha );
76 : }
77 :
78 0 : switch ( data_feature_get_main_type (the_feature) )
79 : {
80 0 : case DATA_FEATURE_TYPE_PORT: /* or */
81 : case DATA_FEATURE_TYPE_IN_PORT_PIN: /* or */
82 : case DATA_FEATURE_TYPE_OUT_PORT_PIN:
83 : {
84 0 : pencil_feature_painter_private_draw_port_pin_icon( this_, layouted_feature, pencil_size, foreground_color, cr );
85 : }
86 0 : break;
87 :
88 0 : case DATA_FEATURE_TYPE_ENTRY: /* or */
89 : case DATA_FEATURE_TYPE_EXIT:
90 : {
91 0 : pencil_feature_painter_private_draw_entry_exit_icon( this_, layouted_feature, pencil_size, foreground_color, cr );
92 : }
93 0 : break;
94 :
95 0 : case DATA_FEATURE_TYPE_PROVIDED_INTERFACE: /* or */
96 : case DATA_FEATURE_TYPE_REQUIRED_INTERFACE:
97 : {
98 0 : pencil_feature_painter_private_draw_interface_icon( this_, layouted_feature, pencil_size, cr );
99 : }
100 0 : break;
101 :
102 0 : case DATA_FEATURE_TYPE_LIFELINE:
103 : {
104 0 : pencil_feature_painter_private_draw_lifeline_icon( this_, layouted_feature, mark_highlighted, pencil_size, cr );
105 : }
106 0 : break;
107 :
108 0 : case DATA_FEATURE_TYPE_PROPERTY: /* or */
109 : case DATA_FEATURE_TYPE_OPERATION: /* or */
110 : case DATA_FEATURE_TYPE_TAGGED_VALUE:
111 : {
112 : /* no icon */
113 : }
114 0 : break;
115 :
116 0 : default:
117 : {
118 0 : U8_LOG_ANOMALY("unknown feature type in pencil_feature_painter_draw");
119 : /* this may happen if a new database file has been read by an old program version */
120 : /* no icon */
121 : }
122 0 : break;
123 : }
124 :
125 : /* draw the label */
126 0 : draw_feature_label_draw_key_and_value( &((*this_).draw_feature_label),
127 : layout_feature_get_data_const( layouted_feature ),
128 : profile,
129 : &foreground_color,
130 : layout_feature_get_label_box_const( layouted_feature ),
131 : pencil_size,
132 : layout,
133 : cr
134 : );
135 :
136 0 : if ( mark_selected )
137 : {
138 0 : pencil_marker_mark_selected_rectangle( &((*this_).marker), *feature_symbol_box, cr );
139 : }
140 :
141 0 : if ( mark_focused )
142 : {
143 0 : pencil_marker_mark_focused_rectangle( &((*this_).marker), *feature_symbol_box, cr );
144 : }
145 : }
146 : else
147 : {
148 0 : U8_LOG_ERROR("invalid visible feature in array!");
149 : }
150 :
151 0 : U8_TRACE_END();
152 0 : }
153 :
154 0 : void pencil_feature_painter_private_draw_lifeline_icon ( pencil_feature_painter_t *this_,
155 : const layout_feature_t *layouted_feature,
156 : bool marked,
157 : const pencil_size_t *pencil_size,
158 : cairo_t *cr )
159 : {
160 0 : U8_TRACE_BEGIN();
161 0 : assert( NULL != pencil_size );
162 0 : assert( NULL != layouted_feature );
163 0 : assert( NULL != cr );
164 :
165 0 : const geometry_rectangle_t *const feature_symbol_box = layout_feature_get_symbol_box_const( layouted_feature );
166 :
167 0 : const double left = geometry_rectangle_get_left ( feature_symbol_box );
168 0 : const double top = geometry_rectangle_get_top ( feature_symbol_box );
169 0 : const double width = geometry_rectangle_get_width ( feature_symbol_box );
170 0 : const double height = geometry_rectangle_get_height ( feature_symbol_box );
171 :
172 : double dashes[2];
173 0 : dashes[0] = 2.0 * pencil_size_get_line_dash_length( pencil_size );
174 0 : dashes[1] = 1.0 * pencil_size_get_line_dash_length( pencil_size );
175 0 : cairo_set_dash ( cr, dashes, 2, 0.0 );
176 :
177 0 : if ( GEOMETRY_DIRECTION_RIGHT == layout_feature_get_icon_direction( layouted_feature ) )
178 : {
179 : /* lineline in timing diagrams */
180 0 : const double center_y = geometry_rectangle_get_center_y ( feature_symbol_box );
181 :
182 0 : cairo_move_to ( cr, left, center_y );
183 0 : cairo_line_to ( cr, left + width, center_y );
184 0 : cairo_stroke (cr);
185 : }
186 0 : else if ( GEOMETRY_DIRECTION_DOWN == layout_feature_get_icon_direction( layouted_feature ) )
187 : {
188 : /* lifeline in sequence diagrams */
189 0 : const double center_x = geometry_rectangle_get_center_x ( feature_symbol_box );
190 :
191 0 : cairo_move_to ( cr, center_x, top );
192 0 : cairo_line_to ( cr, center_x, top + height );
193 0 : cairo_stroke (cr);
194 : }
195 : else
196 : {
197 : /* lifeline in communication diagrams, only drawn if highlighted: */
198 0 : if ( marked )
199 : {
200 0 : cairo_move_to ( cr, left, top );
201 0 : cairo_line_to ( cr, left, top + height );
202 0 : cairo_line_to ( cr, left + width, top + height );
203 0 : cairo_line_to ( cr, left + width, top );
204 0 : cairo_line_to ( cr, left, top );
205 0 : cairo_stroke (cr);
206 : }
207 : }
208 :
209 0 : cairo_set_dash ( cr, NULL, 0, 0.0 );
210 :
211 0 : U8_TRACE_END();
212 0 : }
213 :
214 0 : void pencil_feature_painter_private_draw_port_pin_icon ( pencil_feature_painter_t *this_,
215 : const layout_feature_t *layouted_feature,
216 : const pencil_size_t *pencil_size,
217 : GdkRGBA foreground_color,
218 : cairo_t *cr )
219 : {
220 0 : U8_TRACE_BEGIN();
221 0 : assert( NULL != pencil_size );
222 0 : assert( NULL != layouted_feature );
223 0 : assert( NULL != cr );
224 :
225 0 : const geometry_rectangle_t *const symbol_box_bounds = layout_feature_get_symbol_box_const( layouted_feature );
226 :
227 0 : const double left = geometry_rectangle_get_left ( symbol_box_bounds );
228 0 : const double top = geometry_rectangle_get_top ( symbol_box_bounds );
229 0 : const double width = geometry_rectangle_get_width ( symbol_box_bounds );
230 0 : const double height = geometry_rectangle_get_height ( symbol_box_bounds );
231 :
232 0 : cairo_rectangle ( cr, left, top, width, height );
233 :
234 : /* Note: It is possible to read out the current color and set it again */
235 : /* but the interface for that looks like this might result in 1 additional memory allocation */
236 : /* which shall be avoided */
237 : /* cairo_pattern_t *const defined_color = cairo_get_source( cr ); */
238 : /* cairo_pattern_reference( defined_color ); */
239 : /* ... */
240 : /* cairo_set_source( cr, defined_color ); */
241 : /* cairo_pattern_destroy( defined_color ); */
242 :
243 0 : cairo_set_source_rgba( cr, 1.0, 1.0, 1.0, 1.0 ); /* white background */
244 0 : cairo_fill_preserve (cr);
245 0 : cairo_set_source_rgba( cr, foreground_color.red, foreground_color.green, foreground_color.blue, foreground_color.alpha );
246 0 : cairo_stroke (cr);
247 :
248 : /* draw the arrow */
249 0 : const double center_x = geometry_rectangle_get_center_x ( symbol_box_bounds );
250 0 : const double center_y = geometry_rectangle_get_center_y ( symbol_box_bounds );
251 0 : const double h_arrow_left = left + 0.25*width;
252 0 : const double h_arrow_right = left + 0.75*width;
253 0 : const double h_arrow_top = top + 0.25*height;
254 0 : const double h_arrow_bottom = top + 0.75*height;
255 0 : const double v_arrow_left = left + 0.25*width;
256 0 : const double v_arrow_right = left + 0.75*width;
257 0 : const double v_arrow_top = top + 0.25*height;
258 0 : const double v_arrow_bottom = top + 0.75*height;
259 0 : switch ( layout_feature_get_icon_direction( layouted_feature ) )
260 : {
261 0 : case GEOMETRY_DIRECTION_LEFT:
262 : {
263 0 : cairo_move_to ( cr, h_arrow_left, center_y );
264 0 : cairo_line_to ( cr, h_arrow_right, center_y );
265 0 : cairo_move_to ( cr, h_arrow_right, h_arrow_top );
266 0 : cairo_line_to ( cr, h_arrow_left, center_y );
267 0 : cairo_line_to ( cr, h_arrow_right, h_arrow_bottom );
268 :
269 0 : cairo_stroke (cr);
270 : }
271 0 : break;
272 :
273 0 : case GEOMETRY_DIRECTION_UP:
274 : {
275 0 : cairo_move_to ( cr, center_x, v_arrow_top );
276 0 : cairo_line_to ( cr, center_x, v_arrow_bottom );
277 0 : cairo_move_to ( cr, v_arrow_left, v_arrow_bottom );
278 0 : cairo_line_to ( cr, center_x, v_arrow_top );
279 0 : cairo_line_to ( cr, v_arrow_right, v_arrow_bottom );
280 :
281 0 : cairo_stroke (cr);
282 : }
283 0 : break;
284 :
285 0 : case GEOMETRY_DIRECTION_RIGHT:
286 : {
287 0 : cairo_move_to ( cr, h_arrow_right, center_y );
288 0 : cairo_line_to ( cr, h_arrow_left, center_y );
289 0 : cairo_move_to ( cr, h_arrow_left, h_arrow_top );
290 0 : cairo_line_to ( cr, h_arrow_right, center_y );
291 0 : cairo_line_to ( cr, h_arrow_left, h_arrow_bottom );
292 :
293 0 : cairo_stroke (cr);
294 : }
295 0 : break;
296 :
297 0 : case GEOMETRY_DIRECTION_DOWN:
298 : {
299 0 : cairo_move_to ( cr, center_x, v_arrow_bottom );
300 0 : cairo_line_to ( cr, center_x, v_arrow_top );
301 0 : cairo_move_to ( cr, v_arrow_left, v_arrow_top );
302 0 : cairo_line_to ( cr, center_x, v_arrow_bottom );
303 0 : cairo_line_to ( cr, v_arrow_right, v_arrow_top );
304 :
305 0 : cairo_stroke (cr);
306 : }
307 0 : break;
308 :
309 0 : case GEOMETRY_DIRECTION_CENTER:
310 : {
311 : /* no arrow */
312 : }
313 0 : break;
314 :
315 0 : default:
316 : {
317 0 : U8_LOG_ERROR( "unexpected value in geometry_direction_t." );
318 : }
319 0 : break;
320 : }
321 :
322 0 : U8_TRACE_END();
323 0 : }
324 :
325 0 : void pencil_feature_painter_private_draw_entry_exit_icon ( pencil_feature_painter_t *this_,
326 : const layout_feature_t *layouted_feature,
327 : const pencil_size_t *pencil_size,
328 : GdkRGBA foreground_color,
329 : cairo_t *cr )
330 : {
331 0 : U8_TRACE_BEGIN();
332 0 : assert( NULL != pencil_size );
333 0 : assert( NULL != layouted_feature );
334 0 : assert( NULL != cr );
335 :
336 0 : const data_feature_t *the_feature = layout_feature_get_data_const( layouted_feature );
337 0 : const geometry_rectangle_t *const symbol_box_bounds = layout_feature_get_symbol_box_const( layouted_feature );
338 :
339 0 : const double left = geometry_rectangle_get_left ( symbol_box_bounds );
340 0 : const double top = geometry_rectangle_get_top ( symbol_box_bounds );
341 0 : const double center_x = geometry_rectangle_get_center_x( symbol_box_bounds );
342 0 : const double center_y = geometry_rectangle_get_center_y( symbol_box_bounds );
343 0 : const double circle_x_radius = center_x - left;
344 0 : const double circle_y_radius = center_y - top;
345 0 : const double bottom = geometry_rectangle_get_bottom( symbol_box_bounds );
346 0 : const double right = geometry_rectangle_get_right( symbol_box_bounds );
347 0 : const double ctrl_x_offset = circle_x_radius * (1.0-BEZIER_CTRL_POINT_FOR_90_DEGREE_CIRCLE);
348 0 : const double ctrl_y_offset = circle_y_radius * (1.0-BEZIER_CTRL_POINT_FOR_90_DEGREE_CIRCLE);
349 :
350 0 : cairo_move_to ( cr, center_x, bottom );
351 0 : cairo_curve_to ( cr, left + ctrl_x_offset, bottom, left, bottom - ctrl_y_offset, left /* end point x */, center_y /* end point y */ );
352 0 : cairo_curve_to ( cr, left, top + ctrl_y_offset, left + ctrl_x_offset, top, center_x /* end point x */, top /* end point y */ );
353 0 : cairo_curve_to ( cr, right - ctrl_x_offset, top, right, top + ctrl_y_offset, right /* end point x */, center_y /* end point y */ );
354 0 : cairo_curve_to ( cr, right, bottom - ctrl_y_offset, right - ctrl_x_offset, bottom, center_x /* end point x */, bottom /* end point y */ );
355 :
356 0 : cairo_set_source_rgba( cr, 1.0, 1.0, 1.0, 1.0 ); /* white background */
357 0 : cairo_fill_preserve (cr);
358 0 : cairo_set_source_rgba( cr, foreground_color.red, foreground_color.green, foreground_color.blue, foreground_color.alpha );
359 0 : cairo_stroke (cr);
360 :
361 : /* draw X of exit icon */
362 0 : if ( data_feature_get_main_type( the_feature ) == DATA_FEATURE_TYPE_EXIT )
363 : {
364 0 : const double half_width = geometry_rectangle_get_width ( symbol_box_bounds )/2.0;
365 0 : const double half_height = geometry_rectangle_get_height ( symbol_box_bounds )/2.0;
366 0 : const double cross_end_dx = half_width * SINE_OF_45_DEGREE;
367 0 : const double cross_end_dy = half_height * SINE_OF_45_DEGREE;
368 :
369 0 : cairo_move_to ( cr, center_x + cross_end_dx, center_y - cross_end_dy );
370 0 : cairo_line_to ( cr, center_x - cross_end_dx, center_y + cross_end_dy );
371 0 : cairo_move_to ( cr, center_x - cross_end_dx, center_y - cross_end_dy );
372 0 : cairo_line_to ( cr, center_x + cross_end_dx, center_y + cross_end_dy );
373 :
374 0 : cairo_stroke (cr);
375 : }
376 :
377 0 : U8_TRACE_END();
378 0 : }
379 :
380 0 : void pencil_feature_painter_private_draw_interface_icon ( pencil_feature_painter_t *this_,
381 : const layout_feature_t *layouted_feature,
382 : const pencil_size_t *pencil_size,
383 : cairo_t *cr )
384 : {
385 0 : U8_TRACE_BEGIN();
386 0 : assert( NULL != pencil_size );
387 0 : assert( NULL != layouted_feature );
388 0 : assert( NULL != cr );
389 :
390 0 : const geometry_rectangle_t *const symbol_box_bounds = layout_feature_get_symbol_box_const( layouted_feature );
391 :
392 0 : const double left = geometry_rectangle_get_left ( symbol_box_bounds );
393 0 : const double top = geometry_rectangle_get_top ( symbol_box_bounds );
394 0 : const double width = geometry_rectangle_get_width ( symbol_box_bounds );
395 0 : const double height = geometry_rectangle_get_height ( symbol_box_bounds );
396 :
397 0 : double bottom = top + height;
398 0 : double right = left + width;
399 0 : double half_width = 0.5 * width;
400 0 : double half_height = 0.5 * height;
401 0 : double center_x = left + half_width;
402 0 : double center_y = top + half_height;
403 0 : double ctrl_xoffset = half_width * (1.0-BEZIER_CTRL_POINT_FOR_90_DEGREE_CIRCLE);
404 0 : double ctrl_yoffset = half_height * (1.0-BEZIER_CTRL_POINT_FOR_90_DEGREE_CIRCLE);
405 :
406 0 : switch ( layout_feature_get_icon_direction( layouted_feature ) )
407 : {
408 0 : case GEOMETRY_DIRECTION_LEFT:
409 : {
410 0 : cairo_move_to ( cr, center_x, top );
411 0 : cairo_curve_to ( cr, right - ctrl_xoffset, top, right, top + ctrl_yoffset, right /* end point x */, center_y /* end point y */ );
412 0 : cairo_curve_to ( cr, right, bottom - ctrl_yoffset, right - ctrl_xoffset, bottom, center_x /* end point x */, bottom /* end point y */ );
413 0 : cairo_stroke (cr);
414 : }
415 0 : break;
416 :
417 0 : case GEOMETRY_DIRECTION_UP:
418 : {
419 0 : cairo_move_to ( cr, right, center_y );
420 0 : cairo_curve_to ( cr, right, bottom - ctrl_yoffset, right - ctrl_xoffset, bottom, center_x /* end point x */, bottom /* end point y */ );
421 0 : cairo_curve_to ( cr, left + ctrl_xoffset, bottom, left, bottom - ctrl_yoffset, left /* end point x */, center_y /* end point y */ );
422 0 : cairo_stroke (cr);
423 : }
424 0 : break;
425 :
426 0 : case GEOMETRY_DIRECTION_RIGHT:
427 : {
428 0 : cairo_move_to ( cr, center_x, bottom );
429 0 : cairo_curve_to ( cr, left + ctrl_xoffset, bottom, left, bottom - ctrl_yoffset, left /* end point x */, center_y /* end point y */ );
430 0 : cairo_curve_to ( cr, left, top + ctrl_yoffset, left + ctrl_xoffset, top, center_x /* end point x */, top /* end point y */ );
431 0 : cairo_stroke (cr);
432 : }
433 0 : break;
434 :
435 0 : case GEOMETRY_DIRECTION_DOWN:
436 : {
437 0 : cairo_move_to ( cr, left, center_y );
438 0 : cairo_curve_to ( cr, left, top + ctrl_yoffset, left + ctrl_xoffset, top, center_x /* end point x */, top /* end point y */ );
439 0 : cairo_curve_to ( cr, right - ctrl_xoffset, top, right, top + ctrl_yoffset, right /* end point x */, center_y /* end point y */ );
440 0 : cairo_stroke (cr);
441 : }
442 0 : break;
443 :
444 0 : case GEOMETRY_DIRECTION_CENTER:
445 : {
446 0 : cairo_move_to ( cr, center_x, bottom );
447 0 : cairo_curve_to ( cr, left + ctrl_xoffset, bottom, left, bottom - ctrl_yoffset, left /* end point x */, center_y /* end point y */ );
448 0 : cairo_curve_to ( cr, left, top + ctrl_yoffset, left + ctrl_xoffset, top, center_x /* end point x */, top /* end point y */ );
449 0 : cairo_curve_to ( cr, right - ctrl_xoffset, top, right, top + ctrl_yoffset, right /* end point x */, center_y /* end point y */ );
450 0 : cairo_curve_to ( cr, right, bottom - ctrl_yoffset, right - ctrl_xoffset, bottom, center_x /* end point x */, bottom /* end point y */ );
451 0 : cairo_stroke (cr);
452 : }
453 0 : break;
454 :
455 0 : default:
456 : {
457 0 : U8_LOG_ERROR( "unexpected value in geometry_direction_t." );
458 : }
459 0 : break;
460 : }
461 :
462 0 : U8_TRACE_END();
463 0 : }
464 :
465 0 : void pencil_feature_painter_get_minimum_bounds ( pencil_feature_painter_t *this_,
466 : const data_feature_t *the_feature,
467 : const data_profile_part_t *profile,
468 : const pencil_size_t *pencil_size,
469 : PangoLayout *font_layout,
470 : geometry_dimensions_t *out_feature_bounds )
471 : {
472 0 : U8_TRACE_BEGIN();
473 0 : assert( NULL != the_feature );
474 0 : assert( NULL != profile );
475 0 : assert( NULL != pencil_size );
476 0 : assert( NULL != font_layout );
477 0 : assert( NULL != out_feature_bounds );
478 :
479 0 : const geometry_dimensions_t label_dim_proposal = {
480 0 : .width = 25.0 * pencil_size_get_standard_font_size( pencil_size ),
481 0 : .height = pencil_size_get_standard_font_size( pencil_size )
482 : };
483 0 : draw_feature_label_get_key_and_value_dimensions( &((*this_).draw_feature_label),
484 : the_feature,
485 : profile,
486 : &label_dim_proposal,
487 : pencil_size,
488 : font_layout,
489 : out_feature_bounds
490 : );
491 :
492 0 : U8_TRACE_END();
493 0 : }
494 :
495 :
496 : /*
497 : Copyright 2017-2024 Andreas Warnke
498 :
499 : Licensed under the Apache License, Version 2.0 (the "License");
500 : you may not use this file except in compliance with the License.
501 : You may obtain a copy of the License at
502 :
503 : http://www.apache.org/licenses/LICENSE-2.0
504 :
505 : Unless required by applicable law or agreed to in writing, software
506 : distributed under the License is distributed on an "AS IS" BASIS,
507 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
508 : See the License for the specific language governing permissions and
509 : limitations under the License.
510 : */
|