LCOV - code coverage report
Current view: top level - malcontent-control - cc-bar-chart-group.c (source / functions) Coverage Total Hit
Test: 2 coverage DB files Lines: 0.0 % 329 0
Test Date: 2025-10-05 20:47:42 Functions: 0.0 % 30 0
Branches: 0.0 % 168 0

             Branch data     Line data    Source code
       1                 :             : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
       2                 :             : /*
       3                 :             :  * Copyright 2024 GNOME Foundation, Inc.
       4                 :             :  *
       5                 :             :  * This library is free software; you can redistribute it and/or
       6                 :             :  * modify it under the terms of the GNU Lesser General Public
       7                 :             :  * License as published by the Free Software Foundation; either
       8                 :             :  * version 2 of the License, or (at your option) any later version.
       9                 :             :  *
      10                 :             :  * This library is distributed in the hope that it will be useful,
      11                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      12                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13                 :             :  * Lesser General Public License for more details.
      14                 :             :  *
      15                 :             :  * You should have received a copy of the GNU Lesser General
      16                 :             :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      17                 :             :  *
      18                 :             :  * Authors:
      19                 :             :  *  - Philip Withnall <pwithnall@gnome.org>
      20                 :             :  *
      21                 :             :  * SPDX-License-Identifier: GPL-3.0-or-later
      22                 :             :  */
      23                 :             : 
      24                 :             : #include <glib.h>
      25                 :             : #include <glib-object.h>
      26                 :             : #include <gtk/gtk.h>
      27                 :             : 
      28                 :             : #include "cc-bar-chart-group.h"
      29                 :             : #include "cc-bar-chart-bar.h"
      30                 :             : 
      31                 :             : /**
      32                 :             :  * CcBarChartGroup:
      33                 :             :  *
      34                 :             :  * #CcBarChartGroup is a grouping of bars in a #CcBarChart.
      35                 :             :  *
      36                 :             :  * It contains only #CcBarChartBar children. Currently, exactly one bar is
      37                 :             :  * supported per group, but this could be relaxed in future to support multiple
      38                 :             :  * grouped bars.
      39                 :             :  *
      40                 :             :  * #CcBarChartGroup forms the touch landing area for highlighting and selecting
      41                 :             :  * a #CcBarChartBar, regardless of its rendered height. The group as a whole
      42                 :             :  * may be selected, indicated by #CcBarChartGroup:is-selected.
      43                 :             :  *
      44                 :             :  * # CSS nodes
      45                 :             :  *
      46                 :             :  * |[<!-- language="plain" -->
      47                 :             :  * bar-group[:hover][:selected]
      48                 :             :  * ╰── bar[:hover][:selected]
      49                 :             :  * ]|
      50                 :             :  *
      51                 :             :  * #CcBarChartGroup uses a single CSS node named `bar-group`. Each bar is a
      52                 :             :  * sub-node named `bar`. Bars and groups may have `:hover` or `:selected`
      53                 :             :  * pseudo-selectors to indicate whether they are selected or being hovered over
      54                 :             :  * with the mouse.
      55                 :             :  *
      56                 :             :  * # Accessibility
      57                 :             :  *
      58                 :             :  * #CcBarChartGroup uses the %GTK_ACCESSIBLE_ROLE_GROUP role and #CcBarChartBar
      59                 :             :  * uses the %GTK_ACCESSIBLE_ROLE_LIST_ITEM role.
      60                 :             :  */
      61                 :             : struct _CcBarChartGroup {
      62                 :             :   GtkWidget parent_instance;
      63                 :             : 
      64                 :             :   /* Configured state: */
      65                 :             :   gboolean selectable;
      66                 :             :   enum
      67                 :             :     {
      68                 :             :       SELECTION_STATE_NONE,
      69                 :             :       SELECTION_STATE_GROUP,
      70                 :             :       SELECTION_STATE_BAR,
      71                 :             :     }
      72                 :             :   selection_state;
      73                 :             :   unsigned int selected_bar_index;  /* only defined if selection_state == SELECTION_STATE_BAR */
      74                 :             : 
      75                 :             :   double scale;  /* number of pixels per data value */
      76                 :             :   GPtrArray *bars;  /* (not nullable) (owned) (element-type CcBarChartBar) */
      77                 :             : };
      78                 :             : 
      79   [ #  #  #  #  :           0 : G_DEFINE_TYPE (CcBarChartGroup, cc_bar_chart_group, GTK_TYPE_WIDGET)
                   #  # ]
      80                 :             : 
      81                 :             : typedef enum {
      82                 :             :   PROP_SELECTABLE = 1,
      83                 :             :   PROP_IS_SELECTED,
      84                 :             :   PROP_SELECTED_INDEX,
      85                 :             :   PROP_SELECTED_INDEX_SET,
      86                 :             :   PROP_SCALE,
      87                 :             : } CcBarChartGroupProperty;
      88                 :             : 
      89                 :             : static GParamSpec *props[PROP_SCALE + 1];
      90                 :             : 
      91                 :             : static void cc_bar_chart_group_get_property (GObject    *object,
      92                 :             :                                              guint       property_id,
      93                 :             :                                              GValue     *value,
      94                 :             :                                              GParamSpec *pspec);
      95                 :             : static void cc_bar_chart_group_set_property (GObject      *object,
      96                 :             :                                              guint         property_id,
      97                 :             :                                              const GValue *value,
      98                 :             :                                              GParamSpec   *pspec);
      99                 :             : static void cc_bar_chart_group_dispose (GObject *object);
     100                 :             : static void cc_bar_chart_group_size_allocate (GtkWidget *widget,
     101                 :             :                                               int        width,
     102                 :             :                                               int        height,
     103                 :             :                                               int        baseline);
     104                 :             : static void cc_bar_chart_group_measure (GtkWidget      *widget,
     105                 :             :                                         GtkOrientation  orientation,
     106                 :             :                                         int             for_size,
     107                 :             :                                         int            *minimum,
     108                 :             :                                         int            *natural,
     109                 :             :                                         int            *minimum_baseline,
     110                 :             :                                         int            *natural_baseline);
     111                 :             : static gboolean cc_bar_chart_group_focus (GtkWidget        *widget,
     112                 :             :                                           GtkDirectionType  direction);
     113                 :             : static void gesture_click_pressed_cb (GtkGestureClick *gesture,
     114                 :             :                                       guint            n_press,
     115                 :             :                                       double           x,
     116                 :             :                                       double           y,
     117                 :             :                                       gpointer         user_data);
     118                 :             : 
     119                 :             : static gboolean find_index_for_bar (CcBarChartGroup *self,
     120                 :             :                                     CcBarChartBar   *bar,
     121                 :             :                                     unsigned int    *out_idx);
     122                 :             : static gboolean bar_is_focusable (CcBarChartBar *bar);
     123                 :             : static CcBarChartBar *get_adjacent_focusable_bar (CcBarChartGroup *self,
     124                 :             :                                                   CcBarChartBar   *bar,
     125                 :             :                                                   int              direction);
     126                 :             : static CcBarChartBar *get_first_focusable_bar (CcBarChartGroup *self);
     127                 :             : static CcBarChartBar *get_last_focusable_bar (CcBarChartGroup *self);
     128                 :             : 
     129                 :             : static void
     130                 :           0 : cc_bar_chart_group_class_init (CcBarChartGroupClass *klass)
     131                 :             : {
     132                 :           0 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     133                 :           0 :   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
     134                 :             : 
     135                 :           0 :   object_class->get_property = cc_bar_chart_group_get_property;
     136                 :           0 :   object_class->set_property = cc_bar_chart_group_set_property;
     137                 :           0 :   object_class->dispose = cc_bar_chart_group_dispose;
     138                 :             : 
     139                 :           0 :   widget_class->size_allocate = cc_bar_chart_group_size_allocate;
     140                 :           0 :   widget_class->measure = cc_bar_chart_group_measure;
     141                 :           0 :   widget_class->focus = cc_bar_chart_group_focus;
     142                 :             : 
     143                 :             :   /**
     144                 :             :    * CcBarChartGroup:selectable:
     145                 :             :    *
     146                 :             :    * Whether the group itself can be selected.
     147                 :             :    *
     148                 :             :    * If `FALSE`, any attempt to select the group will select its first bar
     149                 :             :    * instead.
     150                 :             :    */
     151                 :           0 :   props[PROP_SELECTABLE] =
     152                 :           0 :     g_param_spec_boolean ("selectable",
     153                 :             :                           NULL, NULL,
     154                 :             :                           TRUE,
     155                 :             :                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
     156                 :             : 
     157                 :             :   /**
     158                 :             :    * CcBarChartGroup:is-selected:
     159                 :             :    *
     160                 :             :    * Whether the group itself is currently selected.
     161                 :             :    */
     162                 :           0 :   props[PROP_IS_SELECTED] =
     163                 :           0 :     g_param_spec_boolean ("is-selected",
     164                 :             :                           NULL, NULL,
     165                 :             :                           FALSE,
     166                 :             :                           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
     167                 :             : 
     168                 :             :   /**
     169                 :             :    * CcBarChartGroup:selected-index:
     170                 :             :    *
     171                 :             :    * Index of the currently selected bar.
     172                 :             :    *
     173                 :             :    * If nothing is currently selected, the value of this property is undefined.
     174                 :             :    * See #CcBarChartGroup:selected-index-set to check this.
     175                 :             :    */
     176                 :           0 :   props[PROP_SELECTED_INDEX] =
     177                 :           0 :     g_param_spec_uint ("selected-index",
     178                 :             :                        NULL, NULL,
     179                 :             :                        0, G_MAXUINT, 0,
     180                 :             :                        G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
     181                 :             : 
     182                 :             :   /**
     183                 :             :    * CcBarChartGroup:selected-index-set:
     184                 :             :    *
     185                 :             :    * Whether a bar is currently selected.
     186                 :             :    *
     187                 :             :    * If this property is `TRUE`, the value of #CcBarChartGroup:selected-index is
     188                 :             :    * defined.
     189                 :             :    */
     190                 :           0 :   props[PROP_SELECTED_INDEX_SET] =
     191                 :           0 :     g_param_spec_boolean ("selected-index-set",
     192                 :             :                           NULL, NULL,
     193                 :             :                           FALSE,
     194                 :             :                           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
     195                 :             : 
     196                 :             :   /**
     197                 :             :    * CcBarChartGroup:scale:
     198                 :             :    *
     199                 :             :    * Scale used to render the bars in the group, in pixels per data unit.
     200                 :             :    *
     201                 :             :    * It must be greater than `0.0`.
     202                 :             :    *
     203                 :             :    * This is used internally and does not need to be set by code outside
     204                 :             :    * #CcBarChart.
     205                 :             :    */
     206                 :           0 :   props[PROP_SCALE] =
     207                 :           0 :     g_param_spec_double ("scale",
     208                 :             :                          NULL, NULL,
     209                 :             :                          -G_MAXDOUBLE, G_MAXDOUBLE, 1.0,
     210                 :             :                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
     211                 :             : 
     212                 :           0 :   g_object_class_install_properties (object_class, G_N_ELEMENTS (props), props);
     213                 :             : 
     214                 :           0 :   gtk_widget_class_set_template_from_resource (widget_class, "/org/freedesktop/MalcontentControl/ui/cc-bar-chart-group.ui");
     215                 :             : 
     216                 :           0 :   gtk_widget_class_set_css_name (widget_class, "bar-group");
     217                 :           0 :   gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_GROUP);
     218                 :           0 : }
     219                 :             : 
     220                 :             : static void
     221                 :           0 : cc_bar_chart_group_init (CcBarChartGroup *self)
     222                 :             : {
     223                 :           0 :   g_autoptr(GtkGestureClick) gesture = NULL;
     224                 :             : 
     225                 :           0 :   gtk_widget_init_template (GTK_WIDGET (self));
     226                 :             : 
     227                 :           0 :   self->selectable = TRUE;
     228                 :           0 :   self->bars = g_ptr_array_new_null_terminated (1, (GDestroyNotify) gtk_widget_unparent, TRUE);
     229                 :             : 
     230                 :             :   /* Handle clicks */
     231                 :           0 :   gesture = GTK_GESTURE_CLICK (gtk_gesture_click_new ());
     232                 :           0 :   g_signal_connect (gesture, "pressed", G_CALLBACK (gesture_click_pressed_cb), self);
     233                 :           0 :   gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (g_steal_pointer (&gesture)));
     234                 :           0 : }
     235                 :             : 
     236                 :             : static void
     237                 :           0 : cc_bar_chart_group_get_property (GObject    *object,
     238                 :             :                                  guint       property_id,
     239                 :             :                                  GValue     *value,
     240                 :             :                                  GParamSpec *pspec)
     241                 :             : {
     242                 :           0 :   CcBarChartGroup *self = CC_BAR_CHART_GROUP (object);
     243                 :             : 
     244   [ #  #  #  #  :           0 :   switch ((CcBarChartGroupProperty) property_id)
                   #  # ]
     245                 :             :     {
     246                 :           0 :     case PROP_SELECTABLE:
     247                 :           0 :       g_value_set_boolean (value, cc_bar_chart_group_get_selectable (self));
     248                 :           0 :       break;
     249                 :           0 :     case PROP_IS_SELECTED:
     250                 :           0 :       g_value_set_boolean (value, cc_bar_chart_group_get_is_selected (self));
     251                 :           0 :       break;
     252                 :           0 :     case PROP_SELECTED_INDEX: {
     253                 :             :       size_t idx;
     254                 :           0 :       gboolean valid = cc_bar_chart_group_get_selected_index (self, &idx);
     255         [ #  # ]:           0 :       g_value_set_uint (value, valid ? idx : 0);
     256                 :           0 :       break;
     257                 :             :     }
     258                 :           0 :     case PROP_SELECTED_INDEX_SET:
     259                 :           0 :       g_value_set_boolean (value, cc_bar_chart_group_get_selected_index (self, NULL));
     260                 :           0 :       break;
     261                 :           0 :     case PROP_SCALE:
     262                 :           0 :       g_value_set_double (value, cc_bar_chart_group_get_scale (self));
     263                 :           0 :       break;
     264                 :           0 :     default:
     265                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     266                 :           0 :       break;
     267                 :             :     }
     268                 :           0 : }
     269                 :             : 
     270                 :             : static void
     271                 :           0 : cc_bar_chart_group_set_property (GObject      *object,
     272                 :             :                                  guint         property_id,
     273                 :             :                                  const GValue *value,
     274                 :             :                                  GParamSpec   *pspec)
     275                 :             : {
     276                 :           0 :   CcBarChartGroup *self = CC_BAR_CHART_GROUP (object);
     277                 :             : 
     278   [ #  #  #  #  :           0 :   switch ((CcBarChartGroupProperty) property_id)
                   #  # ]
     279                 :             :     {
     280                 :           0 :     case PROP_SELECTABLE:
     281                 :           0 :       cc_bar_chart_group_set_selectable (self, g_value_get_boolean (value));
     282                 :           0 :       break;
     283                 :           0 :     case PROP_IS_SELECTED:
     284                 :           0 :       cc_bar_chart_group_set_is_selected (self, g_value_get_boolean (value));
     285                 :           0 :       break;
     286                 :           0 :     case PROP_SELECTED_INDEX:
     287                 :           0 :       cc_bar_chart_group_set_selected_index (self, TRUE, g_value_get_uint (value));
     288                 :           0 :       break;
     289                 :           0 :     case PROP_SELECTED_INDEX_SET:
     290                 :             :       /* Read only */
     291                 :             :       g_assert_not_reached ();
     292                 :             :       break;
     293                 :           0 :     case PROP_SCALE:
     294                 :           0 :       cc_bar_chart_group_set_scale (self, g_value_get_double (value));
     295                 :           0 :       break;
     296                 :           0 :     default:
     297                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
     298                 :             :     }
     299                 :           0 : }
     300                 :             : 
     301                 :             : static void
     302                 :           0 : cc_bar_chart_group_dispose (GObject *object)
     303                 :             : {
     304                 :           0 :   CcBarChartGroup *self = CC_BAR_CHART_GROUP (object);
     305                 :             : 
     306         [ #  # ]:           0 :   g_clear_pointer (&self->bars, g_ptr_array_unref);
     307                 :           0 :   gtk_widget_dispose_template (GTK_WIDGET (object), CC_TYPE_BAR_CHART_GROUP);
     308                 :             : 
     309                 :           0 :   G_OBJECT_CLASS (cc_bar_chart_group_parent_class)->dispose (object);
     310                 :           0 : }
     311                 :             : 
     312                 :             : static void
     313                 :           0 : cc_bar_chart_group_size_allocate (GtkWidget *widget,
     314                 :             :                                   int        width,
     315                 :             :                                   int        height,
     316                 :             :                                   int        baseline)
     317                 :             : {
     318                 :           0 :   CcBarChartGroup *self = CC_BAR_CHART_GROUP (widget);
     319                 :             : 
     320         [ #  # ]:           0 :   for (unsigned int i = 0; i < self->bars->len; i++)
     321                 :             :     {
     322                 :           0 :       CcBarChartBar *bar = self->bars->pdata[i];
     323                 :             :       int bar_top_y, bar_bottom_y, bar_left_x, bar_right_x;
     324                 :             :       GtkAllocation child_alloc;
     325                 :             : 
     326                 :             :       /* If drawing RTL, reverse the bar positions. */
     327         [ #  # ]:           0 :       if (gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_RTL)
     328                 :           0 :         bar = self->bars->pdata[self->bars->len - i - 1];
     329                 :             : 
     330                 :           0 :       bar_left_x = width * i / self->bars->len;
     331                 :           0 :       bar_right_x = width * (i + 1) / self->bars->len;
     332                 :             : 
     333                 :           0 :       bar_top_y = height - cc_bar_chart_bar_get_value (bar) * self->scale;
     334                 :           0 :       bar_bottom_y = height;
     335                 :             : 
     336                 :           0 :       child_alloc.x = bar_left_x;
     337                 :           0 :       child_alloc.y = bar_top_y;
     338                 :           0 :       child_alloc.width = bar_right_x - bar_left_x;
     339                 :           0 :       child_alloc.height = bar_bottom_y - bar_top_y;
     340                 :             : 
     341                 :           0 :       gtk_widget_size_allocate (GTK_WIDGET (bar), &child_alloc, -1);
     342                 :             :     }
     343                 :           0 : }
     344                 :             : 
     345                 :             : static void
     346                 :           0 : cc_bar_chart_group_measure (GtkWidget      *widget,
     347                 :             :                             GtkOrientation  orientation,
     348                 :             :                             int             for_size,
     349                 :             :                             int            *minimum,
     350                 :             :                             int            *natural,
     351                 :             :                             int            *minimum_baseline,
     352                 :             :                             int            *natural_baseline)
     353                 :             : {
     354                 :           0 :   CcBarChartGroup *self = CC_BAR_CHART_GROUP (widget);
     355                 :             : 
     356         [ #  # ]:           0 :   if (orientation == GTK_ORIENTATION_HORIZONTAL)
     357                 :             :     {
     358                 :           0 :       int total_minimum_width = 0, total_natural_width = 0;
     359                 :             : 
     360         [ #  # ]:           0 :       for (unsigned int i = 0; i < self->bars->len; i++)
     361                 :             :         {
     362                 :           0 :           CcBarChartBar *bar = self->bars->pdata[i];
     363                 :           0 :           int bar_minimum_width = 0, bar_natural_width = 0;
     364                 :             : 
     365                 :           0 :           gtk_widget_measure (GTK_WIDGET (bar), orientation, -1,
     366                 :             :                               &bar_minimum_width, &bar_natural_width,
     367                 :             :                               NULL, NULL);
     368                 :             : 
     369                 :           0 :           total_minimum_width += bar_minimum_width;
     370                 :           0 :           total_natural_width += bar_natural_width;
     371                 :             :         }
     372                 :             : 
     373                 :           0 :       *minimum = total_minimum_width;
     374                 :           0 :       *natural = total_natural_width;
     375                 :           0 :       *minimum_baseline = -1;
     376                 :           0 :       *natural_baseline = -1;
     377                 :             :     }
     378         [ #  # ]:           0 :   else if (orientation == GTK_ORIENTATION_VERTICAL)
     379                 :             :     {
     380                 :           0 :       int maximum_minimum_height = 0, maximum_natural_height = 0;
     381                 :             : 
     382         [ #  # ]:           0 :       for (unsigned int i = 0; i < self->bars->len; i++)
     383                 :             :         {
     384                 :           0 :           CcBarChartBar *bar = self->bars->pdata[i];
     385                 :           0 :           int bar_minimum_height = 0, bar_natural_height = 0;
     386                 :             : 
     387                 :           0 :           gtk_widget_measure (GTK_WIDGET (bar), orientation, -1,
     388                 :             :                               &bar_minimum_height, &bar_natural_height,
     389                 :             :                               NULL, NULL);
     390                 :             : 
     391                 :           0 :           maximum_minimum_height = MAX (maximum_minimum_height, bar_minimum_height);
     392                 :           0 :           maximum_natural_height = MAX (maximum_natural_height, bar_natural_height);
     393                 :             :         }
     394                 :             : 
     395                 :           0 :       *minimum = maximum_minimum_height;
     396                 :           0 :       *natural = maximum_natural_height;
     397                 :           0 :       *minimum_baseline = -1;
     398                 :           0 :       *natural_baseline = -1;
     399                 :             :     }
     400                 :             :   else
     401                 :             :     {
     402                 :             :       g_assert_not_reached ();
     403                 :             :     }
     404                 :           0 : }
     405                 :             : 
     406                 :             : static gboolean
     407                 :           0 : cc_bar_chart_group_focus (GtkWidget        *widget,
     408                 :             :                           GtkDirectionType  direction)
     409                 :             : {
     410                 :           0 :   CcBarChartGroup *self = CC_BAR_CHART_GROUP (widget);
     411                 :             :   GtkWidget *focus_child;
     412                 :           0 :   CcBarChartBar *next_focus_bar = NULL;
     413                 :             : 
     414                 :             :   /* Reverse the direction if in RTL mode, as the chart presents things on a
     415                 :             :    * left–right axis. */
     416         [ #  # ]:           0 :   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
     417                 :             :     {
     418   [ #  #  #  #  :           0 :       switch (direction)
                      # ]
     419                 :             :         {
     420                 :           0 :         case GTK_DIR_TAB_BACKWARD:
     421                 :           0 :           direction = GTK_DIR_TAB_FORWARD;
     422                 :           0 :           break;
     423                 :           0 :         case GTK_DIR_TAB_FORWARD:
     424                 :           0 :           direction = GTK_DIR_TAB_BACKWARD;
     425                 :           0 :           break;
     426                 :           0 :         case GTK_DIR_LEFT:
     427                 :           0 :           direction = GTK_DIR_RIGHT;
     428                 :           0 :           break;
     429                 :           0 :         case GTK_DIR_RIGHT:
     430                 :           0 :           direction = GTK_DIR_LEFT;
     431                 :           0 :           break;
     432                 :           0 :         case GTK_DIR_UP:
     433                 :             :         case GTK_DIR_DOWN:
     434                 :             :         default:
     435                 :             :           /* No change. */
     436                 :           0 :           break;
     437                 :             :         }
     438                 :             :     }
     439                 :             : 
     440                 :           0 :   focus_child = gtk_widget_get_focus_child (widget);
     441                 :             : 
     442         [ #  # ]:           0 :   if (focus_child != NULL)
     443                 :             :     {
     444                 :             :       /* Can the focus move around inside the currently focused child widget? */
     445         [ #  # ]:           0 :       if (gtk_widget_child_focus (focus_child, direction))
     446                 :           0 :         return TRUE;
     447                 :             : 
     448   [ #  #  #  # ]:           0 :       if (CC_IS_BAR_CHART_BAR (focus_child) &&
     449         [ #  # ]:           0 :           (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD))
     450                 :           0 :         next_focus_bar = get_adjacent_focusable_bar (self, CC_BAR_CHART_BAR (focus_child), -1);
     451   [ #  #  #  # ]:           0 :       else if (CC_IS_BAR_CHART_BAR (focus_child) &&
     452         [ #  # ]:           0 :                (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD))
     453                 :           0 :         next_focus_bar = get_adjacent_focusable_bar (self, CC_BAR_CHART_BAR (focus_child), 1);
     454                 :             :     }
     455                 :             :   else
     456                 :             :     {
     457                 :             :       /* No current focus bar. If a bar is selected, focus on that. Otherwise,
     458                 :             :        * focus on the first/last focusable bar, depending on which direction
     459                 :             :        * we’re coming in from. */
     460         [ #  # ]:           0 :       if (self->selection_state == SELECTION_STATE_BAR)
     461                 :           0 :         next_focus_bar = self->bars->pdata[self->selected_bar_index];
     462                 :             : 
     463   [ #  #  #  # ]:           0 :       if (next_focus_bar == NULL &&
     464   [ #  #  #  # ]:           0 :           (direction == GTK_DIR_UP || direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD))
     465                 :           0 :         next_focus_bar = get_last_focusable_bar (self);
     466   [ #  #  #  # ]:           0 :       else if (next_focus_bar == NULL &&
     467   [ #  #  #  # ]:           0 :                (direction == GTK_DIR_DOWN || direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD))
     468                 :           0 :         next_focus_bar = get_first_focusable_bar (self);
     469                 :             :     }
     470                 :             : 
     471         [ #  # ]:           0 :   if (next_focus_bar == NULL)
     472                 :           0 :     return FALSE;
     473                 :             : 
     474                 :           0 :   return gtk_widget_child_focus (GTK_WIDGET (next_focus_bar), direction);
     475                 :             : }
     476                 :             : 
     477                 :             : static void
     478                 :           0 : gesture_click_pressed_cb (GtkGestureClick *gesture,
     479                 :             :                           guint            n_press,
     480                 :             :                           double           x,
     481                 :             :                           double           y,
     482                 :             :                           gpointer         user_data)
     483                 :             : {
     484                 :           0 :   CcBarChartGroup *self = CC_BAR_CHART_GROUP (user_data);
     485                 :             :   GtkWidget *bar;
     486                 :           0 :   unsigned int bar_idx = 0;
     487                 :             : 
     488   [ #  #  #  # ]:           0 :   if (gtk_widget_get_focus_on_click (GTK_WIDGET (self)) && !gtk_widget_has_focus (GTK_WIDGET (self)))
     489                 :           0 :     gtk_widget_grab_focus (GTK_WIDGET (self));
     490                 :             : 
     491                 :           0 :   bar = gtk_widget_pick (GTK_WIDGET (self), x, y, GTK_PICK_DEFAULT);
     492                 :             : 
     493   [ #  #  #  # ]:           0 :   if (!CC_IS_BAR_CHART_BAR (bar) ||
     494                 :           0 :       !find_index_for_bar (self, CC_BAR_CHART_BAR (bar), &bar_idx))
     495                 :           0 :     bar = NULL;
     496                 :             : 
     497                 :             :   /* Select and focus the bar or group. */
     498         [ #  # ]:           0 :   if (bar != NULL)
     499                 :             :     {
     500         [ #  # ]:           0 :       if (bar_is_focusable (CC_BAR_CHART_BAR (bar)))
     501                 :           0 :         gtk_widget_set_focus_child (GTK_WIDGET (self), bar);
     502                 :           0 :       cc_bar_chart_group_set_selected_index (self, TRUE, bar_idx);
     503                 :             :     }
     504                 :             :   else
     505                 :             :     {
     506                 :             :       /* already grabbed focus above */
     507                 :           0 :       cc_bar_chart_group_set_is_selected (self, TRUE);
     508                 :             :     }
     509                 :           0 : }
     510                 :             : 
     511                 :             : static gboolean
     512                 :           0 : find_index_for_bar (CcBarChartGroup *self,
     513                 :             :                     CcBarChartBar   *bar,
     514                 :             :                     unsigned int    *out_idx)
     515                 :             : {
     516                 :           0 :   g_assert (gtk_widget_is_ancestor (GTK_WIDGET (bar), GTK_WIDGET (self)));
     517                 :           0 :   g_assert (self->bars != NULL);
     518                 :             : 
     519                 :           0 :   return g_ptr_array_find (self->bars, bar, out_idx);
     520                 :             : }
     521                 :             : 
     522                 :             : static gboolean
     523                 :           0 : bar_is_focusable (CcBarChartBar *bar)
     524                 :             : {
     525                 :           0 :   GtkWidget *widget = GTK_WIDGET (bar);
     526                 :             : 
     527         [ #  # ]:           0 :   return (gtk_widget_is_visible (widget) &&
     528         [ #  # ]:           0 :           gtk_widget_is_sensitive (widget) &&
     529   [ #  #  #  # ]:           0 :           gtk_widget_get_focusable (widget) &&
     530                 :           0 :           gtk_widget_get_can_focus (widget));
     531                 :             : }
     532                 :             : 
     533                 :             : /* direction == -1 means get previous sensitive and visible bar;
     534                 :             :  * direction == 1 means get next one. */
     535                 :             : static CcBarChartBar *
     536                 :           0 : get_adjacent_focusable_bar (CcBarChartGroup *self,
     537                 :             :                             CcBarChartBar   *bar,
     538                 :             :                             int              direction)
     539                 :             : {
     540                 :             :   unsigned int bar_idx, i;
     541                 :             : 
     542                 :           0 :   g_assert (gtk_widget_is_ancestor (GTK_WIDGET (bar), GTK_WIDGET (self)));
     543                 :           0 :   g_assert (self->bars != NULL);
     544                 :           0 :   g_assert (direction == -1 || direction == 1);
     545                 :             : 
     546         [ #  # ]:           0 :   if (!find_index_for_bar (self, bar, &bar_idx))
     547                 :           0 :     return NULL;
     548                 :             : 
     549                 :           0 :   i = bar_idx;
     550                 :             : 
     551   [ #  #  #  #  :           0 :   while (!((direction == -1 && i == 0) ||
                   #  # ]
     552         [ #  # ]:           0 :            (direction == 1 && i >= self->bars->len - 1)))
     553                 :             :     {
     554                 :             :       CcBarChartBar *adjacent_bar;
     555                 :             : 
     556                 :           0 :       i += direction;
     557                 :           0 :       adjacent_bar = self->bars->pdata[i];
     558                 :             : 
     559         [ #  # ]:           0 :       if (bar_is_focusable (adjacent_bar))
     560                 :           0 :         return adjacent_bar;
     561                 :             :     }
     562                 :             : 
     563                 :           0 :   return NULL;
     564                 :             : }
     565                 :             : 
     566                 :             : static CcBarChartBar *
     567                 :           0 : get_first_focusable_bar (CcBarChartGroup *self)
     568                 :             : {
     569                 :           0 :   g_assert (self->bars != NULL);
     570                 :             : 
     571         [ #  # ]:           0 :   for (unsigned int i = 0; i < self->bars->len; i++)
     572                 :             :     {
     573                 :           0 :       CcBarChartBar *bar = self->bars->pdata[i];
     574                 :             : 
     575         [ #  # ]:           0 :       if (bar_is_focusable (bar))
     576                 :           0 :         return bar;
     577                 :             :     }
     578                 :             : 
     579                 :           0 :   return NULL;
     580                 :             : }
     581                 :             : 
     582                 :             : static CcBarChartBar *
     583                 :           0 : get_last_focusable_bar (CcBarChartGroup *self)
     584                 :             : {
     585                 :           0 :   g_assert (self->bars != NULL);
     586                 :             : 
     587         [ #  # ]:           0 :   for (unsigned int i = 0; i < self->bars->len; i++)
     588                 :             :     {
     589                 :           0 :       CcBarChartBar *bar = self->bars->pdata[self->bars->len - 1 - i];
     590                 :             : 
     591         [ #  # ]:           0 :       if (bar_is_focusable (bar))
     592                 :           0 :         return bar;
     593                 :             :     }
     594                 :             : 
     595                 :           0 :   return NULL;
     596                 :             : }
     597                 :             : 
     598                 :             : /**
     599                 :             :  * cc_bar_chart_group_new:
     600                 :             :  *
     601                 :             :  * Create a new #CcBarChartGroup.
     602                 :             :  *
     603                 :             :  * Returns: (transfer full): the new #CcBarChartGroup
     604                 :             :  */
     605                 :             : CcBarChartGroup *
     606                 :           0 : cc_bar_chart_group_new (void)
     607                 :             : {
     608                 :           0 :   return g_object_new (CC_TYPE_BAR_CHART_GROUP, NULL);
     609                 :             : }
     610                 :             : 
     611                 :             : /**
     612                 :             :  * cc_bar_chart_group_get_bars:
     613                 :             :  * @self: a #CcBarChartGroup
     614                 :             :  * @out_n_bars: (out) (optional): return location for the number of bars,
     615                 :             :  *   or `NULL` to ignore
     616                 :             :  *
     617                 :             :  * Get the bars in the group.
     618                 :             :  *
     619                 :             :  * If there are currently no bars in the group, `NULL` is returned and
     620                 :             :  * @out_n_bars is set to `0`.
     621                 :             :  *
     622                 :             :  * Returns: (array length=out_n_bars zero-terminated=1) (nullable) (transfer none): array of
     623                 :             :  *   bars in the group, or `NULL` if empty
     624                 :             :  */
     625                 :             : CcBarChartBar * const *
     626                 :           0 : cc_bar_chart_group_get_bars (CcBarChartGroup *self,
     627                 :             :                              size_t          *out_n_bars)
     628                 :             : {
     629                 :           0 :   g_return_val_if_fail (CC_IS_BAR_CHART_GROUP (self), NULL);
     630                 :             : 
     631         [ #  # ]:           0 :   if (out_n_bars != NULL)
     632                 :           0 :     *out_n_bars = self->bars->len;
     633                 :             : 
     634         [ #  # ]:           0 :   return (self->bars->len != 0) ? (CcBarChartBar * const *) self->bars->pdata : NULL;
     635                 :             : }
     636                 :             : 
     637                 :             : /**
     638                 :             :  * cc_bar_chart_group_insert_bar:
     639                 :             :  * @self: a #CcBarChartGroup
     640                 :             :  * @idx: position to insert the bar at, or `-1` to append
     641                 :             :  * @bar: (transfer none): bar to insert; will be sunk if floating
     642                 :             :  *
     643                 :             :  * Insert @bar into the group at index @idx.
     644                 :             :  *
     645                 :             :  * Pass `-1` to @idx to append the bar.
     646                 :             :  *
     647                 :             :  * @bar will be unparented from its existing parent (if set) first, so this
     648                 :             :  * method can be used to rearrange bars within the group.
     649                 :             :  */
     650                 :             : void
     651                 :           0 : cc_bar_chart_group_insert_bar (CcBarChartGroup *self,
     652                 :             :                                int              idx,
     653                 :             :                                CcBarChartBar   *bar)
     654                 :             : {
     655                 :             :   CcBarChartBar *previous_sibling;
     656                 :             : 
     657                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_GROUP (self));
     658                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_BAR (bar));
     659                 :             : 
     660                 :           0 :   g_object_ref_sink (bar);
     661                 :             : 
     662                 :           0 :   gtk_widget_unparent (GTK_WIDGET (bar));
     663                 :           0 :   gtk_widget_set_parent (GTK_WIDGET (bar), GTK_WIDGET (self));
     664                 :             : 
     665   [ #  #  #  # ]:           0 :   if (idx < 0 && self->bars->len > 0)
     666                 :           0 :     previous_sibling = self->bars->pdata[self->bars->len - 1];
     667   [ #  #  #  # ]:           0 :   else if (self->bars->len == 0 || idx == 0)
     668                 :           0 :     previous_sibling = NULL;
     669                 :             :   else
     670                 :           0 :     previous_sibling = self->bars->pdata[idx - 1];
     671                 :             : 
     672                 :           0 :   gtk_widget_insert_after (GTK_WIDGET (bar), GTK_WIDGET (self), GTK_WIDGET (previous_sibling));
     673                 :             : 
     674                 :           0 :   g_ptr_array_insert (self->bars, idx, bar);
     675                 :             : 
     676                 :           0 :   g_object_unref (bar);
     677                 :             : 
     678                 :           0 :   gtk_widget_queue_resize (GTK_WIDGET (self));
     679                 :             : }
     680                 :             : 
     681                 :             : /**
     682                 :             :  * cc_bar_chart_group_remove_bar:
     683                 :             :  * @self: a #CcBarChartGroup
     684                 :             :  * @bar: (transfer none): bar to remove
     685                 :             :  *
     686                 :             :  * Remove @bar from the group.
     687                 :             :  *
     688                 :             :  * It is an error to call this on a @bar which is not currently in the group.
     689                 :             :  */
     690                 :             : void
     691                 :           0 : cc_bar_chart_group_remove_bar (CcBarChartGroup *self,
     692                 :             :                                CcBarChartBar   *bar)
     693                 :             : {
     694                 :             :   gboolean was_removed;
     695                 :             : 
     696                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_GROUP (self));
     697                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_BAR (bar));
     698                 :             : 
     699                 :           0 :   was_removed = g_ptr_array_remove (self->bars, bar);
     700                 :           0 :   g_assert (was_removed);
     701                 :             : 
     702                 :           0 :   gtk_widget_unparent (GTK_WIDGET (bar));
     703                 :             : 
     704                 :           0 :   gtk_widget_queue_resize (GTK_WIDGET (self));
     705                 :             : }
     706                 :             : 
     707                 :             : /**
     708                 :             :  * cc_bar_chart_group_get_selectable:
     709                 :             :  * @self: a #CcBarChartGroup
     710                 :             :  *
     711                 :             :  * Get the value of #CcBarChartGroup:selectable.
     712                 :             :  *
     713                 :             :  * Returns: `TRUE` if the group itself is selectable, `FALSE` otherwise
     714                 :             :  */
     715                 :             : gboolean
     716                 :           0 : cc_bar_chart_group_get_selectable (CcBarChartGroup *self)
     717                 :             : {
     718                 :           0 :   g_return_val_if_fail (CC_IS_BAR_CHART_GROUP (self), FALSE);
     719                 :             : 
     720                 :           0 :   return self->selectable;
     721                 :             : }
     722                 :             : 
     723                 :             : /**
     724                 :             :  * cc_bar_chart_group_set_selectable:
     725                 :             :  * @self: a #CcBarChartGroup
     726                 :             :  * @selectable: `TRUE` if the group itself is selectable, `FALSE` otherwise
     727                 :             :  *
     728                 :             :  * Set the value of #CcBarChartGroup:selectable.
     729                 :             :  */
     730                 :             : void
     731                 :           0 : cc_bar_chart_group_set_selectable (CcBarChartGroup *self,
     732                 :             :                                    gboolean         selectable)
     733                 :             : {
     734                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_GROUP (self));
     735                 :             : 
     736         [ #  # ]:           0 :   if (self->selectable == selectable)
     737                 :           0 :     return;
     738                 :             : 
     739                 :           0 :   self->selectable = selectable;
     740                 :             : 
     741                 :           0 :   g_object_freeze_notify (G_OBJECT (self));
     742                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTABLE]);
     743                 :             : 
     744                 :             :   /* If the group is currently selected but shouldn’t be any more, run through
     745                 :             :    * the default fall-through selection logic again. */
     746   [ #  #  #  # ]:           0 :   if (!self->selectable && self->selection_state == SELECTION_STATE_GROUP)
     747                 :           0 :     cc_bar_chart_group_set_is_selected (self, TRUE);
     748                 :             : 
     749                 :           0 :   g_object_thaw_notify (G_OBJECT (self));
     750                 :             : }
     751                 :             : 
     752                 :             : static void
     753                 :           0 : set_or_unset_selection_state_flags (CcBarChartGroup *self,
     754                 :             :                                     gboolean         set)
     755                 :             : {
     756                 :             :   GtkWidget *selected_widget;
     757                 :             : 
     758      [ #  #  # ]:           0 :   switch (self->selection_state)
     759                 :             :     {
     760                 :           0 :     case SELECTION_STATE_BAR:
     761                 :           0 :       selected_widget = GTK_WIDGET (self->bars->pdata[self->selected_bar_index]);
     762                 :           0 :       break;
     763                 :           0 :     case SELECTION_STATE_GROUP:
     764                 :           0 :       selected_widget = GTK_WIDGET (self);
     765                 :           0 :       break;
     766                 :           0 :     case SELECTION_STATE_NONE:
     767                 :             :     default:
     768                 :           0 :       selected_widget = NULL;
     769                 :           0 :       break;
     770                 :             :     }
     771                 :             : 
     772         [ #  # ]:           0 :   if (selected_widget != NULL)
     773                 :             :     {
     774         [ #  # ]:           0 :       if (set)
     775                 :           0 :         gtk_widget_set_state_flags (selected_widget, GTK_STATE_FLAG_SELECTED, FALSE);
     776                 :             :       else
     777                 :           0 :         gtk_widget_unset_state_flags (selected_widget, GTK_STATE_FLAG_SELECTED);
     778                 :             : 
     779                 :           0 :       gtk_accessible_update_state (GTK_ACCESSIBLE (selected_widget),
     780                 :             :                                    GTK_ACCESSIBLE_STATE_SELECTED, set,
     781                 :             :                                    -1);
     782                 :             :     }
     783                 :           0 : }
     784                 :             : 
     785                 :             : /**
     786                 :             :  * cc_bar_chart_group_get_is_selected:
     787                 :             :  * @self: a #CcBarChartGroup
     788                 :             :  *
     789                 :             :  * Get the value of #CcBarChartGroup:is-selected.
     790                 :             :  *
     791                 :             :  * Returns: `TRUE` if the group is selected, `FALSE` otherwise
     792                 :             :  */
     793                 :             : gboolean
     794                 :           0 : cc_bar_chart_group_get_is_selected (CcBarChartGroup *self)
     795                 :             : {
     796                 :           0 :   g_return_val_if_fail (CC_IS_BAR_CHART_GROUP (self), FALSE);
     797                 :             : 
     798                 :           0 :   return (self->selection_state == SELECTION_STATE_GROUP);
     799                 :             : }
     800                 :             : 
     801                 :             : /**
     802                 :             :  * cc_bar_chart_group_set_is_selected:
     803                 :             :  * @self: a #CcBarChartGroup
     804                 :             :  * @is_selected: `TRUE` if the group is selected, `FALSE` otherwise
     805                 :             :  *
     806                 :             :  * Set the value of #CcBarChartGroup:is-selected.
     807                 :             :  */
     808                 :             : void
     809                 :           0 : cc_bar_chart_group_set_is_selected (CcBarChartGroup *self,
     810                 :             :                                     gboolean         is_selected)
     811                 :             : {
     812                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_GROUP (self));
     813                 :             : 
     814                 :             :   /* If the group is not selectable, pass this through to the first bar. */
     815         [ #  # ]:           0 :   if (!self->selectable)
     816                 :             :     {
     817         [ #  # ]:           0 :       if (self->bars->len > 0)
     818                 :           0 :         cc_bar_chart_group_set_selected_index (self, is_selected, 0);
     819                 :           0 :       return;
     820                 :             :     }
     821                 :             : 
     822         [ #  # ]:           0 :   if ((self->selection_state == SELECTION_STATE_GROUP) == is_selected)
     823                 :           0 :     return;
     824                 :             : 
     825                 :             :   /* Update state and flags. */
     826                 :           0 :   set_or_unset_selection_state_flags (self, FALSE);
     827                 :           0 :   self->selection_state = is_selected ? SELECTION_STATE_GROUP : SELECTION_STATE_NONE;
     828                 :           0 :   set_or_unset_selection_state_flags (self, TRUE);
     829                 :             : 
     830                 :             :   /* Re-render */
     831                 :           0 :   gtk_widget_queue_draw (GTK_WIDGET (self));
     832                 :             : 
     833                 :           0 :   g_object_freeze_notify (G_OBJECT (self));
     834                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_IS_SELECTED]);
     835                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTED_INDEX]);
     836                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTED_INDEX_SET]);
     837                 :           0 :   g_object_thaw_notify (G_OBJECT (self));
     838                 :             : }
     839                 :             : 
     840                 :             : /**
     841                 :             :  * cc_bar_chart_group_get_selected_index:
     842                 :             :  * @self: a #CcBarChartGroup
     843                 :             :  * @out_index: (out) (optional): return location for the selected index, or
     844                 :             :  *   `NULL` to ignore
     845                 :             :  *
     846                 :             :  * Get the currently selected bar index.
     847                 :             :  *
     848                 :             :  * If no bar is currently selected, or if the group as a whole is selected
     849                 :             :  * (see cc_bar_chart_group_get_is_selected()), @out_index will be set to `0` and
     850                 :             :  * `FALSE` will be returned.
     851                 :             :  *
     852                 :             :  * Returns: `TRUE` if a bar is currently selected, `FALSE` otherwise
     853                 :             :  */
     854                 :             : gboolean
     855                 :           0 : cc_bar_chart_group_get_selected_index (CcBarChartGroup *self,
     856                 :             :                                        size_t          *out_index)
     857                 :             : {
     858                 :           0 :   g_return_val_if_fail (CC_IS_BAR_CHART_GROUP (self), FALSE);
     859                 :             : 
     860         [ #  # ]:           0 :   if (out_index != NULL)
     861         [ #  # ]:           0 :     *out_index = (self->selection_state == SELECTION_STATE_BAR) ? self->selected_bar_index : 0;
     862                 :             : 
     863                 :           0 :   return (self->selection_state == SELECTION_STATE_BAR);
     864                 :             : }
     865                 :             : 
     866                 :             : /**
     867                 :             :  * cc_bar_chart_group_set_selected_index:
     868                 :             :  * @self: a #CcBarChartGroup
     869                 :             :  * @is_selected: `TRUE` if a bar should be selected, `FALSE` if everything
     870                 :             :  *   should be unselected
     871                 :             :  * @idx: index of the data to select, ignored if @is_selected is `FALSE`
     872                 :             :  *
     873                 :             :  * Set the currently selected bar index, or unselect everything.
     874                 :             :  *
     875                 :             :  * If @is_selected is `TRUE`, the bar at @idx will be selected. If @is_selected
     876                 :             :  * is `FALSE`, @idx will be ignored and all bars (and the group itself) will be
     877                 :             :  * unselected.
     878                 :             :  */
     879                 :             : void
     880                 :           0 : cc_bar_chart_group_set_selected_index (CcBarChartGroup *self,
     881                 :             :                                        gboolean         is_selected,
     882                 :             :                                        size_t           idx)
     883                 :             : {
     884                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_GROUP (self));
     885                 :           0 :   g_return_if_fail (!is_selected || idx < self->bars->len);
     886                 :             : 
     887   [ #  #  #  #  :           0 :   if ((is_selected && self->selection_state == SELECTION_STATE_BAR && self->selected_bar_index == idx) ||
             #  #  #  # ]
     888         [ #  # ]:           0 :       (!is_selected && self->selection_state == SELECTION_STATE_NONE))
     889                 :           0 :     return;
     890                 :             : 
     891                 :             :   /* Update state and flags. */
     892                 :           0 :   set_or_unset_selection_state_flags (self, FALSE);
     893                 :             : 
     894         [ #  # ]:           0 :   self->selection_state = is_selected ? SELECTION_STATE_BAR : SELECTION_STATE_NONE;
     895         [ #  # ]:           0 :   self->selected_bar_index = is_selected ? idx : 0;
     896                 :             : 
     897                 :           0 :   set_or_unset_selection_state_flags (self, TRUE);
     898                 :             : 
     899                 :             :   /* Re-render */
     900                 :           0 :   gtk_widget_queue_draw (GTK_WIDGET (self));
     901                 :             : 
     902                 :           0 :   g_object_freeze_notify (G_OBJECT (self));
     903                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_IS_SELECTED]);
     904                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTED_INDEX]);
     905                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SELECTED_INDEX_SET]);
     906                 :           0 :   g_object_thaw_notify (G_OBJECT (self));
     907                 :             : }
     908                 :             : 
     909                 :             : /**
     910                 :             :  * cc_bar_chart_group_get_scale:
     911                 :             :  * @self: a #CcBarChartGroup
     912                 :             :  *
     913                 :             :  * Get the value of #CcBarChartGroup:scale.
     914                 :             :  *
     915                 :             :  * Returns: pixels per data unit to render the bars with
     916                 :             :  */
     917                 :             : double
     918                 :           0 : cc_bar_chart_group_get_scale (CcBarChartGroup *self)
     919                 :             : {
     920                 :           0 :   g_return_val_if_fail (CC_IS_BAR_CHART_GROUP (self), NAN);
     921                 :             : 
     922                 :           0 :   return self->scale;
     923                 :             : }
     924                 :             : 
     925                 :             : /**
     926                 :             :  * cc_bar_chart_group_set_scale:
     927                 :             :  * @self: a #CcBarChartGroup
     928                 :             :  * @scale: pixels per data unit to render the bars with
     929                 :             :  *
     930                 :             :  * Set the value of #CcBarChartGroup:scale.
     931                 :             :  */
     932                 :             : void
     933                 :           0 : cc_bar_chart_group_set_scale (CcBarChartGroup *self,
     934                 :             :                               double           scale)
     935                 :             : {
     936                 :           0 :   g_return_if_fail (CC_IS_BAR_CHART_GROUP (self));
     937                 :           0 :   g_return_if_fail (scale > 0.0);
     938                 :             : 
     939         [ #  # ]:           0 :   if (scale == self->scale)
     940                 :           0 :     return;
     941                 :             : 
     942                 :           0 :   self->scale = scale;
     943                 :             : 
     944                 :             :   /* Re-render */
     945                 :           0 :   gtk_widget_queue_allocate (GTK_WIDGET (self));
     946                 :             : 
     947                 :           0 :   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SCALE]);
     948                 :             : }
        

Generated by: LCOV version 2.0-1