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

Generated by: LCOV version 1.16