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