hythmbox-2.98/widgets/eggwrapbox.c

Location Tool Test ID Function Issue
eggwrapbox.c:736:31 clang-analyzer Division by zero
eggwrapbox.c:736:31 clang-analyzer Division by zero
eggwrapbox.c:1930:15 clang-analyzer Value stored to 'extra_pixels' is never read
eggwrapbox.c:1930:15 clang-analyzer Value stored to 'extra_pixels' is never read
eggwrapbox.c:1934:13 clang-analyzer Value stored to 'extra_pixels' is never read
eggwrapbox.c:1934:13 clang-analyzer Value stored to 'extra_pixels' is never read
eggwrapbox.c:2111:15 clang-analyzer Value stored to 'extra_pixels' is never read
eggwrapbox.c:2111:15 clang-analyzer Value stored to 'extra_pixels' is never read
eggwrapbox.c:2115:13 clang-analyzer Value stored to 'extra_pixels' is never read
eggwrapbox.c:2115:13 clang-analyzer Value stored to 'extra_pixels' is never read
   1 /* eggwrapbox.c
   2  * Copyright (C) 2007-2010 Openismus GmbH
   3  *
   4  * Authors:
   5  *      Tristan Van Berkom <tristanvb@openismus.com>
   6  *
   7  * This library is free software; you can redistribute it and/or
   8  * modify it under the terms of the GNU Library General Public
   9  * License as published by the Free Software Foundation; either
  10  * version 2 of the License, or (at your option) any later version.
  11  *
  12  * This library is distributed in the hope that it will be useful,
  13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15  * Library General Public License for more details.
  16  *
  17  * You should have received a copy of the GNU Library General Public
  18  * License along with this library; if not, write to the
  19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20  * Boston, MA 02111-1307, USA.
  21  */
  22 
  23 
  24 /**
  25  * SECTION:eggwrapbox
  26  * @Short_Description: A container that wraps its children
  27  * @Title: EggWrapBox
  28  *
  29  * #EggWrapBox positions child widgets in sequence according to its
  30  * orientation. For instance, with the horizontal orientation, the widgets
  31  * will be arranged from left to right, starting a new row under the
  32  * previous row when necessary. Reducing the width in this case will
  33  * require more rows, so a larger height will be requested.
  34  *
  35  * Likewise, with the vertical orientation, the widgets will be arranged
  36  * from top to bottom, starting a new column to the right when necessary.
  37  * Reducing the height will require more columns, so a larger width will be
  38  * requested.
  39  *
  40  * Unlike a GtkTable, the child widgets do not need to align in a grid.
  41  */
  42 
  43 #ifdef HAVE_CONFIG_H
  44 #include <config.h>
  45 #endif
  46 
  47 #include "eggwrapbox.h"
  48 #include "eggwrapbox-enums.h"
  49 
  50 #define P_(msgid) (msgid)
  51 #define GTK_PARAM_READWRITE (G_PARAM_READABLE | G_PARAM_WRITABLE)
  52 
  53 
  54 typedef struct _EggWrapBoxChild  EggWrapBoxChild;
  55 
  56 enum {
  57   PROP_0,
  58   PROP_ORIENTATION,
  59   PROP_ALLOCATION_MODE,
  60   PROP_HORIZONTAL_SPREADING,
  61   PROP_VERTICAL_SPREADING,
  62   PROP_HORIZONTAL_SPACING,
  63   PROP_VERTICAL_SPACING,
  64   PROP_MINIMUM_LINE_CHILDREN,
  65   PROP_NATURAL_LINE_CHILDREN
  66 };
  67 
  68 enum
  69 {
  70   CHILD_PROP_0,
  71   CHILD_PROP_PACKING
  72 };
  73 
  74 struct _EggWrapBoxPrivate
  75 {
  76   GtkOrientation        orientation;
  77   EggWrapAllocationMode mode;
  78   EggWrapBoxSpreading   horizontal_spreading;
  79   EggWrapBoxSpreading   vertical_spreading;
  80 
  81   guint16               vertical_spacing;
  82   guint16               horizontal_spacing;
  83 
  84   guint16               minimum_line_children;
  85   guint16               natural_line_children;
  86 
  87   GList                *children;
  88 };
  89 
  90 struct _EggWrapBoxChild
  91 {
  92   GtkWidget        *widget;
  93 
  94   EggWrapBoxPacking packing;
  95 };
  96 
  97 /* GObjectClass */
  98 static void egg_wrap_box_get_property         (GObject             *object,
  99                                                guint                prop_id,
 100                                                GValue              *value,
 101                                                GParamSpec          *pspec);
 102 static void egg_wrap_box_set_property         (GObject             *object,
 103                                                guint                prop_id,
 104                                                const GValue        *value,
 105                                                GParamSpec          *pspec);
 106 
 107 /* GtkWidgetClass */
 108 static void egg_wrap_box_size_allocate        (GtkWidget           *widget,
 109                                                GtkAllocation       *allocation);
 110 
 111 /* GtkContainerClass */
 112 static void egg_wrap_box_add                  (GtkContainer        *container,
 113                                                GtkWidget           *widget);
 114 static void egg_wrap_box_remove               (GtkContainer        *container,
 115                                                GtkWidget           *widget);
 116 static void egg_wrap_box_forall               (GtkContainer        *container,
 117                                                gboolean             include_internals,
 118                                                GtkCallback          callback,
 119                                                gpointer             callback_data);
 120 static void egg_wrap_box_set_child_property   (GtkContainer        *container,
 121                                                GtkWidget           *child,
 122                                                guint                property_id,
 123                                                const GValue        *value,
 124                                                GParamSpec          *pspec);
 125 static void egg_wrap_box_get_child_property   (GtkContainer        *container,
 126                                                GtkWidget           *child,
 127                                                guint                property_id,
 128                                                GValue              *value,
 129                                                GParamSpec          *pspec);
 130 static GType egg_wrap_box_child_type          (GtkContainer        *container);
 131 
 132 
 133 /* GtkWidget      */
 134 static GtkSizeRequestMode egg_wrap_box_get_request_mode (GtkWidget           *widget);
 135 static void egg_wrap_box_get_preferred_width            (GtkWidget           *widget,
 136                                                          gint                *minimum_size,
 137                                                          gint                *natural_size);
 138 static void egg_wrap_box_get_preferred_height           (GtkWidget           *widget,
 139                                                          gint                *minimum_size,
 140                                                          gint                *natural_size);
 141 static void egg_wrap_box_get_preferred_height_for_width (GtkWidget           *box,
 142                                                          gint                 width,
 143                                                          gint                *minimum_height,
 144                                                          gint                *natural_height);
 145 static void egg_wrap_box_get_preferred_width_for_height (GtkWidget           *box,
 146                                                          gint                 width,
 147                                                          gint                *minimum_height,
 148                                                          gint                *natural_height);
 149 
 150 
 151 G_DEFINE_TYPE_WITH_CODE (EggWrapBox, egg_wrap_box, GTK_TYPE_CONTAINER,
 152                          G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
 153 
 154 
 155 #define ORIENTATION_SPREADING(box)					\
 156   (((EggWrapBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
 157    ((EggWrapBox *)(box))->priv->horizontal_spreading :			\
 158    ((EggWrapBox *)(box))->priv->vertical_spreading)
 159 
 160 #define OPPOSING_ORIENTATION_SPREADING(box)				\
 161   (((EggWrapBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
 162    ((EggWrapBox *)(box))->priv->vertical_spreading :			\
 163    ((EggWrapBox *)(box))->priv->horizontal_spreading)
 164 
 165 
 166 
 167 static void
 168 egg_wrap_box_class_init (EggWrapBoxClass *class)
 169 {
 170   GObjectClass      *gobject_class    = G_OBJECT_CLASS (class);
 171   GtkWidgetClass    *widget_class     = GTK_WIDGET_CLASS (class);
 172   GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (class);
 173 
 174   gobject_class->get_property         = egg_wrap_box_get_property;
 175   gobject_class->set_property         = egg_wrap_box_set_property;
 176 
 177   widget_class->size_allocate         = egg_wrap_box_size_allocate;
 178   widget_class->get_request_mode      = egg_wrap_box_get_request_mode;
 179   widget_class->get_preferred_width   = egg_wrap_box_get_preferred_width;
 180   widget_class->get_preferred_height  = egg_wrap_box_get_preferred_height;
 181   widget_class->get_preferred_height_for_width = egg_wrap_box_get_preferred_height_for_width;
 182   widget_class->get_preferred_width_for_height = egg_wrap_box_get_preferred_width_for_height;
 183 
 184   container_class->add                = egg_wrap_box_add;
 185   container_class->remove             = egg_wrap_box_remove;
 186   container_class->forall             = egg_wrap_box_forall;
 187   container_class->child_type         = egg_wrap_box_child_type;
 188   container_class->set_child_property = egg_wrap_box_set_child_property;
 189   container_class->get_child_property = egg_wrap_box_get_child_property;
 190   gtk_container_class_handle_border_width (container_class);
 191 
 192   /* GObjectClass properties */
 193   g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
 194 
 195   /**
 196    * EggWrapBox:allocation-mode:
 197    *
 198    * The #EggWrapAllocationMode to use.
 199    */
 200   g_object_class_install_property (gobject_class,
 201                                    PROP_ALLOCATION_MODE,
 202                                    g_param_spec_uint ("allocation-mode",
 203                                                       P_("Allocation Mode"),
 204                                                       P_("The allocation mode to use"),
 205                                                       0, EGG_WRAP_ALLOCATE_HOMOGENEOUS,
 206                                                       EGG_WRAP_ALLOCATE_FREE,
 207                                                       GTK_PARAM_READWRITE));
 208 
 209   /**
 210    * EggWrapBox:horizontal-spreading:
 211    *
 212    * The #EggWrapBoxSpreading to used to define what is done with extra
 213    * space in a given orientation.
 214    */
 215   g_object_class_install_property (gobject_class,
 216                                    PROP_HORIZONTAL_SPREADING,
 217                                    g_param_spec_uint ("horizontal-spreading",
 218                                                       P_("Horizontal Spreading"),
 219                                                       P_("The spreading mode to use horizontally"),
 220                                                       0, EGG_WRAP_BOX_SPREAD_EXPAND,
 221 						      EGG_WRAP_BOX_SPREAD_START,
 222                                                       GTK_PARAM_READWRITE));
 223 
 224   /**
 225    * EggWrapBox:vertical-spreading:
 226    *
 227    * The #EggWrapBoxSpreading to used to define what is done with extra
 228    * space in a given orientation.
 229    */
 230   g_object_class_install_property (gobject_class,
 231                                    PROP_VERTICAL_SPREADING,
 232                                    g_param_spec_uint ("vertical-spreading",
 233                                                       P_("Vertical Spreading"),
 234                                                       P_("The spreading mode to use vertically"),
 235 						      0, EGG_WRAP_BOX_SPREAD_EXPAND,
 236                                                       EGG_WRAP_BOX_SPREAD_START,
 237                                                       GTK_PARAM_READWRITE));
 238 
 239 
 240   /**
 241    * EggWrapBox:minimum-line-children:
 242    *
 243    * The minimum number of children to allocate consecutively in the given orientation.
 244    *
 245    * <note><para>Setting the minimum children per line ensures
 246    * that a reasonably small height will be requested
 247    * for the overall minimum width of the box.</para></note>
 248    *
 249    */
 250   g_object_class_install_property (gobject_class,
 251                                    PROP_MINIMUM_LINE_CHILDREN,
 252                                    g_param_spec_uint ("minimum-line-children",
 253                                                       P_("Minimum Line Children"),
 254                                                       P_("The minimum number of children to allocate "
 255                                                         "consecutively in the given orientation."),
 256                                                       0,
 257                                                       65535,
 258                                                       0,
 259                                                       GTK_PARAM_READWRITE));
 260 
 261   /**
 262    * EggWrapBox:natural-line-children:
 263    *
 264    * The maximum amount of children to request space for consecutively in the given orientation.
 265    *
 266    */
 267   g_object_class_install_property (gobject_class,
 268                                    PROP_NATURAL_LINE_CHILDREN,
 269                                    g_param_spec_uint ("natural-line-children",
 270                                                       P_("Natural Line Children"),
 271                                                       P_("The maximum amount of children to request space for "
 272                                                         "consecutively in the given orientation."),
 273                                                       0,
 274                                                       65535,
 275                                                       0,
 276                                                       GTK_PARAM_READWRITE));
 277 
 278   /**
 279    * EggWrapBox:vertical-spacing:
 280    *
 281    * The amount of vertical space between two children.
 282    *
 283    */
 284   g_object_class_install_property (gobject_class,
 285                                    PROP_VERTICAL_SPACING,
 286                                    g_param_spec_uint ("vertical-spacing",
 287                                                      P_("Vertical spacing"),
 288                                                      P_("The amount of vertical space between two children"),
 289                                                      0,
 290                                                      65535,
 291                                                      0,
 292                                                      GTK_PARAM_READWRITE));
 293 
 294   /**
 295    * EggWrapBox:horizontal-spacing:
 296    *
 297    * The amount of horizontal space between two children.
 298    *
 299    */
 300   g_object_class_install_property (gobject_class,
 301                                    PROP_HORIZONTAL_SPACING,
 302                                    g_param_spec_uint ("horizontal-spacing",
 303                                                      P_("Horizontal spacing"),
 304                                                      P_("The amount of horizontal space between two children"),
 305                                                      0,
 306                                                      65535,
 307                                                      0,
 308                                                      GTK_PARAM_READWRITE));
 309 
 310   /* GtkContainerClass child properties */
 311 
 312   /**
 313    * EggWrapBox:packing:
 314    *
 315    * The #EggWrapBoxPacking options to specify how to pack a child into the box.
 316    */
 317   gtk_container_class_install_child_property (container_class,
 318                                               CHILD_PROP_PACKING,
 319                                               g_param_spec_flags
 320                                               ("packing",
 321                                                P_("Packing"),
 322                                                P_("The packing options to use for this child"),
 323                                                EGG_TYPE_WRAP_BOX_PACKING, 0,
 324                                                GTK_PARAM_READWRITE));
 325 
 326   g_type_class_add_private (class, sizeof (EggWrapBoxPrivate));
 327 }
 328 
 329 static void
 330 egg_wrap_box_init (EggWrapBox *box)
 331 {
 332   EggWrapBoxPrivate *priv;
 333 
 334   box->priv = priv =
 335     G_TYPE_INSTANCE_GET_PRIVATE (box, EGG_TYPE_WRAP_BOX, EggWrapBoxPrivate);
 336 
 337   priv->orientation          = GTK_ORIENTATION_HORIZONTAL;
 338   priv->mode                 = EGG_WRAP_ALLOCATE_FREE;
 339   priv->horizontal_spreading = EGG_WRAP_BOX_SPREAD_START;
 340   priv->vertical_spreading   = EGG_WRAP_BOX_SPREAD_START;
 341   priv->horizontal_spacing   = 0;
 342   priv->vertical_spacing     = 0;
 343   priv->children             = NULL;
 344 
 345   gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
 346 }
 347 
 348 /*****************************************************
 349  *                  GObectClass                      *
 350  *****************************************************/
 351 static void
 352 egg_wrap_box_get_property (GObject      *object,
 353                            guint         prop_id,
 354                            GValue       *value,
 355                            GParamSpec   *pspec)
 356 {
 357   EggWrapBox        *box  = EGG_WRAP_BOX (object);
 358   EggWrapBoxPrivate *priv = box->priv;
 359 
 360   switch (prop_id)
 361     {
 362     case PROP_ORIENTATION:
 363       g_value_set_boolean (value, priv->orientation);
 364       break;
 365     case PROP_ALLOCATION_MODE:
 366       g_value_set_uint (value, priv->mode);
 367       break;
 368     case PROP_HORIZONTAL_SPREADING:
 369       g_value_set_uint (value, priv->horizontal_spreading);
 370       break;
 371     case PROP_VERTICAL_SPREADING:
 372       g_value_set_uint (value, priv->vertical_spreading);
 373       break;
 374     case PROP_HORIZONTAL_SPACING:
 375       g_value_set_uint (value, priv->horizontal_spacing);
 376       break;
 377     case PROP_VERTICAL_SPACING:
 378       g_value_set_uint (value, priv->vertical_spacing);
 379       break;
 380     case PROP_MINIMUM_LINE_CHILDREN:
 381       g_value_set_uint (value, priv->minimum_line_children);
 382       break;
 383     case PROP_NATURAL_LINE_CHILDREN:
 384       g_value_set_uint (value, priv->natural_line_children);
 385       break;
 386     default:
 387       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 388       break;
 389     }
 390 }
 391 
 392 static void
 393 egg_wrap_box_set_property (GObject      *object,
 394                            guint         prop_id,
 395                            const GValue *value,
 396                            GParamSpec   *pspec)
 397 {
 398   EggWrapBox        *box = EGG_WRAP_BOX (object);
 399   EggWrapBoxPrivate *priv   = box->priv;
 400 
 401   switch (prop_id)
 402     {
 403     case PROP_ORIENTATION:
 404       priv->orientation = g_value_get_enum (value);
 405 
 406       /* Re-box the children in the new orientation */
 407       gtk_widget_queue_resize (GTK_WIDGET (box));
 408       break;
 409     case PROP_ALLOCATION_MODE:
 410       egg_wrap_box_set_allocation_mode (box, g_value_get_uint (value));
 411       break;
 412     case PROP_HORIZONTAL_SPREADING:
 413       egg_wrap_box_set_horizontal_spreading (box, g_value_get_uint (value));
 414       break;
 415     case PROP_VERTICAL_SPREADING:
 416       egg_wrap_box_set_vertical_spreading (box, g_value_get_uint (value));
 417       break;
 418     case PROP_HORIZONTAL_SPACING:
 419       egg_wrap_box_set_horizontal_spacing (box, g_value_get_uint (value));
 420       break;
 421     case PROP_VERTICAL_SPACING:
 422       egg_wrap_box_set_vertical_spacing (box, g_value_get_uint (value));
 423       break;
 424     case PROP_MINIMUM_LINE_CHILDREN:
 425       egg_wrap_box_set_minimum_line_children (box, g_value_get_uint (value));
 426       break;
 427     case PROP_NATURAL_LINE_CHILDREN:
 428       egg_wrap_box_set_natural_line_children (box, g_value_get_uint (value));
 429       break;
 430     default:
 431       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 432       break;
 433     }
 434 }
 435 
 436 /*****************************************************
 437  *                 GtkWidgetClass                    *
 438  *****************************************************/
 439 
 440 static gint
 441 get_visible_children (EggWrapBox  *box)
 442 {
 443   EggWrapBoxPrivate *priv = box->priv;
 444   GList             *list;
 445   gint               i = 0;
 446 
 447   for (list = priv->children; list; list = list->next)
 448     {
 449       EggWrapBoxChild *child = list->data;
 450 
 451       if (!gtk_widget_get_visible (child->widget))
 452         continue;
 453 
 454       i++;
 455     }
 456 
 457   return i;
 458 }
 459 
 460 static gint
 461 get_visible_expand_children (EggWrapBox     *box,
 462                              GtkOrientation  orientation,
 463                              GList          *cursor,
 464                              gint            n_visible)
 465 {
 466   GList *list;
 467   gint   i, expand_children = 0;
 468 
 469   for (i = 0, list = cursor; (n_visible > 0 ? i < n_visible : TRUE) && list; list = list->next)
 470     {
 471       EggWrapBoxChild *child = list->data;
 472 
 473       if (!gtk_widget_get_visible (child->widget))
 474         continue;
 475 
 476       if ((orientation == GTK_ORIENTATION_HORIZONTAL && (child->packing & EGG_WRAP_BOX_H_EXPAND) != 0) ||
 477           (orientation == GTK_ORIENTATION_VERTICAL   && (child->packing & EGG_WRAP_BOX_V_EXPAND) != 0))
 478         expand_children++;
 479 
 480       i++;
 481     }
 482 
 483   return expand_children;
 484 }
 485 
 486 /* Used in columned modes where all items share at least their
 487  * equal widths or heights
 488  */
 489 static void
 490 get_average_item_size (EggWrapBox      *box,
 491                        GtkOrientation   orientation,
 492                        gint            *min_size,
 493                        gint            *nat_size)
 494 {
 495   EggWrapBoxPrivate *priv = box->priv;
 496   GList             *list;
 497   gint               max_min_size = 0;
 498   gint               max_nat_size = 0;
 499 
 500   for (list = priv->children; list; list = list->next)
 501     {
 502       EggWrapBoxChild *child = list->data;
 503       gint             child_min, child_nat;
 504 
 505       if (!gtk_widget_get_visible (child->widget))
 506         continue;
 507 
 508       if (orientation == GTK_ORIENTATION_HORIZONTAL)
 509         gtk_widget_get_preferred_width (child->widget, &child_min, &child_nat);
 510       else
 511         gtk_widget_get_preferred_height (child->widget, &child_min, &child_nat);
 512 
 513       max_min_size = MAX (max_min_size, child_min);
 514       max_nat_size = MAX (max_nat_size, child_nat);
 515     }
 516 
 517   if (min_size)
 518     *min_size = max_min_size;
 519 
 520   if (nat_size)
 521     *nat_size = max_nat_size;
 522 }
 523 
 524 
 525 /* Gets the largest minimum/natural size for a given size
 526  * (used to get the largest item heights for a fixed item width and the opposite) */
 527 static void
 528 get_largest_size_for_opposing_orientation (EggWrapBox         *box,
 529                                            GtkOrientation      orientation,
 530                                            gint                item_size,
 531                                            gint               *min_item_size,
 532                                            gint               *nat_item_size)
 533 {
 534   EggWrapBoxPrivate *priv = box->priv;
 535   GList             *list;
 536   gint               max_min_size = 0;
 537   gint               max_nat_size = 0;
 538 
 539   for (list = priv->children; list; list = list->next)
 540     {
 541       EggWrapBoxChild *child = list->data;
 542       gint             child_min, child_nat;
 543 
 544       if (!gtk_widget_get_visible (child->widget))
 545         continue;
 546 
 547       if (orientation == GTK_ORIENTATION_HORIZONTAL)
 548         gtk_widget_get_preferred_height_for_width (child->widget,
 549                                                          item_size,
 550                                                          &child_min, &child_nat);
 551       else
 552         gtk_widget_get_preferred_width_for_height (child->widget,
 553                                                    item_size,
 554                                                    &child_min, &child_nat);
 555 
 556       max_min_size = MAX (max_min_size, child_min);
 557       max_nat_size = MAX (max_nat_size, child_nat);
 558     }
 559 
 560   if (min_item_size)
 561     *min_item_size = max_min_size;
 562 
 563   if (nat_item_size)
 564     *nat_item_size = max_nat_size;
 565 }
 566 
 567 
 568 /* Gets the largest minimum/natural size on a single line for a given size
 569  * (used to get the largest line heights for a fixed item width and the opposite
 570  * while itterating over a list of children, note the new index is returned) */
 571 static GList *
 572 get_largest_size_for_line_in_opposing_orientation (EggWrapBox       *box,
 573                                                    GtkOrientation    orientation,
 574                                                    GList            *cursor,
 575                                                    gint              line_length,
 576                                                    GtkRequestedSize *item_sizes,
 577                                                    gint              extra_pixels,
 578                                                    gint             *min_item_size,
 579                                                    gint             *nat_item_size)
 580 {
 581   GList  *list;
 582   gint    max_min_size = 0;
 583   gint    max_nat_size = 0;
 584   gint    i;
 585 
 586   for (list = cursor, i = 0; list && i < line_length; list = list->next)
 587     {
 588       EggWrapBoxChild *child = list->data;
 589       gint             child_min, child_nat, this_item_size;
 590 
 591       if (!gtk_widget_get_visible (child->widget))
 592         continue;
 593 
 594       /* Distribute the extra pixels to the first children in the line
 595        * (could be fancier and spread them out more evenly) */
 596       this_item_size = item_sizes[i].minimum_size;
 597       if (extra_pixels > 0 && ORIENTATION_SPREADING (box) == EGG_WRAP_BOX_SPREAD_EXPAND)
 598         {
 599           this_item_size++;
 600           extra_pixels--;
 601         }
 602 
 603       if (orientation == GTK_ORIENTATION_HORIZONTAL)
 604         gtk_widget_get_preferred_height_for_width (child->widget,
 605                                                    this_item_size,
 606                                                    &child_min, &child_nat);
 607       else
 608         gtk_widget_get_preferred_width_for_height (child->widget,
 609                                                    this_item_size,
 610                                                    &child_min, &child_nat);
 611 
 612       max_min_size = MAX (max_min_size, child_min);
 613       max_nat_size = MAX (max_nat_size, child_nat);
 614 
 615       i++;
 616     }
 617 
 618   if (min_item_size)
 619     *min_item_size = max_min_size;
 620 
 621   if (nat_item_size)
 622     *nat_item_size = max_nat_size;
 623 
 624   /* Return next item in the list */
 625   return list;
 626 }
 627 
 628 
 629 /* Gets the largest minimum/natural size on a single line for a given allocated line size
 630  * (used to get the largest line heights for a width in pixels and the opposite
 631  * while itterating over a list of children, note the new index is returned) */
 632 static GList *
 633 get_largest_size_for_free_line_in_opposing_orientation (EggWrapBox      *box,
 634                                                         GtkOrientation   orientation,
 635                                                         GList           *cursor,
 636                                                         gint             min_items,
 637                                                         gint             avail_size,
 638                                                         gint            *min_item_size,
 639                                                         gint            *nat_item_size,
 640                                                         gint            *extra_pixels,
 641                                                         GArray         **ret_array)
 642 {
 643   EggWrapBoxPrivate *priv = box->priv;
 644   GtkRequestedSize  *sizes;
 645   GList             *list;
 646   GArray            *array;
 647   gint               max_min_size = 0;
 648   gint               max_nat_size = 0;
 649   gint               i, size = avail_size;
 650   gint               line_length, spacing;
 651   gint               expand_children = 0;
 652   gint               expand_per_child;
 653   gint               expand_remainder;
 654 
 655   if (orientation == GTK_ORIENTATION_HORIZONTAL)
 656     spacing = priv->horizontal_spacing;
 657   else
 658     spacing = priv->vertical_spacing;
 659 
 660   /* First determine the length of this line in items (how many items fit) */
 661   for (i = 0, list = cursor; size > 0 && list; list = list->next)
 662     {
 663       EggWrapBoxChild *child = list->data;
 664       gint             child_size;
 665 
 666       if (!gtk_widget_get_visible (child->widget))
 667         continue;
 668 
 669       if (orientation == GTK_ORIENTATION_HORIZONTAL)
 670         gtk_widget_get_preferred_width (child->widget, NULL, &child_size);
 671       else
 672         gtk_widget_get_preferred_height (child->widget, NULL, &child_size);
 673 
 674       if (i > 0)
 675         child_size += spacing;
 676 
 677       if (size - child_size >= 0)
 678         size -= child_size;
 679       else
 680         break;
 681 
 682       i++;
 683     }
 684 
 685   line_length = MAX (min_items, i);
 686   size        = avail_size;
 687 
 688   /* Collect the sizes of the items on this line */
 689   array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
 690 
 691   for (i = 0, list = cursor; i < line_length && list; list = list->next)
 692     {
 693       EggWrapBoxChild  *child = list->data;
 694       GtkRequestedSize  requested;
 695 
 696       if (!gtk_widget_get_visible (child->widget))
 697         continue;
 698 
 699       requested.data = child;
 700       if (orientation == GTK_ORIENTATION_HORIZONTAL)
 701         gtk_widget_get_preferred_width (child->widget,
 702                                         &requested.minimum_size,
 703                                         &requested.natural_size);
 704       else
 705         gtk_widget_get_preferred_height (child->widget,
 706                                          &requested.minimum_size,
 707                                          &requested.natural_size);
 708 
 709       if (i > 0)
 710         size -= spacing;
 711 
 712       size -= requested.minimum_size;
 713 
 714       g_array_append_val (array, requested);
 715 
 716       i++;
 717     }
 718 
 719   sizes = (GtkRequestedSize *)array->data;
 720   size  = gtk_distribute_natural_allocation (size, array->len, sizes);
 721 
 722   if (extra_pixels)
 723     *extra_pixels = size;
 724 
 725   /* Cut out any expand space if we're not distributing any */
 726   if (ORIENTATION_SPREADING (box) != EGG_WRAP_BOX_SPREAD_EXPAND)
 727     size = 0;
 728 
 729   /* Count how many children are going to expand... */
 730   expand_children = get_visible_expand_children (box, orientation,
 731                                                  cursor, line_length);
 732 
 733   /* If no child prefers to expand, they all get some expand space */
 734   if (expand_children == 0)
 735     {
 736       expand_per_child = size / line_length;
Division by zero
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Division by zero
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

737 expand_remainder = size % line_length; 738 } 739 else 740 { 741 expand_per_child = size / expand_children; 742 expand_remainder = size % expand_children; 743 } 744 745 /* Now add the remaining expand space and get the collective size of this line 746 * in the opposing orientation */ 747 for (i = 0, list = cursor; i < line_length && list; list = list->next) 748 { 749 EggWrapBoxChild *child = list->data; 750 gint child_min, child_nat; 751 752 if (!gtk_widget_get_visible (child->widget)) 753 continue; 754 755 g_assert (child == sizes[i].data); 756 757 if ((orientation == GTK_ORIENTATION_HORIZONTAL && (child->packing & EGG_WRAP_BOX_H_EXPAND) != 0) || 758 (orientation == GTK_ORIENTATION_VERTICAL && (child->packing & EGG_WRAP_BOX_V_EXPAND) != 0) || 759 expand_children == 0) 760 { 761 sizes[i].minimum_size += expand_per_child; 762 if (expand_remainder) 763 { 764 sizes[i].minimum_size++; 765 expand_remainder--; 766 } 767 } 768 769 if (orientation == GTK_ORIENTATION_HORIZONTAL) 770 gtk_widget_get_preferred_height_for_width (child->widget, 771 sizes[i].minimum_size, 772 &child_min, &child_nat); 773 else 774 gtk_widget_get_preferred_width_for_height (child->widget, 775 sizes[i].minimum_size, 776 &child_min, &child_nat); 777 778 max_min_size = MAX (max_min_size, child_min); 779 max_nat_size = MAX (max_nat_size, child_nat); 780 781 i++; 782 } 783 784 if (ret_array) 785 *ret_array = array; 786 else 787 g_array_free (array, TRUE); 788 789 if (min_item_size) 790 *min_item_size = max_min_size; 791 792 if (nat_item_size) 793 *nat_item_size = max_nat_size; 794 795 /* Return the next item */ 796 return list; 797 } 798 799 static void 800 allocate_child (EggWrapBox *box, 801 EggWrapBoxChild *child, 802 gint item_offset, 803 gint line_offset, 804 gint item_size, 805 gint line_size) 806 { 807 EggWrapBoxPrivate *priv = box->priv; 808 GtkAllocation widget_allocation; 809 GtkAllocation child_allocation; 810 811 gtk_widget_get_allocation (GTK_WIDGET (box), &widget_allocation); 812 813 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) 814 { 815 child_allocation.x = widget_allocation.x + item_offset; 816 child_allocation.y = widget_allocation.y + line_offset; 817 child_allocation.width = item_size; 818 child_allocation.height = line_size; 819 } 820 else /* GTK_ORIENTATION_VERTICAL */ 821 { 822 child_allocation.x = widget_allocation.x + line_offset; 823 child_allocation.y = widget_allocation.y + item_offset; 824 child_allocation.width = line_size; 825 child_allocation.height = item_size; 826 } 827 828 gtk_widget_size_allocate (child->widget, &child_allocation); 829 } 830 831 /* fit_aligned_item_requests() helper */ 832 static gint 833 gather_aligned_item_requests (EggWrapBox *box, 834 GtkOrientation orientation, 835 gint line_length, 836 gint item_spacing, 837 gint n_children, 838 GtkRequestedSize *item_sizes) 839 { 840 EggWrapBoxPrivate *priv = box->priv; 841 GList *list; 842 gint i; 843 gint extra_items, natural_line_size = 0; 844 845 extra_items = n_children % line_length; 846 847 for (list = priv->children, i = 0; list; list = list->next, i++) 848 { 849 EggWrapBoxChild *child = list->data; 850 gint child_min, child_nat; 851 gint position; 852 853 if (!gtk_widget_get_visible (child->widget)) 854 continue; 855 856 if (orientation == GTK_ORIENTATION_HORIZONTAL) 857 gtk_widget_get_preferred_width (child->widget, 858 &child_min, &child_nat); 859 else 860 gtk_widget_get_preferred_height (child->widget, 861 &child_min, &child_nat); 862 863 /* Get the index and push it over for the last line when spreading to the end */ 864 position = i % line_length; 865 866 if (ORIENTATION_SPREADING (box) == EGG_WRAP_BOX_SPREAD_END && i >= n_children - extra_items) 867 position += line_length - extra_items; 868 869 /* Round up the size of every column/row */ 870 item_sizes[position].minimum_size = MAX (item_sizes[position].minimum_size, child_min); 871 item_sizes[position].natural_size = MAX (item_sizes[position].natural_size, child_nat); 872 } 873 874 for (i = 0; i < line_length; i++) 875 natural_line_size += item_sizes[i].natural_size; 876 877 natural_line_size += (line_length - 1) * item_spacing; 878 879 return natural_line_size; 880 } 881 882 static GtkRequestedSize * 883 fit_aligned_item_requests (EggWrapBox *box, 884 GtkOrientation orientation, 885 gint avail_size, 886 gint item_spacing, 887 gint *line_length, /* in-out */ 888 gint n_children) 889 { 890 GtkRequestedSize *sizes, *try_sizes; 891 gint try_line_size, try_length; 892 893 sizes = g_new0 (GtkRequestedSize, *line_length); 894 895 /* get the sizes for the initial guess */ 896 try_line_size = 897 gather_aligned_item_requests (box, orientation, *line_length, item_spacing, n_children, sizes); 898 899 /* Try columnizing the whole thing and adding an item to the end of the line; 900 * try to fit as many columns into the available size as possible */ 901 for (try_length = *line_length + 1; try_line_size < avail_size; try_length++) 902 { 903 try_sizes = g_new0 (GtkRequestedSize, try_length); 904 try_line_size = gather_aligned_item_requests (box, orientation, try_length, item_spacing, 905 n_children, try_sizes); 906 907 if (try_line_size <= avail_size) 908 { 909 *line_length = try_length; 910 911 g_free (sizes); 912 sizes = try_sizes; 913 } 914 else 915 { 916 /* oops, this one failed; stick to the last size that fit and then return */ 917 g_free (try_sizes); 918 break; 919 } 920 } 921 922 return sizes; 923 } 924 925 926 typedef struct { 927 GArray *requested; 928 gint extra_pixels; 929 } AllocatedLine; 930 931 static void 932 egg_wrap_box_size_allocate (GtkWidget *widget, 933 GtkAllocation *allocation) 934 { 935 EggWrapBox *box = EGG_WRAP_BOX (widget); 936 EggWrapBoxPrivate *priv = box->priv; 937 gint avail_size, avail_other_size, min_items, item_spacing, line_spacing; 938 EggWrapBoxSpreading item_spreading; 939 EggWrapBoxSpreading line_spreading; 940 941 gtk_widget_set_allocation (widget, allocation); 942 943 min_items = MAX (1, priv->minimum_line_children); 944 945 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) 946 { 947 avail_size = allocation->width; 948 avail_other_size = allocation->height; 949 item_spacing = priv->horizontal_spacing; 950 line_spacing = priv->vertical_spacing; 951 } 952 else /* GTK_ORIENTATION_VERTICAL */ 953 { 954 avail_size = allocation->height; 955 avail_other_size = allocation->width; 956 item_spacing = priv->vertical_spacing; 957 line_spacing = priv->horizontal_spacing; 958 } 959 960 item_spreading = ORIENTATION_SPREADING (box); 961 line_spreading = OPPOSING_ORIENTATION_SPREADING (box); 962 963 964 /********************************************************* 965 * Deal with ALIGNED/HOMOGENEOUS modes first, start with * 966 * initial guesses at item/line sizes * 967 *********************************************************/ 968 if (priv->mode == EGG_WRAP_ALLOCATE_ALIGNED || 969 priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 970 { 971 GtkRequestedSize *line_sizes = NULL; 972 GtkRequestedSize *item_sizes = NULL; 973 GList *list; 974 gint min_item_size, nat_item_size; 975 gint line_length; 976 gint item_size = 0; 977 gint line_size = 0, min_fixed_line_size = 0, nat_fixed_line_size = 0; 978 gint line_offset, item_offset, n_children, n_lines, line_count; 979 gint extra_pixels, extra_per_item = 0, extra_extra = 0; 980 gint extra_line_pixels, extra_per_line = 0, extra_line_extra = 0; 981 gint i, this_line_size; 982 983 get_average_item_size (box, priv->orientation, &min_item_size, &nat_item_size); 984 985 /* By default wrap at the natural item width */ 986 line_length = avail_size / (nat_item_size + item_spacing); 987 988 /* After the above aproximation, check if we cant fit one more on the line */ 989 if (line_length * item_spacing + (line_length + 1) * nat_item_size <= avail_size) 990 line_length++; 991 992 /* Its possible we were allocated just less than the natural width of the 993 * minimum item wrap length */ 994 line_length = MAX (min_items, line_length); 995 996 /* Get how many lines we'll be needing to wrap */ 997 n_children = get_visible_children (box); 998 999 /* Here we just use the largest height-for-width and use that for the height 1000 * of all lines */ 1001 if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 1002 { 1003 n_lines = n_children / line_length; 1004 if ((n_children % line_length) > 0) 1005 n_lines++; 1006 1007 n_lines = MAX (n_lines, 1); 1008 1009 /* Now we need the real item allocation size */ 1010 item_size = (avail_size - (line_length - 1) * item_spacing) / line_length; 1011 1012 /* Cut out the expand space if we're not distributing any */ 1013 if (item_spreading != EGG_WRAP_BOX_SPREAD_EXPAND) 1014 item_size = MIN (item_size, nat_item_size); 1015 1016 get_largest_size_for_opposing_orientation (box, priv->orientation, item_size, 1017 &min_fixed_line_size, 1018 &nat_fixed_line_size); 1019 1020 /* resolve a fixed 'line_size' */ 1021 line_size = (avail_other_size - (n_lines - 1) * line_spacing) / n_lines; 1022 1023 if (line_spreading != EGG_WRAP_BOX_SPREAD_EXPAND) 1024 line_size = MIN (line_size, nat_fixed_line_size); 1025 1026 /* Get the real extra pixels incase of EGG_WRAP_BOX_SPREAD_START lines */ 1027 extra_pixels = avail_size - (line_length - 1) * item_spacing - item_size * line_length; 1028 extra_line_pixels = avail_other_size - (n_lines - 1) * line_spacing - line_size * n_lines; 1029 } 1030 else /* EGG_WRAP_ALLOCATE_ALIGNED */ 1031 { 1032 GList *list; 1033 gboolean first_line = TRUE; 1034 1035 /* Find the amount of columns that can fit aligned into the available space 1036 * and collect their requests. 1037 */ 1038 item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size, 1039 item_spacing, &line_length, n_children); 1040 1041 /* Calculate the number of lines after determining the final line_length */ 1042 n_lines = n_children / line_length; 1043 if ((n_children % line_length) > 0) 1044 n_lines++; 1045 1046 n_lines = MAX (n_lines, 1); 1047 line_sizes = g_new0 (GtkRequestedSize, n_lines); 1048 1049 /* Get the available remaining size */ 1050 avail_size -= (line_length - 1) * item_spacing; 1051 for (i = 0; i < line_length; i++) 1052 avail_size -= item_sizes[i].minimum_size; 1053 1054 /* Perform a natural allocation on the columnized items and get the remaining pixels */ 1055 extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes); 1056 1057 /* Now that we have the size of each column of items find the size of each individual 1058 * line based on the aligned item sizes. 1059 */ 1060 for (i = 0, list = priv->children; list != NULL; i++) 1061 { 1062 1063 list = 1064 get_largest_size_for_line_in_opposing_orientation (box, priv->orientation, 1065 list, line_length, 1066 item_sizes, extra_pixels, 1067 &line_sizes[i].minimum_size, 1068 &line_sizes[i].natural_size); 1069 1070 1071 /* Its possible a line is made of completely invisible children */ 1072 if (line_sizes[i].natural_size > 0) 1073 { 1074 if (first_line) 1075 first_line = FALSE; 1076 else 1077 avail_other_size -= line_spacing; 1078 1079 avail_other_size -= line_sizes[i].minimum_size; 1080 1081 line_sizes[i].data = GINT_TO_POINTER (i); 1082 } 1083 } 1084 1085 /* Distribute space among lines naturally */ 1086 extra_line_pixels = gtk_distribute_natural_allocation (avail_other_size, n_lines, line_sizes); 1087 } 1088 1089 /********************************************************* 1090 * Initial sizes of items/lines guessed at this point, * 1091 * go on to distribute expand space if needed. * 1092 *********************************************************/ 1093 1094 /* FIXME: This portion needs to consider which columns 1095 * and rows asked for expand space and distribute those 1096 * accordingly for the case of ALIGNED allocation. 1097 * 1098 * If at least one child in a column/row asked for expand; 1099 * we should make that row/column expand entirely. 1100 */ 1101 1102 /* Calculate expand space per item */ 1103 if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1104 { 1105 extra_per_item = extra_pixels / MAX (line_length -1, 1); 1106 extra_extra = extra_pixels % MAX (line_length -1, 1); 1107 } 1108 else if (item_spreading == EGG_WRAP_BOX_SPREAD_EXPAND) 1109 { 1110 extra_per_item = extra_pixels / line_length; 1111 extra_extra = extra_pixels % line_length; 1112 } 1113 1114 /* Calculate expand space per line */ 1115 if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1116 { 1117 extra_per_line = extra_line_pixels / MAX (n_lines -1, 1); 1118 extra_line_extra = extra_line_pixels % MAX (n_lines -1, 1); 1119 } 1120 else if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND) 1121 { 1122 extra_per_line = extra_line_pixels / n_lines; 1123 extra_line_extra = extra_line_pixels % n_lines; 1124 } 1125 1126 /********************************************************* 1127 * Prepare item/line initial offsets and jump into the * 1128 * real allocation loop. * 1129 *********************************************************/ 1130 line_offset = item_offset = 0; 1131 1132 /* prepend extra space to item_offset/line_offset for SPREAD_END */ 1133 if (item_spreading == EGG_WRAP_BOX_SPREAD_END) 1134 item_offset += extra_pixels; 1135 1136 if (line_spreading == EGG_WRAP_BOX_SPREAD_END) 1137 line_offset += extra_line_pixels; 1138 1139 /* Get the allocation size for the first line */ 1140 if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 1141 this_line_size = line_size; 1142 else 1143 { 1144 this_line_size = line_sizes[0].minimum_size; 1145 1146 if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND) 1147 { 1148 this_line_size += extra_per_line; 1149 1150 if (extra_line_extra > 0) 1151 this_line_size++; 1152 } 1153 } 1154 1155 for (i = 0, line_count = 0, list = priv->children; list; list = list->next) 1156 { 1157 EggWrapBoxChild *child = list->data; 1158 gint position; 1159 gint this_item_size; 1160 1161 if (!gtk_widget_get_visible (child->widget)) 1162 continue; 1163 1164 /* Get item position */ 1165 position = i % line_length; 1166 1167 /* adjust the line_offset/count at the beginning of each new line */ 1168 if (i > 0 && position == 0) 1169 { 1170 /* Push the line_offset */ 1171 line_offset += this_line_size + line_spacing; 1172 1173 if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1174 { 1175 line_offset += extra_per_line; 1176 1177 if (line_count < extra_line_extra) 1178 line_offset++; 1179 } 1180 1181 line_count++; 1182 1183 /* Get the new line size */ 1184 if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 1185 this_line_size = line_size; 1186 else 1187 { 1188 this_line_size = line_sizes[line_count].minimum_size; 1189 1190 if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND) 1191 { 1192 this_line_size += extra_per_line; 1193 1194 if (line_count < extra_line_extra) 1195 this_line_size++; 1196 } 1197 } 1198 1199 item_offset = 0; 1200 1201 if (item_spreading == EGG_WRAP_BOX_SPREAD_END) 1202 { 1203 item_offset += extra_pixels; 1204 1205 /* If we're on the last line, prepend the space for 1206 * any leading items */ 1207 if (line_count == n_lines -1) 1208 { 1209 gint extra_items = n_children % line_length; 1210 1211 if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 1212 { 1213 item_offset += item_size * (line_length - extra_items); 1214 item_offset += item_spacing * (line_length - extra_items); 1215 } 1216 else 1217 { 1218 gint j; 1219 1220 for (j = 0; j < (line_length - extra_items); j++) 1221 { 1222 item_offset += item_sizes[j].minimum_size; 1223 item_offset += item_spacing; 1224 } 1225 } 1226 } 1227 } 1228 } 1229 1230 /* Push the index along for the last line when spreading to the end */ 1231 if (item_spreading == EGG_WRAP_BOX_SPREAD_END && 1232 line_count == n_lines -1) 1233 { 1234 gint extra_items = n_children % line_length; 1235 1236 position += line_length - extra_items; 1237 } 1238 1239 if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 1240 this_item_size = item_size; 1241 else /* aligned mode */ 1242 this_item_size = item_sizes[position].minimum_size; 1243 1244 if (item_spreading == EGG_WRAP_BOX_SPREAD_EXPAND) 1245 { 1246 this_item_size += extra_per_item; 1247 1248 if (position < extra_extra) 1249 this_item_size++; 1250 } 1251 1252 /* Do the actual allocation */ 1253 allocate_child (box, child, item_offset, line_offset, this_item_size, this_line_size); 1254 1255 item_offset += this_item_size; 1256 item_offset += item_spacing; 1257 1258 /* deal with extra spacing here */ 1259 if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1260 { 1261 item_offset += extra_per_item; 1262 1263 if (position < extra_extra) 1264 item_offset++; 1265 } 1266 1267 i++; 1268 } 1269 1270 g_free (item_sizes); 1271 g_free (line_sizes); 1272 } 1273 else /* EGG_WRAP_ALLOCATE_FREE */ 1274 { 1275 /* Here we just fit as many children as we can allocate their natural size to 1276 * on each line and add the heights for each of them on each line */ 1277 GtkRequestedSize requested; 1278 GtkRequestedSize *sizes = NULL; 1279 GList *list = priv->children; 1280 gboolean first_line = TRUE; 1281 gint i, line_count = 0; 1282 gint line_offset, item_offset; 1283 gint extra_per_line = 0, extra_line_extra = 0; 1284 gint extra_pixels; 1285 GArray *array; 1286 1287 array = g_array_new (0, TRUE, sizeof (GtkRequestedSize)); 1288 1289 while (list != NULL) 1290 { 1291 GArray *line_array; 1292 AllocatedLine *line; 1293 1294 list = 1295 get_largest_size_for_free_line_in_opposing_orientation (box, priv->orientation, 1296 list, min_items, avail_size, 1297 &requested.minimum_size, 1298 &requested.natural_size, 1299 &extra_pixels, 1300 &line_array); 1301 1302 /* Its possible a line is made of completely invisible children */ 1303 if (requested.natural_size > 0) 1304 { 1305 if (first_line) 1306 first_line = FALSE; 1307 else 1308 avail_other_size -= line_spacing; 1309 1310 avail_other_size -= requested.minimum_size; 1311 1312 line = g_slice_new0 (AllocatedLine); 1313 line->requested = line_array; 1314 line->extra_pixels = extra_pixels; 1315 1316 requested.data = line; 1317 1318 g_array_append_val (array, requested); 1319 } 1320 } 1321 1322 /* Distribute space among lines naturally */ 1323 sizes = (GtkRequestedSize *)array->data; 1324 avail_other_size = gtk_distribute_natural_allocation (avail_other_size, array->len, sizes); 1325 1326 /* Calculate expand space per line */ 1327 if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1328 { 1329 extra_per_line = avail_other_size / MAX (array->len -1, 1); 1330 extra_line_extra = avail_other_size % MAX (array->len -1, 1); 1331 } 1332 else if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND) 1333 { 1334 extra_per_line = avail_other_size / array->len; 1335 extra_line_extra = avail_other_size % array->len; 1336 } 1337 1338 if (line_spreading == EGG_WRAP_BOX_SPREAD_END) 1339 line_offset = avail_other_size; 1340 else 1341 line_offset = 0; 1342 1343 for (line_count = 0; line_count < array->len; line_count++) 1344 { 1345 AllocatedLine *line = (AllocatedLine *)sizes[line_count].data; 1346 GArray *line_array = line->requested; 1347 GtkRequestedSize *line_sizes = (GtkRequestedSize *)line_array->data; 1348 gint line_size = sizes[line_count].minimum_size; 1349 gint extra_per_item = 0; 1350 gint extra_extra = 0; 1351 1352 /* Set line start offset */ 1353 item_offset = 0; 1354 1355 if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND) 1356 { 1357 line_size += extra_per_line; 1358 1359 if (line_count < extra_line_extra) 1360 line_size++; 1361 } 1362 1363 if (item_spreading == EGG_WRAP_BOX_SPREAD_END) 1364 item_offset += line->extra_pixels; 1365 else if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1366 { 1367 extra_per_item = line->extra_pixels / MAX (line_array->len -1, 1); 1368 extra_extra = line->extra_pixels % MAX (line_array->len -1, 1); 1369 } 1370 1371 for (i = 0; i < line_array->len; i++) 1372 { 1373 EggWrapBoxChild *child = line_sizes[i].data; 1374 gint item_size = line_sizes[i].minimum_size; 1375 1376 /* Do the actual allocation */ 1377 allocate_child (box, child, item_offset, line_offset, item_size, line_size); 1378 1379 /* Add extra space evenly between children */ 1380 if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1381 { 1382 item_offset += extra_per_item; 1383 if (i < extra_extra) 1384 item_offset++; 1385 } 1386 1387 /* Move item cursor along for the next allocation */ 1388 item_offset += item_spacing; 1389 item_offset += item_size; 1390 } 1391 1392 /* New line, increment offset and reset item cursor */ 1393 line_offset += line_spacing; 1394 line_offset += line_size; 1395 1396 if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN) 1397 { 1398 line_offset += extra_per_line; 1399 1400 if (line_count < extra_line_extra) 1401 line_offset++; 1402 } 1403 1404 /* Free the array for this line now its not needed anymore */ 1405 g_array_free (line_array, TRUE); 1406 g_slice_free (AllocatedLine, line); 1407 } 1408 1409 g_array_free (array, TRUE); 1410 } 1411 } 1412 1413 /***************************************************** 1414 * GtkContainerClass * 1415 *****************************************************/ 1416 static void 1417 egg_wrap_box_add (GtkContainer *container, 1418 GtkWidget *widget) 1419 { 1420 egg_wrap_box_insert_child (EGG_WRAP_BOX (container), widget, -1, 0); 1421 } 1422 1423 static gint 1424 find_child_in_list (EggWrapBoxChild *child_in_list, 1425 GtkWidget *search) 1426 { 1427 return (child_in_list->widget == search) ? 0 : -1; 1428 } 1429 1430 static void 1431 egg_wrap_box_remove (GtkContainer *container, 1432 GtkWidget *widget) 1433 { 1434 EggWrapBox *box = EGG_WRAP_BOX (container); 1435 EggWrapBoxPrivate *priv = box->priv; 1436 GList *list; 1437 1438 list = g_list_find_custom (priv->children, widget, 1439 (GCompareFunc)find_child_in_list); 1440 1441 if (list) 1442 { 1443 EggWrapBoxChild *child = list->data; 1444 gboolean was_visible = gtk_widget_get_visible (widget); 1445 1446 gtk_widget_unparent (widget); 1447 1448 g_slice_free (EggWrapBoxChild, child); 1449 priv->children = g_list_delete_link (priv->children, list); 1450 1451 if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container))) 1452 gtk_widget_queue_resize (GTK_WIDGET (container)); 1453 } 1454 } 1455 1456 static void 1457 egg_wrap_box_forall (GtkContainer *container, 1458 gboolean include_internals, 1459 GtkCallback callback, 1460 gpointer callback_data) 1461 { 1462 EggWrapBox *box = EGG_WRAP_BOX (container); 1463 EggWrapBoxPrivate *priv = box->priv; 1464 EggWrapBoxChild *child; 1465 GList *list; 1466 1467 list = priv->children; 1468 1469 while (list) 1470 { 1471 child = list->data; 1472 list = list->next; 1473 1474 (* callback) (child->widget, callback_data); 1475 } 1476 } 1477 1478 static GType 1479 egg_wrap_box_child_type (GtkContainer *container) 1480 { 1481 return GTK_TYPE_WIDGET; 1482 } 1483 1484 static void 1485 egg_wrap_box_set_child_property (GtkContainer *container, 1486 GtkWidget *widget, 1487 guint property_id, 1488 const GValue *value, 1489 GParamSpec *pspec) 1490 { 1491 EggWrapBox *box = EGG_WRAP_BOX (container); 1492 EggWrapBoxPrivate *priv = box->priv; 1493 EggWrapBoxChild *child; 1494 GList *list; 1495 1496 list = g_list_find_custom (priv->children, widget, 1497 (GCompareFunc)find_child_in_list); 1498 g_return_if_fail (list != NULL); 1499 1500 child = list->data; 1501 1502 switch (property_id) 1503 { 1504 case CHILD_PROP_PACKING: 1505 child->packing = g_value_get_flags (value); 1506 break; 1507 default: 1508 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); 1509 break; 1510 } 1511 1512 if (gtk_widget_get_visible (widget) && 1513 gtk_widget_get_visible (GTK_WIDGET (box))) 1514 gtk_widget_queue_resize (widget); 1515 } 1516 1517 static void 1518 egg_wrap_box_get_child_property (GtkContainer *container, 1519 GtkWidget *widget, 1520 guint property_id, 1521 GValue *value, 1522 GParamSpec *pspec) 1523 { 1524 EggWrapBox *box = EGG_WRAP_BOX (container); 1525 EggWrapBoxPrivate *priv = box->priv; 1526 EggWrapBoxChild *child; 1527 GList *list; 1528 1529 list = g_list_find_custom (priv->children, widget, 1530 (GCompareFunc)find_child_in_list); 1531 g_return_if_fail (list != NULL); 1532 1533 child = list->data; 1534 1535 switch (property_id) 1536 { 1537 case CHILD_PROP_PACKING: 1538 g_value_set_flags (value, child->packing); 1539 break; 1540 default: 1541 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); 1542 break; 1543 } 1544 } 1545 1546 /***************************************************** 1547 * size requests * 1548 *****************************************************/ 1549 1550 1551 static GtkSizeRequestMode 1552 egg_wrap_box_get_request_mode (GtkWidget *widget) 1553 { 1554 EggWrapBox *box = EGG_WRAP_BOX (widget); 1555 EggWrapBoxPrivate *priv = box->priv; 1556 1557 return (priv->orientation == GTK_ORIENTATION_HORIZONTAL) ? 1558 GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT; 1559 } 1560 1561 /* Gets the largest minimum and natural length of 1562 * 'line_length' consecutive items */ 1563 static void 1564 get_largest_line_length (EggWrapBox *box, 1565 GtkOrientation orientation, 1566 gint line_length, 1567 gint *min_size, 1568 gint *nat_size) 1569 { 1570 EggWrapBoxPrivate *priv = box->priv; 1571 GList *list, *l; 1572 gint max_min_size = 0; 1573 gint max_nat_size = 0; 1574 gint spacing; 1575 1576 if (orientation == GTK_ORIENTATION_HORIZONTAL) 1577 spacing = priv->horizontal_spacing; 1578 else 1579 spacing = priv->vertical_spacing; 1580 1581 /* Get the largest size of 'line_length' consecutive items in the list. 1582 */ 1583 for (list = priv->children; list; list = list->next) 1584 { 1585 gint line_min = 0; 1586 gint line_nat = 0; 1587 gint i; 1588 1589 for (l = list, i = 0; l && i < line_length; l = l->next) 1590 { 1591 EggWrapBoxChild *child = l->data; 1592 gint child_min, child_nat; 1593 1594 if (!gtk_widget_get_visible (child->widget)) 1595 continue; 1596 1597 if (orientation == GTK_ORIENTATION_HORIZONTAL) 1598 gtk_widget_get_preferred_width (child->widget, 1599 &child_min, &child_nat); 1600 else /* GTK_ORIENTATION_VERTICAL */ 1601 gtk_widget_get_preferred_height (child->widget, 1602 &child_min, &child_nat); 1603 1604 line_min += child_min; 1605 line_nat += child_nat; 1606 1607 i++; 1608 } 1609 1610 max_min_size = MAX (max_min_size, line_min); 1611 max_nat_size = MAX (max_nat_size, line_nat); 1612 } 1613 1614 max_min_size += (line_length - 1) * spacing; 1615 max_nat_size += (line_length - 1) * spacing; 1616 1617 if (min_size) 1618 *min_size = max_min_size; 1619 1620 if (nat_size) 1621 *nat_size = max_nat_size; 1622 } 1623 1624 /* Gets the largest minimum and natural length of 1625 * 'line_length' consecutive items when aligned into rows/columns */ 1626 static void 1627 get_largest_aligned_line_length (EggWrapBox *box, 1628 GtkOrientation orientation, 1629 gint line_length, 1630 gint *min_size, 1631 gint *nat_size) 1632 { 1633 EggWrapBoxPrivate *priv = box->priv; 1634 GList *list; 1635 gint max_min_size = 0; 1636 gint max_nat_size = 0; 1637 gint spacing, i; 1638 GtkRequestedSize *aligned_item_sizes; 1639 1640 if (orientation == GTK_ORIENTATION_HORIZONTAL) 1641 spacing = priv->horizontal_spacing; 1642 else 1643 spacing = priv->vertical_spacing; 1644 1645 aligned_item_sizes = g_new0 (GtkRequestedSize, line_length); 1646 1647 /* Get the largest sizes of each index in the line. 1648 */ 1649 for (list = priv->children, i = 0; list; list = list->next) 1650 { 1651 EggWrapBoxChild *child = list->data; 1652 gint child_min, child_nat; 1653 1654 if (!gtk_widget_get_visible (child->widget)) 1655 continue; 1656 1657 if (orientation == GTK_ORIENTATION_HORIZONTAL) 1658 gtk_widget_get_preferred_width (child->widget, 1659 &child_min, &child_nat); 1660 else /* GTK_ORIENTATION_VERTICAL */ 1661 gtk_widget_get_preferred_height (child->widget, 1662 &child_min, &child_nat); 1663 1664 aligned_item_sizes[i % line_length].minimum_size = 1665 MAX (aligned_item_sizes[i % line_length].minimum_size, child_min); 1666 1667 aligned_item_sizes[i % line_length].natural_size = 1668 MAX (aligned_item_sizes[i % line_length].natural_size, child_nat); 1669 1670 i++; 1671 } 1672 1673 /* Add up the largest indexes */ 1674 for (i = 0; i < line_length; i++) 1675 { 1676 max_min_size += aligned_item_sizes[i].minimum_size; 1677 max_nat_size += aligned_item_sizes[i].natural_size; 1678 } 1679 1680 g_free (aligned_item_sizes); 1681 1682 max_min_size += (line_length - 1) * spacing; 1683 max_nat_size += (line_length - 1) * spacing; 1684 1685 if (min_size) 1686 *min_size = max_min_size; 1687 1688 if (nat_size) 1689 *nat_size = max_nat_size; 1690 } 1691 1692 1693 static void 1694 egg_wrap_box_get_preferred_width (GtkWidget *widget, 1695 gint *minimum_size, 1696 gint *natural_size) 1697 { 1698 EggWrapBox *box = EGG_WRAP_BOX (widget); 1699 EggWrapBoxPrivate *priv = box->priv; 1700 gint min_item_width, nat_item_width; 1701 gint min_items, nat_items; 1702 gint min_width, nat_width; 1703 1704 min_items = MAX (1, priv->minimum_line_children); 1705 nat_items = MAX (min_items, priv->natural_line_children); 1706 1707 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) 1708 { 1709 min_width = nat_width = 0; 1710 1711 if (priv->mode == EGG_WRAP_ALLOCATE_FREE || 1712 priv->mode == EGG_WRAP_ALLOCATE_ALIGNED) 1713 { 1714 /* In FREE and ALIGNED modes; horizontally oriented boxes 1715 * need enough width for the widest row */ 1716 if (min_items == 1) 1717 { 1718 get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL, 1719 &min_item_width, &nat_item_width); 1720 1721 min_width += min_item_width; 1722 nat_width += nat_item_width; 1723 } 1724 else if (priv->mode == EGG_WRAP_ALLOCATE_FREE) 1725 { 1726 gint min_line_length, nat_line_length; 1727 1728 get_largest_line_length (box, GTK_ORIENTATION_HORIZONTAL, min_items, 1729 &min_line_length, &nat_line_length); 1730 1731 if (nat_items > min_items) 1732 get_largest_line_length (box, GTK_ORIENTATION_HORIZONTAL, nat_items, 1733 NULL, &nat_line_length); 1734 1735 min_width += min_line_length; 1736 nat_width += nat_line_length; 1737 } 1738 else /* EGG_WRAP_MODE_ALIGNED */ 1739 { 1740 gint min_line_length, nat_line_length; 1741 1742 get_largest_aligned_line_length (box, GTK_ORIENTATION_HORIZONTAL, min_items, 1743 &min_line_length, &nat_line_length); 1744 1745 if (nat_items > min_items) 1746 get_largest_aligned_line_length (box, GTK_ORIENTATION_HORIZONTAL, nat_items, 1747 NULL, &nat_line_length); 1748 1749 min_width += min_line_length; 1750 nat_width += nat_line_length; 1751 } 1752 } 1753 else /* In HOMOGENEOUS mode; horizontally oriented boxs 1754 * give the same width to all children */ 1755 { 1756 get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL, 1757 &min_item_width, &nat_item_width); 1758 1759 min_width += min_item_width * min_items; 1760 min_width += (min_items -1) * priv->horizontal_spacing; 1761 1762 nat_width += nat_item_width * nat_items; 1763 nat_width += (nat_items -1) * priv->horizontal_spacing; 1764 } 1765 } 1766 else /* GTK_ORIENTATION_VERTICAL */ 1767 { 1768 /* Return the width for the minimum height */ 1769 gint min_height; 1770 1771 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL); 1772 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, min_height, 1773 &min_width, &nat_width); 1774 1775 } 1776 1777 if (minimum_size) 1778 *minimum_size = min_width; 1779 1780 if (natural_size) 1781 *natural_size = nat_width; 1782 } 1783 1784 static void 1785 egg_wrap_box_get_preferred_height (GtkWidget *widget, 1786 gint *minimum_size, 1787 gint *natural_size) 1788 { 1789 EggWrapBox *box = EGG_WRAP_BOX (widget); 1790 EggWrapBoxPrivate *priv = box->priv; 1791 gint min_item_height, nat_item_height; 1792 gint min_items, nat_items; 1793 gint min_height, nat_height; 1794 1795 min_items = MAX (1, priv->minimum_line_children); 1796 nat_items = MAX (min_items, priv->natural_line_children); 1797 1798 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) 1799 { 1800 /* Return the height for the minimum width */ 1801 gint min_width; 1802 1803 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL); 1804 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, 1805 &min_height, &nat_height); 1806 } 1807 else /* GTK_ORIENTATION_VERTICAL */ 1808 { 1809 min_height = nat_height = 0; 1810 1811 if (priv->mode == EGG_WRAP_ALLOCATE_FREE || 1812 priv->mode == EGG_WRAP_ALLOCATE_ALIGNED) 1813 { 1814 /* In FREE and ALIGNED modes; vertically oriented boxes 1815 * need enough height for the tallest column */ 1816 if (min_items == 1) 1817 { 1818 get_average_item_size (box, GTK_ORIENTATION_VERTICAL, 1819 &min_item_height, &nat_item_height); 1820 1821 min_height += min_item_height; 1822 nat_height += nat_item_height; 1823 } 1824 else if (priv->mode == EGG_WRAP_ALLOCATE_FREE) 1825 { 1826 gint min_line_length, nat_line_length; 1827 1828 get_largest_line_length (box, GTK_ORIENTATION_VERTICAL, min_items, 1829 &min_line_length, &nat_line_length); 1830 1831 if (nat_items > min_items) 1832 get_largest_line_length (box, GTK_ORIENTATION_VERTICAL, nat_items, 1833 NULL, &nat_line_length); 1834 1835 min_height += min_line_length; 1836 nat_height += nat_line_length; 1837 } 1838 else /* EGG_WRAP_ALLOCATE_ALIGNED */ 1839 { 1840 gint min_line_length, nat_line_length; 1841 1842 get_largest_aligned_line_length (box, GTK_ORIENTATION_VERTICAL, min_items, 1843 &min_line_length, &nat_line_length); 1844 1845 if (nat_items > min_items) 1846 get_largest_aligned_line_length (box, GTK_ORIENTATION_VERTICAL, nat_items, 1847 NULL, &nat_line_length); 1848 1849 min_height += min_line_length; 1850 nat_height += nat_line_length; 1851 } 1852 1853 } 1854 else /* In HOMOGENEOUS mode; vertically oriented boxs 1855 * give the same height to all children */ 1856 { 1857 get_average_item_size (box, GTK_ORIENTATION_VERTICAL, 1858 &min_item_height, &nat_item_height); 1859 1860 min_height += min_item_height * min_items; 1861 min_height += (min_items -1) * priv->vertical_spacing; 1862 1863 nat_height += nat_item_height * nat_items; 1864 nat_height += (nat_items -1) * priv->vertical_spacing; 1865 } 1866 } 1867 1868 if (minimum_size) 1869 *minimum_size = min_height; 1870 1871 if (natural_size) 1872 *natural_size = nat_height; 1873 } 1874 1875 static void 1876 egg_wrap_box_get_preferred_height_for_width (GtkWidget *widget, 1877 gint width, 1878 gint *minimum_height, 1879 gint *natural_height) 1880 { 1881 EggWrapBox *box = EGG_WRAP_BOX (widget); 1882 EggWrapBoxPrivate *priv = box->priv; 1883 gint min_item_width, nat_item_width; 1884 gint min_items; 1885 gint min_height, nat_height; 1886 gint avail_size, n_children; 1887 1888 min_items = MAX (1, priv->minimum_line_children); 1889 1890 min_height = 0; 1891 nat_height = 0; 1892 1893 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) 1894 { 1895 gint min_width; 1896 1897 n_children = get_visible_children (box); 1898 1899 /* Make sure its no smaller than the minimum */ 1900 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL); 1901 1902 avail_size = MAX (width, min_width); 1903 1904 if (priv->mode == EGG_WRAP_ALLOCATE_ALIGNED || 1905 priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 1906 { 1907 gint line_length; 1908 gint item_size, extra_pixels; 1909 1910 get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL, &min_item_width, &nat_item_width); 1911 1912 /* By default wrap at the natural item width */ 1913 line_length = avail_size / (nat_item_width + priv->horizontal_spacing); 1914 1915 /* After the above aproximation, check if we cant fit one more on the line */ 1916 if (line_length * priv->horizontal_spacing + (line_length + 1) * nat_item_width <= avail_size) 1917 line_length++; 1918 1919 /* Its possible we were allocated just less than the natural width of the 1920 * minimum item wrap length */ 1921 line_length = MAX (min_items, line_length); 1922 1923 /* Now we need the real item allocation size */ 1924 item_size = (avail_size - (line_length - 1) * priv->horizontal_spacing) / line_length; 1925 1926 /* Cut out the expand space if we're not distributing any */ 1927 if (priv->horizontal_spreading != EGG_WRAP_BOX_SPREAD_EXPAND) 1928 { 1929 item_size = MIN (item_size, nat_item_width); 1930 extra_pixels = 0;
Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1931 } 1932 else 1933 /* Collect the extra pixels for expand children */ 1934 extra_pixels = (avail_size - (line_length - 1) * priv->horizontal_spacing) % line_length;
Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

1935 1936 if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 1937 { 1938 gint min_item_height, nat_item_height; 1939 gint lines; 1940 1941 /* Here we just use the largest height-for-width and 1942 * add up the size accordingly */ 1943 get_largest_size_for_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL, item_size, 1944 &min_item_height, &nat_item_height); 1945 1946 /* Round up how many lines we need to allocate for */ 1947 lines = n_children / line_length; 1948 if ((n_children % line_length) > 0) 1949 lines++; 1950 1951 min_height = min_item_height * lines; 1952 nat_height = nat_item_height * lines; 1953 1954 min_height += (lines - 1) * priv->vertical_spacing; 1955 nat_height += (lines - 1) * priv->vertical_spacing; 1956 } 1957 else /* EGG_WRAP_ALLOCATE_ALIGNED */ 1958 { 1959 GList *list = priv->children; 1960 gint min_line_height, nat_line_height, i; 1961 gboolean first_line = TRUE; 1962 GtkRequestedSize *item_sizes; 1963 1964 /* First get the size each set of items take to span the line 1965 * when aligning the items above and below after wrapping. 1966 */ 1967 item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size, 1968 priv->horizontal_spacing, &line_length, n_children); 1969 1970 1971 /* Get the available remaining size */ 1972 avail_size -= (line_length - 1) * priv->horizontal_spacing; 1973 for (i = 0; i < line_length; i++) 1974 avail_size -= item_sizes[i].minimum_size; 1975 1976 extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes); 1977 1978 while (list != NULL) 1979 { 1980 list = 1981 get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL, 1982 list, line_length, 1983 item_sizes, extra_pixels, 1984 &min_line_height, &nat_line_height); 1985 1986 /* Its possible the line only had invisible widgets */ 1987 if (nat_line_height > 0) 1988 { 1989 if (first_line) 1990 first_line = FALSE; 1991 else 1992 { 1993 min_height += priv->vertical_spacing; 1994 nat_height += priv->vertical_spacing; 1995 } 1996 1997 min_height += min_line_height; 1998 nat_height += nat_line_height; 1999 } 2000 } 2001 2002 g_free (item_sizes); 2003 } 2004 } 2005 else /* EGG_WRAP_ALLOCATE_FREE */ 2006 { 2007 /* Here we just fit as many children as we can allocate their natural size to 2008 * on each line and add the heights for each of them on each line */ 2009 GList *list = priv->children; 2010 gint min_line_height = 0, nat_line_height = 0; 2011 gboolean first_line = TRUE; 2012 2013 while (list != NULL) 2014 { 2015 list = 2016 get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL, 2017 list, min_items, avail_size, 2018 &min_line_height, &nat_line_height, 2019 NULL, NULL); 2020 2021 /* Its possible the last line only had invisible widgets */ 2022 if (nat_line_height > 0) 2023 { 2024 if (first_line) 2025 first_line = FALSE; 2026 else 2027 { 2028 min_height += priv->vertical_spacing; 2029 nat_height += priv->vertical_spacing; 2030 } 2031 2032 min_height += min_line_height; 2033 nat_height += nat_line_height; 2034 } 2035 } 2036 } 2037 } 2038 else /* GTK_ORIENTATION_VERTICAL */ 2039 { 2040 /* Return the minimum height */ 2041 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, &nat_height); 2042 } 2043 2044 if (minimum_height) 2045 *minimum_height = min_height; 2046 2047 if (natural_height) 2048 *natural_height = nat_height; 2049 } 2050 2051 static void 2052 egg_wrap_box_get_preferred_width_for_height (GtkWidget *widget, 2053 gint height, 2054 gint *minimum_width, 2055 gint *natural_width) 2056 { 2057 EggWrapBox *box = EGG_WRAP_BOX (widget); 2058 EggWrapBoxPrivate *priv = box->priv; 2059 gint min_item_height, nat_item_height; 2060 gint min_items; 2061 gint min_width, nat_width; 2062 gint avail_size, n_children; 2063 2064 min_items = MAX (1, priv->minimum_line_children); 2065 2066 min_width = 0; 2067 nat_width = 0; 2068 2069 if (priv->orientation == GTK_ORIENTATION_HORIZONTAL) 2070 { 2071 /* Return the minimum width */ 2072 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, &nat_width); 2073 } 2074 else /* GTK_ORIENTATION_VERTICAL */ 2075 { 2076 gint min_height; 2077 2078 n_children = get_visible_children (box); 2079 2080 /* Make sure its no smaller than the minimum */ 2081 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL); 2082 2083 avail_size = MAX (height, min_height); 2084 2085 if (priv->mode == EGG_WRAP_ALLOCATE_ALIGNED || 2086 priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 2087 { 2088 gint line_length; 2089 gint item_size, extra_pixels; 2090 2091 get_average_item_size (box, GTK_ORIENTATION_VERTICAL, &min_item_height, &nat_item_height); 2092 2093 /* By default wrap at the natural item width */ 2094 line_length = avail_size / (nat_item_height + priv->vertical_spacing); 2095 2096 /* After the above aproximation, check if we cant fit one more on the line */ 2097 if (line_length * priv->vertical_spacing + (line_length + 1) * nat_item_height <= avail_size) 2098 line_length++; 2099 2100 /* Its possible we were allocated just less than the natural width of the 2101 * minimum item wrap length */ 2102 line_length = MAX (min_items, line_length); 2103 2104 /* Now we need the real item allocation size */ 2105 item_size = (avail_size - (line_length - 1) * priv->vertical_spacing) / line_length; 2106 2107 /* Cut out the expand space if we're not distributing any */ 2108 if (priv->vertical_spreading != EGG_WRAP_BOX_SPREAD_EXPAND) 2109 { 2110 item_size = MIN (item_size, nat_item_height); 2111 extra_pixels = 0;
Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2112 } 2113 else 2114 /* Collect the extra pixels for expand children */ 2115 extra_pixels = (avail_size - (line_length - 1) * priv->vertical_spacing) % line_length;
Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

Value stored to 'extra_pixels' is never read
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

2116 2117 if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS) 2118 { 2119 gint min_item_width, nat_item_width; 2120 gint lines; 2121 2122 /* Here we just use the largest height-for-width and 2123 * add up the size accordingly */ 2124 get_largest_size_for_opposing_orientation (box, GTK_ORIENTATION_VERTICAL, item_size, 2125 &min_item_width, &nat_item_width); 2126 2127 /* Round up how many lines we need to allocate for */ 2128 n_children = get_visible_children (box); 2129 lines = n_children / line_length; 2130 if ((n_children % line_length) > 0) 2131 lines++; 2132 2133 min_width = min_item_width * lines; 2134 nat_width = nat_item_width * lines; 2135 2136 min_width += (lines - 1) * priv->horizontal_spacing; 2137 nat_width += (lines - 1) * priv->horizontal_spacing; 2138 } 2139 else /* EGG_WRAP_ALLOCATE_ALIGNED */ 2140 { 2141 GList *list = priv->children; 2142 gint min_line_width, nat_line_width, i; 2143 gboolean first_line = TRUE; 2144 GtkRequestedSize *item_sizes; 2145 2146 /* First get the size each set of items take to span the line 2147 * when aligning the items above and below after wrapping. 2148 */ 2149 item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size, 2150 priv->vertical_spacing, &line_length, n_children); 2151 2152 /* Get the available remaining size */ 2153 avail_size -= (line_length - 1) * priv->horizontal_spacing; 2154 for (i = 0; i < line_length; i++) 2155 avail_size -= item_sizes[i].minimum_size; 2156 2157 extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes); 2158 2159 while (list != NULL) 2160 { 2161 list = 2162 get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL, 2163 list, line_length, 2164 item_sizes, extra_pixels, 2165 &min_line_width, &nat_line_width); 2166 2167 /* Its possible the last line only had invisible widgets */ 2168 if (nat_line_width > 0) 2169 { 2170 if (first_line) 2171 first_line = FALSE; 2172 else 2173 { 2174 min_width += priv->horizontal_spacing; 2175 nat_width += priv->horizontal_spacing; 2176 } 2177 2178 min_width += min_line_width; 2179 nat_width += nat_line_width; 2180 } 2181 } 2182 g_free (item_sizes); 2183 } 2184 } 2185 else /* EGG_WRAP_ALLOCATE_FREE */ 2186 { 2187 /* Here we just fit as many children as we can allocate their natural size to 2188 * on each line and add the heights for each of them on each line */ 2189 GList *list = priv->children; 2190 gint min_line_width = 0, nat_line_width = 0; 2191 gboolean first_line = TRUE; 2192 2193 while (list != NULL) 2194 { 2195 list = 2196 get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL, 2197 list, min_items, avail_size, 2198 &min_line_width, &nat_line_width, 2199 NULL, NULL); 2200 2201 /* Its possible the last line only had invisible widgets */ 2202 if (nat_line_width > 0) 2203 { 2204 if (first_line) 2205 first_line = FALSE; 2206 else 2207 { 2208 min_width += priv->horizontal_spacing; 2209 nat_width += priv->horizontal_spacing; 2210 } 2211 2212 min_width += min_line_width; 2213 nat_width += nat_line_width; 2214 } 2215 } 2216 } 2217 } 2218 2219 if (minimum_width) 2220 *minimum_width = min_width; 2221 2222 if (natural_width) 2223 *natural_width = nat_width; 2224 } 2225 2226 /***************************************************** 2227 * API * 2228 *****************************************************/ 2229 2230 /** 2231 * egg_wrap_box_new: 2232 * @mode: The #EggWrapAllocationMode to use 2233 * @horizontal_spreading: The horizontal #EggWrapBoxSpreading policy to use 2234 * @vertical_spreading: The vertical #EggWrapBoxSpreading policy to use 2235 * @horizontal_spacing: The horizontal spacing to add between children 2236 * @vertical_spacing: The vertical spacing to add between children 2237 * 2238 * Creates an #EggWrapBox. 2239 * 2240 * Returns: A new #EggWrapBox container 2241 */ 2242 GtkWidget * 2243 egg_wrap_box_new (EggWrapAllocationMode mode, 2244 EggWrapBoxSpreading horizontal_spreading, 2245 EggWrapBoxSpreading vertical_spreading, 2246 guint horizontal_spacing, 2247 guint vertical_spacing) 2248 { 2249 return (GtkWidget *)g_object_new (EGG_TYPE_WRAP_BOX, 2250 "allocation-mode", mode, 2251 "horizontal-spreading", horizontal_spreading, 2252 "vertical-spreading", vertical_spreading, 2253 "vertical-spacing", vertical_spacing, 2254 "horizontal-spacing", horizontal_spacing, 2255 NULL); 2256 } 2257 2258 /** 2259 * egg_wrap_box_set_allocation_mode: 2260 * @box: An #EggWrapBox 2261 * @mode: The #EggWrapAllocationMode to use. 2262 * 2263 * Sets the allocation mode for @box's children. 2264 */ 2265 void 2266 egg_wrap_box_set_allocation_mode (EggWrapBox *box, 2267 EggWrapAllocationMode mode) 2268 { 2269 EggWrapBoxPrivate *priv; 2270 2271 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2272 2273 priv = box->priv; 2274 2275 if (priv->mode != mode) 2276 { 2277 priv->mode = mode; 2278 2279 gtk_widget_queue_resize (GTK_WIDGET (box)); 2280 2281 g_object_notify (G_OBJECT (box), "allocation-mode"); 2282 } 2283 } 2284 2285 /** 2286 * egg_wrap_box_get_allocation_mode: 2287 * @box: An #EggWrapBox 2288 * 2289 * Gets the allocation mode. 2290 * 2291 * Returns: The #EggWrapAllocationMode for @box. 2292 */ 2293 EggWrapAllocationMode 2294 egg_wrap_box_get_allocation_mode (EggWrapBox *box) 2295 { 2296 g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE); 2297 2298 return box->priv->mode; 2299 } 2300 2301 2302 /** 2303 * egg_wrap_box_set_horizontal_spreading: 2304 * @box: An #EggWrapBox 2305 * @spreading: The #EggWrapBoxSpreading to use. 2306 * 2307 * Sets the horizontal spreading mode for @box's children. 2308 */ 2309 void 2310 egg_wrap_box_set_horizontal_spreading (EggWrapBox *box, 2311 EggWrapBoxSpreading spreading) 2312 { 2313 EggWrapBoxPrivate *priv; 2314 2315 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2316 2317 priv = box->priv; 2318 2319 if (priv->horizontal_spreading != spreading) 2320 { 2321 priv->horizontal_spreading = spreading; 2322 2323 gtk_widget_queue_resize (GTK_WIDGET (box)); 2324 2325 g_object_notify (G_OBJECT (box), "horizontal-spreading"); 2326 } 2327 } 2328 2329 /** 2330 * egg_wrap_box_get_horizontal_spreading: 2331 * @box: An #EggWrapBox 2332 * 2333 * Gets the horizontal spreading mode. 2334 * 2335 * Returns: The horizontal #EggWrapBoxSpreading for @box. 2336 */ 2337 EggWrapBoxSpreading 2338 egg_wrap_box_get_horizontal_spreading (EggWrapBox *box) 2339 { 2340 g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE); 2341 2342 return box->priv->horizontal_spreading; 2343 } 2344 2345 2346 /** 2347 * egg_wrap_box_set_vertical_spreading: 2348 * @box: An #EggWrapBox 2349 * @spreading: The #EggWrapBoxSpreading to use. 2350 * 2351 * Sets the vertical spreading mode for @box's children. 2352 */ 2353 void 2354 egg_wrap_box_set_vertical_spreading (EggWrapBox *box, 2355 EggWrapBoxSpreading spreading) 2356 { 2357 EggWrapBoxPrivate *priv; 2358 2359 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2360 2361 priv = box->priv; 2362 2363 if (priv->vertical_spreading != spreading) 2364 { 2365 priv->vertical_spreading = spreading; 2366 2367 gtk_widget_queue_resize (GTK_WIDGET (box)); 2368 2369 g_object_notify (G_OBJECT (box), "vertical-spreading"); 2370 } 2371 } 2372 2373 /** 2374 * egg_wrap_box_get_vertical_spreading: 2375 * @box: An #EggWrapBox 2376 * 2377 * Gets the vertical spreading mode. 2378 * 2379 * Returns: The vertical #EggWrapBoxSpreading for @box. 2380 */ 2381 EggWrapBoxSpreading 2382 egg_wrap_box_get_vertical_spreading (EggWrapBox *box) 2383 { 2384 g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE); 2385 2386 return box->priv->vertical_spreading; 2387 } 2388 2389 2390 /** 2391 * egg_wrap_box_set_vertical_spacing: 2392 * @box: An #EggWrapBox 2393 * @spacing: The spacing to use. 2394 * 2395 * Sets the vertical space to add between children. 2396 */ 2397 void 2398 egg_wrap_box_set_vertical_spacing (EggWrapBox *box, 2399 guint spacing) 2400 { 2401 EggWrapBoxPrivate *priv; 2402 2403 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2404 2405 priv = box->priv; 2406 2407 if (priv->vertical_spacing != spacing) 2408 { 2409 priv->vertical_spacing = spacing; 2410 2411 gtk_widget_queue_resize (GTK_WIDGET (box)); 2412 2413 g_object_notify (G_OBJECT (box), "vertical-spacing"); 2414 } 2415 } 2416 2417 /** 2418 * egg_wrap_box_get_vertical_spacing: 2419 * @box: An #EggWrapBox 2420 * 2421 * Gets the vertical spacing. 2422 * 2423 * Returns: The vertical spacing. 2424 */ 2425 guint 2426 egg_wrap_box_get_vertical_spacing (EggWrapBox *box) 2427 { 2428 g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE); 2429 2430 return box->priv->vertical_spacing; 2431 } 2432 2433 /** 2434 * egg_wrap_box_set_horizontal_spacing: 2435 * @box: An #EggWrapBox 2436 * @spacing: The spacing to use. 2437 * 2438 * Sets the horizontal space to add between children. 2439 */ 2440 void 2441 egg_wrap_box_set_horizontal_spacing (EggWrapBox *box, 2442 guint spacing) 2443 { 2444 EggWrapBoxPrivate *priv; 2445 2446 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2447 2448 priv = box->priv; 2449 2450 if (priv->horizontal_spacing != spacing) 2451 { 2452 priv->horizontal_spacing = spacing; 2453 2454 gtk_widget_queue_resize (GTK_WIDGET (box)); 2455 2456 g_object_notify (G_OBJECT (box), "horizontal-spacing"); 2457 } 2458 } 2459 2460 /** 2461 * egg_wrap_box_get_horizontal_spacing: 2462 * @box: An #EggWrapBox 2463 * 2464 * Gets the horizontal spacing. 2465 * 2466 * Returns: The horizontal spacing. 2467 */ 2468 guint 2469 egg_wrap_box_get_horizontal_spacing (EggWrapBox *box) 2470 { 2471 g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE); 2472 2473 return box->priv->horizontal_spacing; 2474 } 2475 2476 /** 2477 * egg_wrap_box_set_minimum_line_children: 2478 * @box: An #EggWrapBox 2479 * @n_children: The minimum amount of children per line. 2480 * 2481 * Sets the minimum amount of children to line up 2482 * in @box's orientation before wrapping. 2483 */ 2484 void 2485 egg_wrap_box_set_minimum_line_children (EggWrapBox *box, 2486 guint n_children) 2487 { 2488 EggWrapBoxPrivate *priv; 2489 2490 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2491 2492 priv = box->priv; 2493 2494 if (priv->minimum_line_children != n_children) 2495 { 2496 priv->minimum_line_children = n_children; 2497 2498 gtk_widget_queue_resize (GTK_WIDGET (box)); 2499 2500 g_object_notify (G_OBJECT (box), "minimum-line-children"); 2501 } 2502 } 2503 2504 /** 2505 * egg_wrap_box_get_minimum_line_children: 2506 * @box: An #EggWrapBox 2507 * 2508 * Gets the minimum amount of children per line. 2509 * 2510 * Returns: The minimum amount of children per line. 2511 */ 2512 guint 2513 egg_wrap_box_get_minimum_line_children (EggWrapBox *box) 2514 { 2515 g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE); 2516 2517 return box->priv->minimum_line_children; 2518 } 2519 2520 /** 2521 * egg_wrap_box_set_natural_line_children: 2522 * @box: An #EggWrapBox 2523 * @n_children: The natural amount of children per line. 2524 * 2525 * Sets the natural length of items to request and 2526 * allocate space for in @box's orientation. 2527 * 2528 * Setting the natural amount of children per line 2529 * limits the overall natural size request to be no more 2530 * than @n_children items long in the given orientation. 2531 */ 2532 void 2533 egg_wrap_box_set_natural_line_children (EggWrapBox *box, 2534 guint n_children) 2535 { 2536 EggWrapBoxPrivate *priv; 2537 2538 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2539 2540 priv = box->priv; 2541 2542 if (priv->natural_line_children != n_children) 2543 { 2544 priv->natural_line_children = n_children; 2545 2546 gtk_widget_queue_resize (GTK_WIDGET (box)); 2547 2548 g_object_notify (G_OBJECT (box), "natural-line-children"); 2549 } 2550 } 2551 2552 /** 2553 * egg_wrap_box_get_natural_line_children: 2554 * @box: An #EggWrapBox 2555 * 2556 * Gets the natural amount of children per line. 2557 * 2558 * Returns: The natural amount of children per line. 2559 */ 2560 guint 2561 egg_wrap_box_get_natural_line_children (EggWrapBox *box) 2562 { 2563 g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE); 2564 2565 return box->priv->natural_line_children; 2566 } 2567 2568 2569 /** 2570 * egg_wrap_box_insert_child: 2571 * @box: And #EggWrapBox 2572 * @widget: the child #GtkWidget to add 2573 * @index: the position in the child list to insert, specify -1 to append to the list. 2574 * @packing: The #EggWrapBoxPacking options to use. 2575 * 2576 * Adds a child to an #EggWrapBox with its packing options set 2577 * 2578 */ 2579 void 2580 egg_wrap_box_insert_child (EggWrapBox *box, 2581 GtkWidget *widget, 2582 gint index, 2583 EggWrapBoxPacking packing) 2584 { 2585 EggWrapBoxPrivate *priv; 2586 EggWrapBoxChild *child; 2587 GList *list; 2588 2589 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2590 g_return_if_fail (GTK_IS_WIDGET (widget)); 2591 2592 priv = box->priv; 2593 2594 list = g_list_find_custom (priv->children, widget, 2595 (GCompareFunc)find_child_in_list); 2596 g_return_if_fail (list == NULL); 2597 2598 child = g_slice_new0 (EggWrapBoxChild); 2599 child->widget = widget; 2600 child->packing = packing; 2601 2602 priv->children = g_list_insert (priv->children, child, index); 2603 2604 gtk_widget_set_parent (widget, GTK_WIDGET (box)); 2605 } 2606 2607 /** 2608 * egg_wrap_box_reorder_child: 2609 * @box: An #EggWrapBox 2610 * @widget: The child to reorder 2611 * @index: The new child position 2612 * 2613 * Reorders the child @widget in @box's list of children. 2614 */ 2615 void 2616 egg_wrap_box_reorder_child (EggWrapBox *box, 2617 GtkWidget *widget, 2618 guint index) 2619 { 2620 EggWrapBoxPrivate *priv; 2621 EggWrapBoxChild *child; 2622 GList *list; 2623 2624 g_return_if_fail (EGG_IS_WRAP_BOX (box)); 2625 g_return_if_fail (GTK_IS_WIDGET (widget)); 2626 2627 priv = box->priv; 2628 2629 list = g_list_find_custom (priv->children, widget, 2630 (GCompareFunc)find_child_in_list); 2631 g_return_if_fail (list != NULL); 2632 2633 if (g_list_position (priv->children, list) != index) 2634 { 2635 child = list->data; 2636 priv->children = g_list_delete_link (priv->children, list); 2637 priv->children = g_list_insert (priv->children, child, index); 2638 2639 gtk_widget_queue_resize (GTK_WIDGET (box)); 2640 } 2641 }