Line data Source code
1 : /* File: draw_classifier_label.c; Copyright and License: see below */
2 :
3 : #include "draw/draw_classifier_label.h"
4 : #include "u8/u8_trace.h"
5 : #include "entity/data_classifier.h"
6 : #include "entity/data_diagramelement.h"
7 : #include "u8/u8_i32.h"
8 : #include "utf8stream/utf8stream_writer.h"
9 : #include "utf8stringbuf/utf8stringbuf.h"
10 : #include "utf8stringbuf/utf8string.h"
11 : #include "u8stream/universal_memory_output_stream.h"
12 : #include "u8stream/universal_output_stream.h"
13 : #include <pango/pangocairo.h>
14 : #include <stdio.h>
15 : #include <stdlib.h>
16 : #include <assert.h>
17 :
18 : static const int DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH = -1;
19 : static const int DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH = -1;
20 : #define DRAW_CLASSIFIER_LEFT_POINTING_GUILLEMENTS "\xc2\xab"
21 : #define DRAW_CLASSIFIER_RIGHT_POINTING_GUILLEMENTS "\xc2\xbb"
22 : #define DRAW_CLASSIFIER_COLON ':'
23 :
24 3 : void draw_classifier_label_init( draw_classifier_label_t *this_ )
25 : {
26 3 : utf8stream_writemem_init( &((*this_).text_builder), &((*this_).text_buffer), sizeof( (*this_).text_buffer) );
27 3 : draw_line_breaker_init( &((*this_).linebr) );
28 3 : }
29 :
30 3 : void draw_classifier_label_destroy( draw_classifier_label_t *this_ )
31 : {
32 3 : draw_line_breaker_destroy( &((*this_).linebr) );
33 3 : const u8_error_t text_err = utf8stream_writemem_destroy( &((*this_).text_builder) );
34 3 : if ( text_err != U8_ERROR_NONE )
35 : {
36 0 : U8_LOG_WARNING_HEX( "error at draw/draw_classifier_label: buffer too small", text_err );
37 : }
38 3 : }
39 :
40 256 : void draw_classifier_label_get_stereotype_and_name_dimensions( draw_classifier_label_t *this_,
41 : const data_visible_classifier_t *visible_classifier,
42 : bool with_stereotype,
43 : const geometry_dimensions_t *proposed_bounds,
44 : const pencil_size_t *pencil_size,
45 : PangoLayout *font_layout,
46 : geometry_dimensions_t *out_label_dim )
47 : {
48 256 : U8_TRACE_BEGIN();
49 256 : assert( NULL != visible_classifier );
50 256 : assert( NULL != proposed_bounds );
51 256 : assert( NULL != pencil_size );
52 256 : assert( NULL != font_layout );
53 256 : assert( NULL != out_label_dim );
54 :
55 256 : if ( data_visible_classifier_is_valid( visible_classifier ) )
56 : {
57 : const data_classifier_t *const classifier
58 256 : = data_visible_classifier_get_classifier_const( visible_classifier );
59 : const data_diagramelement_t *const diagramelement
60 256 : = data_visible_classifier_get_diagramelement_const( visible_classifier );
61 : const data_diagramelement_flag_t display_flags
62 256 : = data_diagramelement_get_display_flags( diagramelement );
63 :
64 : /* stereotype text */
65 256 : int text1_height = 0;
66 256 : int text1_width = 0;
67 : {
68 256 : if ( with_stereotype && data_classifier_has_stereotype( classifier ) )
69 : {
70 : /* prepare text */
71 : char stereotype_text[DATA_CLASSIFIER_MAX_STEREOTYPE_SIZE+4];
72 256 : utf8stringbuf_t stereotype_buf = UTF8STRINGBUF(stereotype_text);
73 256 : utf8stringbuf_copy_str( stereotype_buf, DRAW_CLASSIFIER_LEFT_POINTING_GUILLEMENTS );
74 256 : utf8stringbuf_append_str( stereotype_buf, data_classifier_get_stereotype_const( classifier ) );
75 256 : utf8stringbuf_append_str( stereotype_buf, DRAW_CLASSIFIER_RIGHT_POINTING_GUILLEMENTS );
76 :
77 : /* determine text width and height */
78 256 : pango_layout_set_font_description (font_layout, pencil_size_get_standard_font_description(pencil_size) );
79 256 : pango_layout_set_text (font_layout, utf8stringbuf_get_string( stereotype_buf ), DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH );
80 256 : pango_layout_get_pixel_size (font_layout, &text1_width, &text1_height);
81 256 : text1_height += PENCIL_SIZE_FONT_ALIGN_MARGIN; /* allow to align font with pixel border */
82 256 : text1_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
83 : }
84 : }
85 :
86 : /* draw name text */
87 : int text2_width;
88 : int text2_height;
89 : double space_for_line;
90 : {
91 256 : int proposed_pango_width = geometry_dimensions_get_width( proposed_bounds );
92 :
93 : /* prepare text */
94 256 : u8_error_t name_err = U8_ERROR_NONE;
95 256 : utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
96 :
97 256 : if ( 0 != ( display_flags & DATA_DIAGRAMELEMENT_FLAG_ANONYMOUS_INSTANCE ) )
98 : {
99 0 : name_err |= utf8stream_writer_write_char( to_name, DRAW_CLASSIFIER_COLON );
100 : }
101 256 : utf8stringview_t class_name = UTF8STRINGVIEW_STR( data_classifier_get_name_const( classifier ) );
102 : /* insert linebreaks */
103 256 : name_err |= draw_line_breaker_append( &((*this_).linebr), &class_name, to_name );
104 256 : const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
105 :
106 : /* determine text width and height */
107 256 : pango_layout_set_font_description( font_layout, pencil_size_get_title_font_description(pencil_size) );
108 512 : pango_layout_set_text( font_layout,
109 : utf8stringview_get_start( &name ),
110 256 : utf8stringview_get_length( &name )
111 : );
112 256 : pango_layout_set_width( font_layout, proposed_pango_width * PANGO_SCALE );
113 : /* option for nicer layout but harder to read: */
114 : /* pango_layout_set_wrap( font_layout, PANGO_WRAP_WORD_CHAR ); */
115 256 : pango_layout_get_pixel_size (font_layout, &text2_width, &text2_height);
116 256 : text2_height += PENCIL_SIZE_FONT_ALIGN_MARGIN; /* allow to align font with pixel border */
117 256 : text2_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
118 :
119 : /* restore pango context */
120 256 : pango_layout_set_width(font_layout, DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH );
121 :
122 : /* for space between stereotype and name */
123 256 : text2_height += pencil_size_get_font_line_gap( pencil_size );
124 :
125 : /* for underscores under object instance names, add 2 * gap: */
126 256 : space_for_line = 2.0 * pencil_size_get_standard_object_border( pencil_size );
127 :
128 : /* cleanup the text_builder */
129 256 : name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
130 256 : if ( name_err != U8_ERROR_NONE )
131 : {
132 0 : U8_LOG_WARNING_HEX( "error at get_dim/draw_line_breaker_append", name_err );
133 : }
134 : }
135 :
136 : /* draw description text */
137 256 : int text3_width = 0;
138 256 : int text3_height = 0;
139 256 : if ( DATA_CLASSIFIER_TYPE_COMMENT == data_classifier_get_main_type ( classifier ) )
140 : {
141 8 : pango_layout_set_font_description (font_layout, pencil_size_get_standard_font_description(pencil_size) );
142 8 : pango_layout_set_text ( font_layout,
143 : data_classifier_get_description_const( classifier ),
144 : DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH
145 : );
146 8 : pango_layout_get_pixel_size (font_layout, &text3_width, &text3_height);
147 8 : text3_height += PENCIL_SIZE_FONT_ALIGN_MARGIN; /* allow to align font with pixel border */
148 8 : text3_width += PENCIL_SIZE_FONT_ALIGN_MARGIN;
149 : }
150 :
151 256 : *out_label_dim = (geometry_dimensions_t) {
152 256 : .width = u8_i32_max3( text1_width, text2_width, text3_width ),
153 256 : .height = text1_height + text2_height + space_for_line + text3_height
154 : };
155 : }
156 : else
157 : {
158 0 : U8_LOG_ERROR("invalid visible classifier in draw_classifier_label_get_stereotype_and_name_dimensions()");
159 0 : *out_label_dim = (geometry_dimensions_t) { .width = 0.0, .height = 0.0 };
160 : }
161 256 : U8_TRACE_END();
162 256 : }
163 :
164 0 : void draw_classifier_label_draw_stereotype_and_name( draw_classifier_label_t *this_,
165 : const data_visible_classifier_t *visible_classifier,
166 : bool with_stereotype,
167 : const GdkRGBA *color,
168 : const geometry_rectangle_t *label_box,
169 : const pencil_size_t *pencil_size,
170 : PangoLayout *font_layout,
171 : cairo_t *cr )
172 : {
173 0 : U8_TRACE_BEGIN();
174 0 : assert( NULL != visible_classifier );
175 0 : assert( NULL != color );
176 0 : assert( NULL != label_box );
177 0 : assert( NULL != pencil_size );
178 0 : assert( NULL != font_layout );
179 0 : assert( NULL != cr );
180 :
181 : /* define names for input data: */
182 : const data_classifier_t *const classifier
183 0 : = data_visible_classifier_get_classifier_const( visible_classifier );
184 : const data_diagramelement_t *const diagramelement
185 0 : = data_visible_classifier_get_diagramelement_const( visible_classifier );
186 0 : const data_classifier_type_t classifier_type = data_classifier_get_main_type( classifier );
187 0 : const data_diagramelement_flag_t display_flags = data_diagramelement_get_display_flags( diagramelement );
188 :
189 0 : const double left = geometry_rectangle_get_left( label_box );
190 0 : const double top = geometry_rectangle_get_top( label_box );
191 0 : const double width = geometry_rectangle_get_width( label_box );
192 0 : const double f_line_gap = pencil_size_get_font_line_gap( pencil_size );
193 :
194 : /* draw stereotype text */
195 0 : int text1_height = 0;
196 : {
197 0 : if ( with_stereotype && data_classifier_has_stereotype( classifier ) )
198 : {
199 : /* prepare text */
200 : char stereotype_text[DATA_CLASSIFIER_MAX_STEREOTYPE_SIZE+4];
201 0 : utf8stringbuf_t stereotype_buf = UTF8STRINGBUF(stereotype_text);
202 0 : utf8stringbuf_copy_str( stereotype_buf, DRAW_CLASSIFIER_LEFT_POINTING_GUILLEMENTS );
203 0 : utf8stringbuf_append_str( stereotype_buf, data_classifier_get_stereotype_const( classifier ) );
204 0 : utf8stringbuf_append_str( stereotype_buf, DRAW_CLASSIFIER_RIGHT_POINTING_GUILLEMENTS );
205 :
206 : int text1_width;
207 0 : cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
208 0 : pango_layout_set_font_description( font_layout, pencil_size_get_standard_font_description(pencil_size) );
209 0 : pango_layout_set_text( font_layout,
210 0 : utf8stringbuf_get_string( stereotype_buf ),
211 : DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH
212 : );
213 0 : pango_layout_get_pixel_size (font_layout, &text1_width, &text1_height);
214 0 : cairo_move_to( cr,
215 0 : ceil( left + 0.5*( width - text1_width ) ),
216 : ceil( top )
217 : ); /* align font with pixel border */
218 0 : pango_cairo_show_layout( cr, font_layout );
219 : }
220 : }
221 :
222 : /* draw name text */
223 0 : int text2_height = 0;
224 : {
225 : /* prepare text */
226 0 : u8_error_t name_err = U8_ERROR_NONE;
227 0 : utf8stream_writer_t *to_name = utf8stream_writemem_get_writer( &((*this_).text_builder) );
228 :
229 0 : if ( 0 != ( display_flags & DATA_DIAGRAMELEMENT_FLAG_ANONYMOUS_INSTANCE ) )
230 : {
231 0 : name_err |= utf8stream_writer_write_char( to_name, DRAW_CLASSIFIER_COLON );
232 : }
233 0 : utf8stringview_t class_name = UTF8STRINGVIEW_STR( data_classifier_get_name_const( classifier ) );
234 : /* insert linebreaks */
235 0 : name_err |= draw_line_breaker_append( &((*this_).linebr), &class_name, to_name );
236 0 : const utf8stringview_t name = utf8stream_writemem_get_view( &((*this_).text_builder) );
237 :
238 : int text2_width;
239 0 : const double f_size = pencil_size_get_standard_font_size( pencil_size );
240 0 : cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
241 0 : pango_layout_set_font_description( font_layout, pencil_size_get_title_font_description(pencil_size) );
242 0 : pango_layout_set_text( font_layout,
243 : utf8stringview_get_start( &name ),
244 0 : utf8stringview_get_length( &name )
245 : );
246 0 : pango_layout_set_width( font_layout, (width+f_size) * PANGO_SCALE ); /* add gap to avoid line breaks by rounding errors and whitespace character widths */
247 : /* option for nicer layout but harder to read: */
248 : /* pango_layout_set_wrap( font_layout, PANGO_WRAP_WORD_CHAR ); */
249 0 : pango_layout_get_pixel_size( font_layout, &text2_width, &text2_height );
250 :
251 : /* draw text */
252 0 : cairo_move_to( cr,
253 0 : ceil( left + 0.5*( width - text2_width ) ),
254 0 : ceil( top + text1_height + f_line_gap )
255 : ); /* align font with pixel border */
256 0 : pango_cairo_show_layout( cr, font_layout );
257 :
258 : /* restore pango context */
259 0 : pango_layout_set_width( font_layout, DRAW_CLASSIFIER_PANGO_UNLIMITED_WIDTH );
260 :
261 : /* underline instances */
262 0 : if ( 0 != ( display_flags & ( DATA_DIAGRAMELEMENT_FLAG_ANONYMOUS_INSTANCE | DATA_DIAGRAMELEMENT_FLAG_NAMED_INSTANCE ) ) )
263 : {
264 0 : cairo_move_to( cr, left + 0.5*( width - text2_width ), top+text1_height+f_line_gap+text2_height );
265 0 : cairo_line_to( cr, left + 0.5*( width + text2_width ), top+text1_height+f_line_gap+text2_height );
266 0 : cairo_stroke(cr);
267 : }
268 :
269 : /* cleanup the text_builder */
270 0 : name_err |= utf8stream_writemem_reset( &((*this_).text_builder) );
271 0 : if ( name_err != U8_ERROR_NONE )
272 : {
273 0 : U8_LOG_WARNING_HEX( "error at draw/draw_line_breaker_append", name_err );
274 : }
275 : }
276 :
277 : /* draw description text */
278 0 : if ( DATA_CLASSIFIER_TYPE_COMMENT == classifier_type )
279 : {
280 : int text3_width;
281 : int text3_height;
282 0 : cairo_set_source_rgba( cr, color->red, color->green, color->blue, color->alpha );
283 0 : pango_layout_set_font_description (font_layout, pencil_size_get_standard_font_description(pencil_size) );
284 0 : pango_layout_set_text ( font_layout,
285 : data_classifier_get_description_const( classifier ),
286 : DRAW_CLASSIFIER_PANGO_AUTO_DETECT_LENGTH
287 : );
288 0 : pango_layout_get_pixel_size (font_layout, &text3_width, &text3_height);
289 :
290 : /* draw text */
291 0 : cairo_move_to( cr,
292 0 : ceil( left + 0.5*( width - text3_width ) ),
293 0 : ceil( top + text1_height + f_line_gap + text2_height + f_line_gap )
294 : ); /* align font with pixel border */
295 0 : pango_cairo_show_layout( cr, font_layout );
296 : }
297 :
298 0 : U8_TRACE_END();
299 0 : }
300 :
301 :
302 : /*
303 : Copyright 2016-2024 Andreas Warnke
304 : http://www.apache.org/licenses/LICENSE-2.0
305 :
306 : Licensed under the Apache License, Version 2.0 (the "License");
307 : you may not use this file except in compliance with the License.
308 : You may obtain a copy of the License at
309 :
310 :
311 : Unless required by applicable law or agreed to in writing, software
312 : distributed under the License is distributed on an "AS IS" BASIS,
313 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
314 : See the License for the specific language governing permissions and
315 : limitations under the License.
316 : */
|