LCOV - code coverage report
Current view: top level - pencil/source/draw - draw_classifier_label.c (source / functions) Hit Total Coverage
Test: crystal-facet-uml_v1.57.0_covts Lines: 58 133 43.6 %
Date: 2024-04-07 11:14:42 Functions: 1 2 50.0 %

          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             : */

Generated by: LCOV version 1.16