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.61.0_covts Lines: 64 137 46.7 %
Date: 2024-10-26 21:44:38 Functions: 3 4 75.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 "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             : */

Generated by: LCOV version 1.16