LCOV - code coverage report
Current view: top level - gui/source/sketch - gui_sketch_nav_tree.c (source / functions) Hit Total Coverage
Test: crystal-facet-uml_v1.62.0_covts Lines: 244 374 65.2 %
Date: 2024-12-21 18:34:41 Functions: 7 11 63.6 %

          Line data    Source code
       1             : /* File: gui_sketch_nav_tree.c; Copyright and License: see below */
       2             : 
       3             : #include "sketch/gui_sketch_nav_tree.h"
       4             : #include "geometry/geometry_rectangle.h"
       5             : #include "u8/u8_trace.h"
       6             : #include "u8/u8_log.h"
       7             : #include "u8/u8_i32.h"
       8             : #include <gdk/gdk.h>
       9             : 
      10             : static const int GUI_SKETCH_NAV_TREE_INDENT = 12;
      11             : static const int OBJ_GAP = 3;
      12             : static const int GAP_HEIGHT = 2;
      13             : static const int GUI_SKETCH_NAV_TREE_PANGO_AUTO_DETECT_LENGTH = -1;  /*!< pango automatically determines the string length */
      14             : 
      15           4 : void gui_sketch_nav_tree_init( gui_sketch_nav_tree_t *this_,
      16             :                                gui_resources_t *resources,
      17             :                                gui_sketch_texture_t *texture_downloader )
      18             : {
      19           4 :     U8_TRACE_BEGIN();
      20           4 :     assert( resources != NULL );
      21           4 :     assert( texture_downloader != NULL );
      22             : 
      23           4 :     (*this_).ancestors_count = 0;
      24           4 :     (*this_).siblings_count = 0;
      25           4 :     (*this_).children_count = 0;
      26           4 :     (*this_).siblings_self_index = -1;
      27             : 
      28           4 :     (*this_).node_count = 0;
      29           4 :     (*this_).gap_count = 0;
      30             : 
      31           4 :     (*this_).visible = false;
      32           4 :     shape_int_rectangle_init( &((*this_).bounds), 0, 0, 0, 0 );
      33             : 
      34           4 :     gui_sketch_style_init( &((*this_).sketch_style) );
      35           4 :     gui_sketch_marker_init( &((*this_).sketch_marker), true );
      36           4 :     (*this_).resources = resources;
      37           4 :     (*this_).texture_downloader = texture_downloader;
      38             : 
      39           4 :     U8_TRACE_END();
      40           4 : }
      41             : 
      42           4 : void gui_sketch_nav_tree_destroy( gui_sketch_nav_tree_t *this_ )
      43             : {
      44           4 :     U8_TRACE_BEGIN();
      45             : 
      46           4 :     (*this_).resources = NULL;
      47           4 :     (*this_).texture_downloader = NULL;
      48           4 :     gui_sketch_marker_destroy( &((*this_).sketch_marker) );
      49           4 :     gui_sketch_style_destroy( &((*this_).sketch_style) );
      50             : 
      51           4 :     gui_sketch_nav_tree_invalidate_data( this_ );
      52             : 
      53           4 :     shape_int_rectangle_destroy( &((*this_).bounds) );
      54             : 
      55           4 :     U8_TRACE_END();
      56           4 : }
      57             : 
      58           0 : void gui_sketch_nav_tree_load_data( gui_sketch_nav_tree_t *this_, data_row_t diagram_id, data_database_reader_t *db_reader )
      59             : {
      60           0 :     U8_TRACE_BEGIN();
      61           0 :     assert( NULL != db_reader );
      62             : 
      63           0 :     gui_sketch_nav_tree_invalidate_data( this_ );
      64             : 
      65           0 :     u8_error_t db_err = U8_ERROR_NONE;
      66             : 
      67             :     /* get ancestors */
      68           0 :     bool finished = false;
      69           0 :     for ( unsigned int anc_index = 0; ( anc_index < GUI_SKETCH_NAV_TREE_CONST_MAX_ANCESTORS ) && ( db_err == U8_ERROR_NONE ) && ( ! finished ); anc_index ++ )
      70             :     {
      71             :         data_row_t id_to_load;
      72           0 :         if ( anc_index == 0 )
      73             :         {
      74           0 :             id_to_load = diagram_id;
      75             :         }
      76             :         else
      77             :         {
      78           0 :             id_to_load = data_diagram_get_parent_row_id( &((*this_).ancestor_diagrams[anc_index-1]) );
      79             :         }
      80             : 
      81           0 :         if ( id_to_load != DATA_ROW_VOID )
      82             :         {
      83           0 :             db_err = data_database_reader_get_diagram_by_id ( db_reader, id_to_load, &((*this_).ancestor_diagrams[anc_index]) );
      84           0 :             if ( db_err == U8_ERROR_NONE )
      85             :             {
      86           0 :                 (*this_).ancestors_count = anc_index+1;
      87             :             }
      88             :         }
      89             :         else
      90             :         {
      91           0 :             finished = true;
      92             :         }
      93             :     }
      94             : 
      95           0 :     if ( diagram_id != DATA_ROW_VOID )
      96             :     {
      97           0 :         if ( (*this_).ancestors_count == 0 )
      98             :         {
      99           0 :             db_err = U8_ERROR_INVALID_REQUEST;
     100           0 :             U8_LOG_ANOMALY_INT( "gui_sketch_nav_tree_load_data cannot load diagram", diagram_id );
     101             :         }
     102             :     }
     103             : 
     104             :     /* get siblings */
     105           0 :     if ( db_err == U8_ERROR_NONE )
     106             :     {
     107           0 :         const data_row_t parent_id
     108           0 :             = ( (*this_).ancestors_count == 0 )
     109             :             ? DATA_ROW_VOID
     110           0 :             : data_diagram_get_parent_row_id( &((*this_).ancestor_diagrams[0]) );
     111             : 
     112             :         data_diagram_iterator_t diagram_iterator;
     113           0 :         db_err |= data_diagram_iterator_init_empty( &diagram_iterator );
     114           0 :         db_err |= data_database_reader_get_diagrams_by_parent_id( db_reader,
     115             :                                                                   parent_id,
     116             :                                                                   &diagram_iterator
     117             :                                                                 );
     118           0 :         uint_fast32_t next_idx = 0;
     119           0 :         for ( ; (next_idx < GUI_SKETCH_NAV_TREE_CONST_MAX_SIBLINGS) && data_diagram_iterator_has_next( &diagram_iterator ); next_idx++ )
     120             :         {
     121           0 :             db_err |= data_diagram_iterator_next( &diagram_iterator, &( (*this_).sibling_diagrams[next_idx] ) );
     122             :             /* search self in list of siblings */
     123           0 :             if ( diagram_id == data_diagram_get_row_id( &((*this_).sibling_diagrams[next_idx]) ) )
     124             :             {
     125           0 :                 (*this_).siblings_self_index = next_idx;
     126             :             }
     127             :         }
     128           0 :         (*this_).siblings_count = next_idx;
     129           0 :         if ( data_diagram_iterator_has_next( &diagram_iterator ) )
     130             :         {
     131           0 :             db_err |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
     132           0 :             U8_LOG_WARNING_INT( "gui_sketch_nav_tree_load_data finds too many sibling diagrams", GUI_SKETCH_NAV_TREE_CONST_MAX_SIBLINGS );
     133             :         }
     134           0 :         db_err |= data_diagram_iterator_destroy( &diagram_iterator );
     135             :     }
     136             : 
     137             :     /* get children */
     138           0 :     if ( db_err == U8_ERROR_NONE )
     139             :     {
     140             :         data_diagram_iterator_t diagram_iterator;
     141           0 :         db_err |= data_diagram_iterator_init_empty( &diagram_iterator );
     142           0 :         db_err |= data_database_reader_get_diagrams_by_parent_id( db_reader,
     143             :                                                                   diagram_id,
     144             :                                                                   &diagram_iterator
     145             :                                                                 );
     146           0 :         uint_fast32_t next_idx = 0;
     147           0 :         for ( ; (next_idx < GUI_SKETCH_NAV_TREE_CONST_MAX_CHILDREN) && data_diagram_iterator_has_next( &diagram_iterator ); next_idx++ )
     148             :         {
     149           0 :             db_err |= data_diagram_iterator_next( &diagram_iterator, &( (*this_).child_diagrams[next_idx] ) );
     150             :         }
     151           0 :         (*this_).children_count = next_idx;
     152           0 :         if ( data_diagram_iterator_has_next( &diagram_iterator ) )
     153             :         {
     154           0 :             db_err |= U8_ERROR_ARRAY_BUFFER_EXCEEDED;
     155           0 :             U8_LOG_WARNING_INT( "gui_sketch_nav_tree_load_data finds too many children diagrams", GUI_SKETCH_NAV_TREE_CONST_MAX_CHILDREN );
     156             :         }
     157           0 :         db_err |= data_diagram_iterator_destroy( &diagram_iterator );
     158             :     }
     159             : 
     160             :     /* invalidate layout positions */
     161             :     {
     162           0 :         (*this_).node_count = 0;
     163           0 :         (*this_).gap_count = 0;
     164             :     }
     165             : 
     166           0 :     U8_TRACE_END();
     167           0 : }
     168             : 
     169           4 : void gui_sketch_nav_tree_do_layout( gui_sketch_nav_tree_t *this_, cairo_t *cr )
     170             : {
     171           4 :     U8_TRACE_BEGIN();
     172           4 :     assert( (*this_).ancestors_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_ANCESTORS );
     173           4 :     assert( (*this_).siblings_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_SIBLINGS );
     174           4 :     assert( (*this_).children_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_CHILDREN );
     175             : 
     176             :     /* create the font_layout */
     177             :     PangoLayout *font_layout;
     178             :     {
     179           4 :         font_layout = pango_cairo_create_layout (cr);
     180             :         const PangoFontDescription *const std_font
     181           4 :             = gui_sketch_style_get_standard_font_description( &((*this_).sketch_style ) );
     182           4 :         pango_layout_set_font_description( font_layout, std_font );
     183             :     }
     184             : 
     185           4 :     int32_t y_pos = shape_int_rectangle_get_top( &((*this_).bounds) ) + OBJ_GAP;
     186           4 :     const int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
     187           4 :     const uint_fast32_t width = shape_int_rectangle_get_width( &((*this_).bounds) );
     188             : 
     189           4 :     (*this_).node_count = 0;
     190           4 :     (*this_).gap_count = 0;
     191             : 
     192           4 :     if ( (*this_).ancestors_count == 0 )
     193             :     {
     194           1 :         pos_nav_tree_node_t *const new_root_node = &((*this_).node_pos[0]);
     195           1 :         (*this_).node_count = 1;
     196             : 
     197             :         /* show only a new root button */
     198             :         assert( 1 <= GUI_SKETCH_NAV_TREE_CONST_MAX_NODES );
     199           1 :         pos_nav_tree_node_init( new_root_node, POS_NAV_TREE_NODE_TYPE_NEW_ROOT, NULL );
     200           1 :         gui_sketch_nav_tree_private_layout_node( this_, new_root_node, 0, &y_pos, font_layout );
     201             : 
     202             :         /* no gaps in this case */
     203           1 :         assert( 0 == (*this_).gap_count );
     204           1 :         assert( 1 == (*this_).node_count );
     205             :     }
     206             :     else
     207             :     {
     208             :         /* layout ancestors excluding self (at index 0 in ancestor_diagrams) */
     209           3 :         const unsigned int anc_count = (*this_).ancestors_count;
     210           3 :         assert( anc_count > 0 );
     211           3 :         assert( anc_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_ANCESTORS );
     212           3 :         assert( (*this_).node_count + (anc_count-1) <= GUI_SKETCH_NAV_TREE_CONST_MAX_NODES );
     213           6 :         for ( unsigned int anc_idx = 1; anc_idx < anc_count; anc_idx ++ )
     214             :         {
     215           3 :             const data_diagram_t *const anc_diag = &((*this_).ancestor_diagrams[anc_count-anc_idx]);
     216             : 
     217             :             /* layout upper-gap of ancestor */
     218             :             {
     219           3 :                 pos_nav_tree_gap_t *const upper_gap = &((*this_).gap_pos[(*this_).gap_count]);
     220           3 :                 (*this_).gap_count ++;
     221             : 
     222           3 :                 pos_nav_tree_gap_init( upper_gap, data_diagram_get_parent_data_id(anc_diag), 0 );
     223           3 :                 const int indent = (anc_idx-1) * GUI_SKETCH_NAV_TREE_INDENT;
     224             : 
     225           3 :                 pos_nav_tree_gap_set_gap_box_coords( upper_gap, left+indent, y_pos, width-indent, GAP_HEIGHT );
     226           3 :                 y_pos += GAP_HEIGHT;
     227             :             }
     228             : 
     229             :             /* layout ancestor node */
     230             :             {
     231           3 :                 pos_nav_tree_node_t *const anc_node = &((*this_).node_pos[(*this_).node_count]);
     232           3 :                 (*this_).node_count ++;
     233             : 
     234           3 :                 const pos_nav_tree_node_type_t n_type
     235           3 :                     = ((anc_idx+1)==anc_count) ? POS_NAV_TREE_NODE_TYPE_OPEN : POS_NAV_TREE_NODE_TYPE_ANCESTOR;
     236           3 :                 pos_nav_tree_node_init( anc_node, n_type, anc_diag );
     237           3 :                 gui_sketch_nav_tree_private_layout_node( this_, anc_node, (anc_idx-1), &y_pos, font_layout );
     238             :             }
     239             :         }
     240             : 
     241           3 :         const unsigned int tree_depth = (*this_).ancestors_count-1;
     242             : 
     243             :         /* layout siblings including self */
     244           3 :         const unsigned int sibl_count = (*this_).siblings_count;
     245           3 :         assert( sibl_count > 0 );
     246           3 :         assert( sibl_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_SIBLINGS );
     247           3 :         const unsigned int child_count = (*this_).children_count;
     248           3 :         assert( child_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_CHILDREN );
     249           3 :         assert( (*this_).node_count + sibl_count + child_count + 2 <= GUI_SKETCH_NAV_TREE_CONST_MAX_NODES );
     250           3 :         int32_t previous_sibl_order = INT32_MIN;
     251           8 :         for ( unsigned int sibl_idx = 0; sibl_idx < sibl_count; sibl_idx ++ )
     252             :         {
     253           5 :             const data_diagram_t *const sibl_diag = &((*this_).sibling_diagrams[sibl_idx]);
     254           5 :             const bool is_self = ( sibl_idx == (*this_).siblings_self_index );
     255             : 
     256             :             /* layout upper-gap of sibling */
     257             :             {
     258           5 :                 pos_nav_tree_gap_t *const upper_gap = &((*this_).gap_pos[(*this_).gap_count]);
     259           5 :                 (*this_).gap_count ++;
     260             : 
     261           5 :                 const int32_t gap_sibl_order
     262           5 :                     = (previous_sibl_order/2)+(data_diagram_get_list_order( sibl_diag )/2);  /* no overrun */
     263           5 :                 pos_nav_tree_gap_init( upper_gap, data_diagram_get_parent_data_id(sibl_diag), gap_sibl_order );
     264           5 :                 const int indent = tree_depth * GUI_SKETCH_NAV_TREE_INDENT;
     265           5 :                 pos_nav_tree_gap_set_gap_box_coords( upper_gap, left+indent, y_pos, width-indent, GAP_HEIGHT );
     266           5 :                 y_pos += GAP_HEIGHT;
     267             :             }
     268             : 
     269             :             /* layout sibling node */
     270             :             {
     271           5 :                 pos_nav_tree_node_t *const sibl_node = &((*this_).node_pos[(*this_).node_count]);
     272           5 :                 (*this_).node_count ++;
     273             : 
     274           5 :                 const pos_nav_tree_node_type_t n_type = is_self ? POS_NAV_TREE_NODE_TYPE_OPEN : POS_NAV_TREE_NODE_TYPE_CLOSED;
     275           5 :                 pos_nav_tree_node_init( sibl_node, n_type, sibl_diag );
     276           5 :                 gui_sketch_nav_tree_private_layout_node( this_, sibl_node, tree_depth, &y_pos, font_layout );
     277             :             }
     278             : 
     279             :             /* layout children */
     280           5 :             if ( is_self )
     281             :             {
     282           3 :                 int32_t prev_child_order = INT32_MIN;
     283           4 :                 for ( unsigned int chld_idx = 0; chld_idx < child_count; chld_idx ++ )
     284             :                 {
     285           1 :                     const data_diagram_t *const chld_diag = &((*this_).child_diagrams[chld_idx]);
     286             : 
     287             :                     /* layout upper-gap of child */
     288             :                     {
     289           1 :                         pos_nav_tree_gap_t *const upper_gap = &((*this_).gap_pos[(*this_).gap_count]);
     290           1 :                         (*this_).gap_count ++;
     291             : 
     292           1 :                         const int32_t gap_child_order
     293           1 :                             = (prev_child_order/2)+(data_diagram_get_list_order( chld_diag )/2);  /* no overrun */
     294           1 :                         pos_nav_tree_gap_init( upper_gap, data_diagram_get_parent_data_id(chld_diag), gap_child_order );
     295           1 :                         const int indent = (tree_depth+1) * GUI_SKETCH_NAV_TREE_INDENT;
     296           1 :                         pos_nav_tree_gap_set_gap_box_coords( upper_gap, left+indent, y_pos, width-indent, GAP_HEIGHT );
     297           1 :                         y_pos += GAP_HEIGHT;
     298             :                     }
     299             : 
     300             :                     /* layout child node */
     301             :                     {
     302           1 :                         pos_nav_tree_node_t *const chld_node = &((*this_).node_pos[(*this_).node_count]);
     303           1 :                         (*this_).node_count ++;
     304             : 
     305           1 :                         pos_nav_tree_node_init( chld_node, POS_NAV_TREE_NODE_TYPE_CLOSED, chld_diag );
     306           1 :                         gui_sketch_nav_tree_private_layout_node( this_, chld_node, tree_depth+1, &y_pos, font_layout );
     307             :                     }
     308             : 
     309           1 :                     prev_child_order = data_diagram_get_list_order( sibl_diag );
     310             :                 }
     311             : 
     312             :                 /* layout lower-gap of child (if any) */
     313             :                 {
     314           3 :                     pos_nav_tree_gap_t *const lower_gap = &((*this_).gap_pos[(*this_).gap_count]);
     315           3 :                     (*this_).gap_count ++;
     316             : 
     317           3 :                     const int32_t gap_child_order = (prev_child_order/2)+(INT32_MAX/2);  /* no overrun */
     318           3 :                     pos_nav_tree_gap_init( lower_gap, data_diagram_get_data_id(sibl_diag), gap_child_order );
     319           3 :                     const int indent = (tree_depth+1) * GUI_SKETCH_NAV_TREE_INDENT;
     320           3 :                     pos_nav_tree_gap_set_gap_box_coords( lower_gap, left+indent, y_pos, width-indent, GAP_HEIGHT );
     321           3 :                     y_pos += GAP_HEIGHT;
     322             :                 }
     323             : 
     324             :                 /* add a new child button */
     325             :                 {
     326           3 :                     pos_nav_tree_node_t *const new_chld_node = &((*this_).node_pos[(*this_).node_count]);
     327           3 :                     (*this_).node_count ++;
     328             : 
     329           3 :                     pos_nav_tree_node_init( new_chld_node, POS_NAV_TREE_NODE_TYPE_NEW_CHILD, NULL );
     330           3 :                     gui_sketch_nav_tree_private_layout_node( this_, new_chld_node, tree_depth+1, &y_pos, font_layout );
     331             :                 }
     332             :             }
     333             : 
     334           5 :             previous_sibl_order = data_diagram_get_list_order( sibl_diag );
     335             :         }
     336             : 
     337           3 :         const bool self_is_root = ( (*this_).ancestors_count == 1 );
     338           3 :         if ( ! self_is_root )
     339             :         {
     340             :             /* layout lower-gap of siblings */
     341           2 :             const data_diagram_t *const self_diag = &((*this_).ancestor_diagrams[0]);
     342             :             {
     343           2 :                 pos_nav_tree_gap_t *const lower_gap = &((*this_).gap_pos[(*this_).gap_count]);
     344           2 :                 (*this_).gap_count ++;
     345             : 
     346           2 :                 const int32_t gap_sibl_order = (previous_sibl_order/2)+(INT32_MAX/2);  /* no overrun */
     347           2 :                 pos_nav_tree_gap_init( lower_gap, data_diagram_get_parent_data_id(self_diag), gap_sibl_order );
     348           2 :                 const int indent = tree_depth * GUI_SKETCH_NAV_TREE_INDENT;
     349           2 :                 pos_nav_tree_gap_set_gap_box_coords( lower_gap, left+indent, y_pos, width-indent, GAP_HEIGHT );
     350           2 :                 y_pos += GAP_HEIGHT;
     351             :             }
     352             : 
     353             :             /* show a new sibling button unless root */
     354             :             {
     355           2 :                 pos_nav_tree_node_t *const new_sibl_node = &((*this_).node_pos[(*this_).node_count]);
     356           2 :                 (*this_).node_count ++;
     357             : 
     358           2 :                 pos_nav_tree_node_init( new_sibl_node, POS_NAV_TREE_NODE_TYPE_NEW_SIBLING, NULL );
     359           2 :                 gui_sketch_nav_tree_private_layout_node( this_, new_sibl_node, tree_depth, &y_pos, font_layout );
     360             :             }
     361             :         }
     362             : 
     363           3 :         assert( (*this_).gap_count == (*this_).node_count );
     364             :     }
     365             : 
     366             :     /* release the font_layout */
     367           4 :     g_object_unref(font_layout);
     368             : 
     369           4 :     U8_TRACE_END();
     370           4 : }
     371             : 
     372          15 : void gui_sketch_nav_tree_private_layout_node ( gui_sketch_nav_tree_t *this_,
     373             :                                                pos_nav_tree_node_t *node,
     374             :                                                uint32_t tree_depth,
     375             :                                                int32_t *io_y_pos,
     376             :                                                PangoLayout *font_layout )
     377             : {
     378          15 :     U8_TRACE_BEGIN();
     379          15 :     assert( NULL != node );
     380          15 :     assert( NULL != io_y_pos );
     381          15 :     assert( NULL != font_layout );
     382             : 
     383          15 :     const int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
     384          15 :     const uint_fast32_t width = shape_int_rectangle_get_width( &((*this_).bounds) );
     385          15 :     const uint_fast32_t indent = tree_depth*GUI_SKETCH_NAV_TREE_INDENT;
     386          15 :     const data_diagram_t *data_or_null = pos_nav_tree_node_get_data_const( node );
     387             : 
     388             :     /* determine icon dimensions */
     389             :     {
     390          15 :         const pos_nav_tree_node_type_t node_type = pos_nav_tree_node_get_type( node );
     391          15 :         GdkTexture *icon = pos_nav_tree_node_type_get_icon( node_type, false, (*this_).resources );
     392          15 :         const double icon_width = gdk_texture_get_width( icon );
     393          15 :         const double icon_height = gdk_texture_get_height( icon );
     394             : 
     395          15 :         const shape_int_rectangle_t new_icon_box = (shape_int_rectangle_t) {
     396          15 :             .left=left+indent+OBJ_GAP,
     397          15 :             .top=(*io_y_pos)+OBJ_GAP,
     398          15 :             .width=icon_width+0.999,
     399          15 :             .height=icon_height+0.999 };
     400          15 :         pos_nav_tree_node_set_icon_box( node, &new_icon_box );
     401             :     }
     402             : 
     403             :     /* determine label dimensions */
     404          15 :     const shape_int_rectangle_t *const icon_box = pos_nav_tree_node_get_icon_box_const( node );
     405             :     shape_int_rectangle_t new_label_box;
     406          15 :     if ( data_or_null == NULL )
     407             :     {
     408           6 :         shape_int_rectangle_init( &new_label_box, shape_int_rectangle_get_right(icon_box), (*io_y_pos), 0, 0 );
     409             :     }
     410             :     else
     411             :     {
     412           9 :         int_fast32_t proposed_pango_width = width - indent - shape_int_rectangle_get_width(icon_box) - (4*OBJ_GAP);
     413           9 :         pango_layout_set_text( font_layout,
     414             :                                data_diagram_get_name_const( data_or_null ),
     415             :                                GUI_SKETCH_NAV_TREE_PANGO_AUTO_DETECT_LENGTH
     416             :                              );
     417           9 :         pango_layout_set_width(font_layout, proposed_pango_width * PANGO_SCALE );
     418             :         int text_width;
     419             :         int text_height;
     420           9 :         pango_layout_get_pixel_size(font_layout, &text_width, &text_height);
     421             : 
     422           9 :         int_fast32_t x_pos = shape_int_rectangle_get_right(icon_box);
     423           9 :         shape_int_rectangle_init( &new_label_box, x_pos+OBJ_GAP, (*io_y_pos)+OBJ_GAP, text_width, text_height );
     424             :     }
     425          15 :     pos_nav_tree_node_set_label_box( node, &new_label_box );
     426             : 
     427             :     *io_y_pos
     428          15 :         = u8_i32_max2( shape_int_rectangle_get_bottom(icon_box), shape_int_rectangle_get_bottom(&new_label_box) )
     429          15 :         + OBJ_GAP;
     430             : 
     431          15 :     shape_int_rectangle_destroy( &new_label_box );
     432             : 
     433          15 :     U8_TRACE_END();
     434          15 : }
     435             : 
     436           4 : void gui_sketch_nav_tree_invalidate_data( gui_sketch_nav_tree_t *this_ )
     437             : {
     438           4 :     U8_TRACE_BEGIN();
     439           4 :     assert( (*this_).ancestors_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_ANCESTORS );
     440           4 :     assert( (*this_).siblings_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_SIBLINGS );
     441           4 :     assert( (*this_).children_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_CHILDREN );
     442             : 
     443             :     /* clear data */
     444          10 :     for ( int anc_index = 0; anc_index < (*this_).ancestors_count; anc_index ++ )
     445             :     {
     446           6 :         data_diagram_destroy( &((*this_).ancestor_diagrams[anc_index]) );
     447             :     }
     448           4 :     (*this_).ancestors_count = 0;
     449             : 
     450           9 :     for ( int sib_index = 0; sib_index < (*this_).siblings_count; sib_index ++ )
     451             :     {
     452           5 :         data_diagram_destroy( &((*this_).sibling_diagrams[sib_index]) );
     453             :     }
     454           4 :     (*this_).siblings_count = 0;
     455           4 :     (*this_).siblings_self_index = -1;
     456             : 
     457           5 :     for ( int chi_index = 0; chi_index < (*this_).children_count; chi_index ++ )
     458             :     {
     459           1 :         data_diagram_destroy( &((*this_).child_diagrams[chi_index]) );
     460             :     }
     461           4 :     (*this_).children_count = 0;
     462             : 
     463             :     /* clear layout infos */
     464           4 :     assert( (*this_).node_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_NODES );
     465          19 :     for ( int node_index = 0; node_index < (*this_).node_count; node_index ++ )
     466             :     {
     467          15 :         pos_nav_tree_node_destroy( &((*this_).node_pos[node_index]) );
     468             :     }
     469           4 :     (*this_).node_count = 0;
     470             : 
     471           4 :     assert( (*this_).gap_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_GAPS );
     472          18 :     for ( int gap_index = 0; gap_index < (*this_).gap_count; gap_index ++ )
     473             :     {
     474          14 :         pos_nav_tree_gap_destroy( &((*this_).gap_pos[gap_index]) );
     475             :     }
     476           4 :     (*this_).gap_count = 0;
     477             : 
     478           4 :     U8_TRACE_END();
     479           4 : }
     480             : 
     481          18 : gui_error_t gui_sketch_nav_tree_get_gap_info_at_pos ( const gui_sketch_nav_tree_t *this_,
     482             :                                                       int32_t x,
     483             :                                                       int32_t y,
     484             :                                                       data_id_t *out_parent_id,
     485             :                                                       int32_t *out_list_order,
     486             :                                                       shape_int_rectangle_t *out_gap_line )
     487             : {
     488          18 :     U8_TRACE_BEGIN();
     489          18 :     assert ( NULL != out_parent_id );
     490          18 :     assert ( NULL != out_list_order );
     491          18 :     assert ( NULL != out_gap_line );
     492             : 
     493             :     gui_error_t ret_error;
     494             : 
     495             :     /* search closest gap */
     496          18 :     if ( shape_int_rectangle_contains( &((*this_).bounds), x, y ) )
     497             :     {
     498          18 :         const unsigned int gap_count = (*this_).gap_count;
     499          18 :         assert( gap_count <= GUI_SKETCH_NAV_TREE_CONST_MAX_GAPS );
     500          18 :         if ( gap_count == 0 )
     501             :         {
     502           1 :             ret_error = GUI_ERROR_OUT_OF_BOUNDS;
     503             :         }
     504             :         else
     505             :         {
     506          17 :             const pos_nav_tree_gap_t *closest = &((*this_).gap_pos[0]);
     507          17 :             int closest_dist = INT32_MAX;
     508         109 :             for ( unsigned int idx = 0; idx < gap_count; idx ++ )
     509             :             {
     510          92 :                 const pos_nav_tree_gap_t *const current = &((*this_).gap_pos[idx]);
     511          92 :                 const shape_int_rectangle_t *const current_box = pos_nav_tree_gap_get_gap_box_const( current );
     512          92 :                 const int current_dist
     513          92 :                     = ( y < shape_int_rectangle_get_top( current_box ) )
     514          32 :                     ? ( shape_int_rectangle_get_top( current_box ) - y )
     515         184 :                     : (( y > shape_int_rectangle_get_bottom( current_box ) )
     516          60 :                     ? ( y - shape_int_rectangle_get_bottom( current_box ) )
     517         120 :                     : 0 );
     518          92 :                 if ( current_dist < closest_dist )
     519             :                 {
     520          60 :                     closest = current;
     521          60 :                     closest_dist = current_dist;
     522             :                 }
     523             :             }
     524          17 :             *out_parent_id = pos_nav_tree_gap_get_parent_id( closest );
     525          17 :             *out_list_order = pos_nav_tree_gap_get_list_order( closest );
     526          17 :             shape_int_rectangle_replace( out_gap_line, pos_nav_tree_gap_get_gap_box_const( closest ) );
     527          17 :             ret_error = GUI_ERROR_NONE;
     528             :         }
     529             :     }
     530             :     else
     531             :     {
     532           0 :         ret_error = GUI_ERROR_OUT_OF_BOUNDS;
     533             :     }
     534             : 
     535          18 :     U8_TRACE_END_ERR( ret_error );
     536          18 :     return ret_error;
     537             : }
     538             : 
     539          15 : void gui_sketch_nav_tree_get_object_id_at_pos ( const gui_sketch_nav_tree_t *this_,
     540             :                                                 int32_t x,
     541             :                                                 int32_t y,
     542             :                                                 data_id_t *out_selected_id )
     543             : {
     544          15 :     U8_TRACE_BEGIN();
     545          15 :     assert ( NULL != out_selected_id );
     546             : 
     547             :     /* default in case no object found */
     548             :     {
     549          15 :         data_id_reinit_void( out_selected_id );
     550             :     }
     551             : 
     552             :     /* search object */
     553          15 :     if ( shape_int_rectangle_contains( &((*this_).bounds), x, y ) )
     554             :     {
     555          15 :         const unsigned int count = (*this_).node_count;
     556          15 :         assert( count <= GUI_SKETCH_NAV_TREE_CONST_MAX_NODES );
     557          56 :         for ( unsigned int idx = 0; idx < count; idx ++ )
     558             :         {
     559          50 :             const pos_nav_tree_node_t *const node = &((*this_).node_pos[idx]);
     560          50 :             const shape_int_rectangle_t *icon_box = pos_nav_tree_node_get_icon_box_const( node );
     561          50 :             const shape_int_rectangle_t *label_box = pos_nav_tree_node_get_label_box_const( node );
     562             : 
     563          50 :             if ( shape_int_rectangle_contains( icon_box, x, y ) || shape_int_rectangle_contains( label_box, x, y ) )
     564             :             {
     565             :                 /* const pos_nav_tree_node_type_t node_type = pos_nav_tree_node_get_type( node ); */
     566          15 :                 const data_diagram_t *const data_or_null = pos_nav_tree_node_get_data_const( node );
     567          15 :                 if ( data_or_null != NULL )
     568             :                 {
     569           9 :                     *out_selected_id = data_diagram_get_data_id( data_or_null );
     570           9 :                     break;
     571             :                 }
     572             :             }
     573             :         }
     574             :     }
     575             : 
     576          15 :     U8_TRACE_END();
     577          15 : }
     578             : 
     579             : static const double GREY_R = 0.8;
     580             : static const double GREY_G = 0.8;
     581             : static const double GREY_B = 0.8;
     582             : static const double GREY_A = 1.0;
     583             : 
     584           0 : void gui_sketch_nav_tree_draw ( gui_sketch_nav_tree_t *this_, gui_marked_set_t *marker, cairo_t *cr )
     585             : {
     586           0 :     U8_TRACE_BEGIN();
     587           0 :     assert( NULL != marker );
     588           0 :     assert( NULL != cr );
     589             : 
     590           0 :     if ( (*this_).visible )
     591             :     {
     592           0 :         PangoLayout *font_layout = pango_cairo_create_layout (cr);
     593             :         {
     594             :             const PangoFontDescription *const std_font
     595           0 :                 = gui_sketch_style_get_standard_font_description( &((*this_).sketch_style ) );
     596           0 :             pango_layout_set_font_description ( font_layout, std_font );
     597             :         }
     598             : 
     599             :         /* draw background */
     600             :         {
     601           0 :             const int_fast32_t left = shape_int_rectangle_get_left( &((*this_).bounds) );
     602           0 :             const int_fast32_t top = shape_int_rectangle_get_top( &((*this_).bounds) );
     603           0 :             const uint_fast32_t width = shape_int_rectangle_get_width( &((*this_).bounds) );
     604           0 :             const uint_fast32_t height = shape_int_rectangle_get_height( &((*this_).bounds) );
     605             : 
     606           0 :             cairo_set_source_rgba( cr, GREY_R, GREY_G, GREY_B, GREY_A );
     607           0 :             cairo_rectangle ( cr, left, top, width, height );
     608           0 :             cairo_fill (cr);
     609             :         }
     610             : 
     611             :         /* draw icons and text */
     612           0 :         const unsigned int count = (*this_).node_count;
     613           0 :         assert( count <= GUI_SKETCH_NAV_TREE_CONST_MAX_NODES );
     614           0 :         for ( unsigned int idx = 0; idx < count; idx ++ )
     615             :         {
     616           0 :             const pos_nav_tree_node_t *const node = &((*this_).node_pos[idx]);
     617           0 :             gui_sketch_nav_tree_private_draw_node( this_, node, marker, font_layout, cr );
     618             :         }
     619             : 
     620           0 :         g_object_unref(font_layout);
     621             :     }
     622             : 
     623           0 :     U8_TRACE_END();
     624           0 : }
     625             : 
     626           0 : void gui_sketch_nav_tree_private_draw_node( gui_sketch_nav_tree_t *this_,
     627             :                                             const pos_nav_tree_node_t *node,
     628             :                                             const gui_marked_set_t *marker,
     629             :                                             PangoLayout *font_layout,
     630             :                                             cairo_t *cr )
     631             : {
     632           0 :     U8_TRACE_BEGIN();
     633           0 :     assert( NULL != cr );
     634           0 :     assert( NULL != node );
     635           0 :     assert( NULL != marker );
     636           0 :     assert( NULL != font_layout );
     637             : 
     638           0 :     const data_diagram_t *const diag_or_null = pos_nav_tree_node_get_data_const( node );
     639             : 
     640             :     /* draw marker and set color */
     641           0 :     if ( diag_or_null != NULL )
     642             :     {
     643             :         shape_int_rectangle_t destination_rect;
     644           0 :         shape_int_rectangle_init_by_bounds( &destination_rect,
     645             :                                             pos_nav_tree_node_get_icon_box_const(node),
     646             :                                             pos_nav_tree_node_get_label_box_const(node)
     647             :                                           );
     648             : 
     649           0 :         gui_sketch_marker_prepare_draw( &((*this_).sketch_marker),
     650             :                                         data_diagram_get_data_id( diag_or_null ),
     651             :                                         marker,
     652             :                                         destination_rect,
     653             :                                         cr
     654             :                                       );
     655             : 
     656           0 :         shape_int_rectangle_destroy( &destination_rect );
     657             :     }
     658             :     else
     659             :     {
     660           0 :         const GdkRGBA std_color = gui_sketch_style_get_standard_color( &((*this_).sketch_style) );
     661           0 :         cairo_set_source_rgba( cr, std_color.red, std_color.green, std_color.blue, std_color.alpha );
     662             :     }
     663             : 
     664             :     /* draw text first, use the above set color and font */
     665           0 :     if ( diag_or_null != NULL )
     666             :     {
     667             :         /* what to draw */
     668           0 :         const char *const label = data_diagram_get_name_const( diag_or_null );
     669             : 
     670             :         /* where to draw to */
     671             :         const shape_int_rectangle_t *const label_box
     672           0 :             = pos_nav_tree_node_get_label_box_const( node );
     673             : 
     674             :         /* do draw */
     675           0 :         cairo_move_to( cr, shape_int_rectangle_get_left(label_box), shape_int_rectangle_get_top(label_box) );
     676           0 :         pango_layout_set_text( font_layout, label, GUI_SKETCH_NAV_TREE_PANGO_AUTO_DETECT_LENGTH );
     677           0 :         const unsigned int text_width
     678           0 :             = shape_int_rectangle_get_width(label_box)
     679           0 :             +(2.0*OBJ_GAP);  /* add gap to avoid line breaks by rounding errors and whitespace character widths */
     680           0 :         pango_layout_set_width(font_layout, text_width * PANGO_SCALE );
     681           0 :         pango_cairo_show_layout( cr, font_layout );
     682             :     }
     683             : 
     684             :     /* draw the icon */
     685             :     {
     686             :         /* what to draw */
     687           0 :         const pos_nav_tree_node_type_t node_type = pos_nav_tree_node_get_type( node );
     688           0 :         const gui_sketch_action_t btn_act = gui_marked_set_get_highlighted_button( marker );
     689           0 :         const bool highlight
     690           0 :             = (( node_type == POS_NAV_TREE_NODE_TYPE_NEW_ROOT )&&( btn_act == GUI_SKETCH_ACTION_NEW_ROOT_DIAGRAM ))
     691           0 :             || (( node_type == POS_NAV_TREE_NODE_TYPE_NEW_SIBLING )&&( btn_act == GUI_SKETCH_ACTION_NEW_SIBLING_DIAGRAM ))
     692           0 :             || (( node_type == POS_NAV_TREE_NODE_TYPE_NEW_CHILD )&&( btn_act == GUI_SKETCH_ACTION_NEW_CHILD_DIAGRAM ));
     693           0 :         GdkTexture *icon = pos_nav_tree_node_type_get_icon( node_type, highlight, (*this_).resources );
     694             : 
     695             :         /* where to draw to */
     696             :         const shape_int_rectangle_t *const icon_box
     697           0 :             = pos_nav_tree_node_get_icon_box_const( node );
     698           0 :         const int x = shape_int_rectangle_get_left(icon_box);
     699           0 :         const int y = shape_int_rectangle_get_top(icon_box);
     700             : 
     701             :         /* do draw */
     702           0 :         gui_sketch_texture_draw( (*this_).texture_downloader, icon, x, y, cr );
     703             :     }
     704             : 
     705           0 :     U8_TRACE_END();
     706           0 : }
     707             : 
     708           0 : void gui_sketch_nav_tree_draw_overlay( const gui_sketch_nav_tree_t *this_,
     709             :                                        const gui_sketch_drag_state_t *drag_state,
     710             :                                        cairo_t *cr )
     711             : {
     712           0 :     U8_TRACE_BEGIN();
     713           0 :     assert( NULL != drag_state );
     714           0 :     assert( NULL != cr );
     715             : 
     716           0 :     if ( (*this_).visible && gui_sketch_drag_state_is_dragging( drag_state ) )
     717             :     {
     718           0 :         const int32_t to_x = gui_sketch_drag_state_get_to_x( drag_state );
     719           0 :         const int32_t to_y = gui_sketch_drag_state_get_to_y( drag_state );
     720             :         data_id_t out_parent_id;
     721             :         int32_t out_list_order;
     722             :         shape_int_rectangle_t out_gap_line;
     723             :         gui_error_t gap_err;
     724           0 :         gap_err = gui_sketch_nav_tree_get_gap_info_at_pos( this_,
     725             :                                                            to_x,
     726             :                                                            to_y,
     727             :                                                            &out_parent_id,
     728             :                                                            &out_list_order,
     729             :                                                            &out_gap_line
     730             :                                                          );
     731           0 :         if ( gap_err == GUI_ERROR_NONE )
     732             :         {
     733           0 :             const GdkRGBA high_color = gui_sketch_style_get_highlight_color( &((*this_).sketch_style) );
     734           0 :             cairo_set_source_rgba( cr, high_color.red, high_color.green, high_color.blue, high_color.alpha );
     735           0 :             cairo_rectangle( cr,
     736           0 :                              shape_int_rectangle_get_left(&out_gap_line),
     737           0 :                              shape_int_rectangle_get_top(&out_gap_line),
     738           0 :                              shape_int_rectangle_get_width(&out_gap_line),
     739           0 :                              shape_int_rectangle_get_height(&out_gap_line)
     740             :                            );
     741           0 :             cairo_fill (cr);
     742             :         }
     743             :         else
     744             :         {
     745           0 :             U8_TRACE_INFO("dragging diagram outside nav_tree");
     746             :         }
     747             :     }
     748           0 :     U8_TRACE_END();
     749           0 : }
     750             : 
     751             : 
     752             : /*
     753             : Copyright 2018-2024 Andreas Warnke
     754             : 
     755             : Licensed under the Apache License, Version 2.0 (the "License");
     756             : you may not use this file except in compliance with the License.
     757             : You may obtain a copy of the License at
     758             : 
     759             :     http://www.apache.org/licenses/LICENSE-2.0
     760             : 
     761             : Unless required by applicable law or agreed to in writing, software
     762             : distributed under the License is distributed on an "AS IS" BASIS,
     763             : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     764             : See the License for the specific language governing permissions and
     765             : limitations under the License.
     766             : */

Generated by: LCOV version 1.16