home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / gtkhwrapbox.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  17.7 KB  |  657 lines

  1. /* GTK - The GIMP Toolkit
  2.  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  3.  *
  4.  * GtkHWrapBox: Horizontal wrapping box widget
  5.  * Copyright (C) 1999 Tim Janik
  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. #include "gtkhwrapbox.h"
  23. #include <math.h>
  24.  
  25.  
  26. /* --- prototypes --- */
  27. static void    gtk_hwrap_box_class_init    (GtkHWrapBoxClass   *klass);
  28. static void    gtk_hwrap_box_init          (GtkHWrapBox        *hwbox);
  29. static void    gtk_hwrap_box_size_request  (GtkWidget          *widget,
  30.                         GtkRequisition     *requisition);
  31. static void    gtk_hwrap_box_size_allocate (GtkWidget          *widget,
  32.                         GtkAllocation      *allocation);
  33. static GSList* reverse_list_row_children   (GtkWrapBox         *wbox,
  34.                         GtkWrapBoxChild   **child_p,
  35.                         GtkAllocation      *area,
  36.                         guint              *max_height,
  37.                         gboolean           *can_vexpand);
  38.  
  39.  
  40. /* --- variables --- */
  41. static gpointer parent_class = NULL;
  42.  
  43.  
  44. /* --- functions --- */
  45. GtkType
  46. gtk_hwrap_box_get_type (void)
  47. {
  48.   static GtkType hwrap_box_type = 0;
  49.   
  50.   if (!hwrap_box_type)
  51.     {
  52.       static const GtkTypeInfo hwrap_box_info =
  53.       {
  54.     "GtkHWrapBox",
  55.     sizeof (GtkHWrapBox),
  56.     sizeof (GtkHWrapBoxClass),
  57.     (GtkClassInitFunc) gtk_hwrap_box_class_init,
  58.     (GtkObjectInitFunc) gtk_hwrap_box_init,
  59.         /* reserved_1 */ NULL,
  60.     /* reserved_2 */ NULL,
  61.     (GtkClassInitFunc) NULL,
  62.       };
  63.       
  64.       hwrap_box_type = gtk_type_unique (GTK_TYPE_WRAP_BOX, &hwrap_box_info);
  65.     }
  66.   
  67.   return hwrap_box_type;
  68. }
  69.  
  70. static void
  71. gtk_hwrap_box_class_init (GtkHWrapBoxClass *class)
  72. {
  73.   GtkObjectClass *object_class;
  74.   GtkWidgetClass *widget_class;
  75.   GtkContainerClass *container_class;
  76.   GtkWrapBoxClass *wrap_box_class;
  77.   
  78.   object_class = GTK_OBJECT_CLASS (class);
  79.   widget_class = GTK_WIDGET_CLASS (class);
  80.   container_class = GTK_CONTAINER_CLASS (class);
  81.   wrap_box_class = GTK_WRAP_BOX_CLASS (class);
  82.   
  83.   parent_class = gtk_type_class (GTK_TYPE_WRAP_BOX);
  84.   
  85.   widget_class->size_request = gtk_hwrap_box_size_request;
  86.   widget_class->size_allocate = gtk_hwrap_box_size_allocate;
  87.  
  88.   wrap_box_class->rlist_line_children = reverse_list_row_children;
  89. }
  90.  
  91. static void
  92. gtk_hwrap_box_init (GtkHWrapBox *hwbox)
  93. {
  94.   hwbox->max_child_width = 0;
  95.   hwbox->max_child_height = 0;
  96. }
  97.  
  98. GtkWidget*
  99. gtk_hwrap_box_new (gboolean homogeneous)
  100. {
  101.   GtkHWrapBox *hwbox;
  102.  
  103.   hwbox = GTK_HWRAP_BOX (gtk_widget_new (GTK_TYPE_HWRAP_BOX, NULL));
  104.  
  105.   GTK_WRAP_BOX (hwbox)->homogeneous = homogeneous ? TRUE : FALSE;
  106.  
  107.   return GTK_WIDGET (hwbox);
  108. }
  109.  
  110. static inline void
  111. get_child_requisition (GtkWrapBox     *wbox,
  112.                GtkWidget      *child,
  113.                GtkRequisition *child_requisition)
  114. {
  115.   if (wbox->homogeneous)
  116.     {
  117.       GtkHWrapBox *hwbox = GTK_HWRAP_BOX (wbox);
  118.       
  119.       child_requisition->width = hwbox->max_child_width;
  120.       child_requisition->height = hwbox->max_child_height;
  121.     }
  122.   else
  123.     gtk_widget_get_child_requisition (child, child_requisition);
  124. }
  125.  
  126. #ifdef UNUSED_CODE
  127. static void
  128. _gtk_hwrap_box_size_request (GtkWidget      *widget,
  129.                  GtkRequisition *requisition)
  130. {
  131.   GtkHWrapBox *this = GTK_HWRAP_BOX (widget);
  132.   GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
  133.   GtkWrapBoxChild *child;
  134.   guint area = 0;
  135.   
  136.   g_return_if_fail (requisition != NULL);
  137.   
  138.   /*<h2v-off>*/
  139.   requisition->width = 0;
  140.   requisition->height = 0;
  141.   this->max_child_width = 0;
  142.   this->max_child_height = 0;
  143.   
  144.   for (child = wbox->children; child; child = child->next)
  145.     if (GTK_WIDGET_VISIBLE (child->widget))
  146.       {
  147.     GtkRequisition child_requisition;
  148.     
  149.     gtk_widget_size_request (child->widget, &child_requisition);
  150.     
  151.     area += child_requisition.width * child_requisition.height;
  152.     this->max_child_width = MAX (this->max_child_width, child_requisition.width);
  153.     this->max_child_height = MAX (this->max_child_height, child_requisition.height);
  154.       }
  155.   if (wbox->homogeneous)
  156.     area = this->max_child_width * this->max_child_height * wbox->n_children;
  157.   
  158.   if (area)
  159.     {
  160.       requisition->width = sqrt (area * wbox->aspect_ratio);
  161.       requisition->height = area / requisition->width;
  162.     }
  163.   else
  164.     {
  165.       requisition->width = 0;
  166.       requisition->height = 0;
  167.     }
  168.   
  169.   requisition->width += GTK_CONTAINER (wbox)->border_width * 2;
  170.   requisition->height += GTK_CONTAINER (wbox)->border_width * 2;
  171.   /*<h2v-on>*/
  172. }
  173. #endif
  174.  
  175. static gfloat
  176. get_layout_size (GtkHWrapBox *this,
  177.          guint        max_width,
  178.          guint       *width_inc)
  179. {
  180.   GtkWrapBox *wbox = GTK_WRAP_BOX (this);
  181.   GtkWrapBoxChild *child;
  182.   guint n_rows, left_over = 0, total_height = 0;
  183.   gboolean last_row_filled = TRUE;
  184.  
  185.   *width_inc = this->max_child_width + 1;
  186.  
  187.   n_rows = 0;
  188.   for (child = wbox->children; child; child = child->next)
  189.     {
  190.       GtkWrapBoxChild *row_child;
  191.       GtkRequisition child_requisition;
  192.       guint row_width, row_height, n = 1;
  193.  
  194.       if (!GTK_WIDGET_VISIBLE (child->widget))
  195.     continue;
  196.  
  197.       get_child_requisition (wbox, child->widget, &child_requisition);
  198.       if (!last_row_filled)
  199.     *width_inc = MIN (*width_inc, child_requisition.width - left_over);
  200.       row_width = child_requisition.width;
  201.       row_height = child_requisition.height;
  202.       for (row_child = child->next; row_child && n < wbox->child_limit; row_child = row_child->next)
  203.     {
  204.       if (GTK_WIDGET_VISIBLE (row_child->widget))
  205.         {
  206.           get_child_requisition (wbox, row_child->widget, &child_requisition);
  207.           if (row_width + wbox->hspacing + child_requisition.width > max_width)
  208.         break;
  209.           row_width += wbox->hspacing + child_requisition.width;
  210.           row_height = MAX (row_height, child_requisition.height);
  211.           n++;
  212.         }
  213.       child = row_child;
  214.     }
  215.       last_row_filled = n >= wbox->child_limit;
  216.       left_over = last_row_filled ? 0 : max_width - (row_width + wbox->hspacing);
  217.       total_height += (n_rows ? wbox->vspacing : 0) + row_height;
  218.       n_rows++;
  219.     }
  220.  
  221.   if (*width_inc > this->max_child_width)
  222.     *width_inc = 0;
  223.   
  224.   return MAX (total_height, 1);
  225. }
  226.  
  227. static void
  228. gtk_hwrap_box_size_request (GtkWidget      *widget,
  229.                 GtkRequisition *requisition)
  230. {
  231.   GtkHWrapBox *this = GTK_HWRAP_BOX (widget);
  232.   GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
  233.   GtkWrapBoxChild *child;
  234.   gfloat ratio_dist, layout_width = 0;
  235.   guint row_inc = 0;
  236.   
  237.   g_return_if_fail (requisition != NULL);
  238.   
  239.   requisition->width = 0;
  240.   requisition->height = 0;
  241.   this->max_child_width = 0;
  242.   this->max_child_height = 0;
  243.  
  244.   /* size_request all children */
  245.   for (child = wbox->children; child; child = child->next)
  246.     if (GTK_WIDGET_VISIBLE (child->widget))
  247.       {
  248.     GtkRequisition child_requisition;
  249.     
  250.     gtk_widget_size_request (child->widget, &child_requisition);
  251.  
  252.     this->max_child_width = MAX (this->max_child_width, child_requisition.width);
  253.     this->max_child_height = MAX (this->max_child_height, child_requisition.height);
  254.       }
  255.  
  256.   /* figure all possible layouts */
  257.   ratio_dist = 32768;
  258.   layout_width = this->max_child_width;
  259.   do
  260.     {
  261.       gfloat layout_height;
  262.       gfloat ratio, dist;
  263.  
  264.       layout_width += row_inc;
  265.       layout_height = get_layout_size (this, layout_width, &row_inc);
  266.       ratio = layout_width / layout_height;        /*<h2v-skip>*/
  267.       dist = MAX (ratio, wbox->aspect_ratio) - MIN (ratio, wbox->aspect_ratio);
  268.       if (dist < ratio_dist)
  269.     {
  270.       ratio_dist = dist;
  271.       requisition->width = layout_width;
  272.       requisition->height = layout_height;
  273.     }
  274.       
  275.       /* g_print ("ratio for width %d height %d = %f\n",
  276.      (gint) layout_width,
  277.      (gint) layout_height,
  278.      ratio);
  279.       */
  280.     }
  281.   while (row_inc);
  282.  
  283.   requisition->width += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
  284.   requisition->height += GTK_CONTAINER (wbox)->border_width * 2; /*<h2v-skip>*/
  285.   /* g_print ("choosen: width %d, height %d\n",
  286.      requisition->width,
  287.      requisition->height);
  288.   */
  289. }
  290.  
  291. static GSList*
  292. reverse_list_row_children (GtkWrapBox       *wbox,
  293.                GtkWrapBoxChild **child_p,
  294.                GtkAllocation    *area,
  295.                guint            *max_child_size,
  296.                gboolean         *expand_line)
  297. {
  298.   GSList *slist = NULL;
  299.   guint width = 0, row_width = area->width;
  300.   GtkWrapBoxChild *child = *child_p;
  301.   
  302.   *max_child_size = 0;
  303.   *expand_line = FALSE;
  304.   
  305.   while (child && !GTK_WIDGET_VISIBLE (child->widget))
  306.     {
  307.       *child_p = child->next;
  308.       child = *child_p;
  309.     }
  310.   
  311.   if (child)
  312.     {
  313.       GtkRequisition child_requisition;
  314.       guint n = 1;
  315.       
  316.       get_child_requisition (wbox, child->widget, &child_requisition);
  317.       width += child_requisition.width;
  318.       *max_child_size = MAX (*max_child_size, child_requisition.height);
  319.       *expand_line |= child->vexpand;
  320.       slist = g_slist_prepend (slist, child);
  321.       *child_p = child->next;
  322.       child = *child_p;
  323.       
  324.       while (child && n < wbox->child_limit)
  325.     {
  326.       if (GTK_WIDGET_VISIBLE (child->widget))
  327.         {
  328.           get_child_requisition (wbox, child->widget, &child_requisition);
  329.           if (width + wbox->hspacing + child_requisition.width > row_width ||
  330.           child->forced_break)
  331.         break;
  332.           width += wbox->hspacing + child_requisition.width;
  333.           *max_child_size = MAX (*max_child_size, child_requisition.height);
  334.           *expand_line |= child->vexpand;
  335.           slist = g_slist_prepend (slist, child);
  336.           n++;
  337.         }
  338.       *child_p = child->next;
  339.       child = *child_p;
  340.     }
  341.     }
  342.   
  343.   return slist;
  344. }
  345.  
  346. static void
  347. layout_row (GtkWrapBox    *wbox,
  348.         GtkAllocation *area,
  349.         GSList        *children,
  350.         guint          children_per_line,
  351.         gboolean       vexpand)
  352. {
  353.   GSList *slist;
  354.   guint n_children = 0, n_expand_children = 0, have_expand_children = 0, total_width = 0;
  355.   gfloat x, width, extra;
  356.   GtkAllocation child_allocation;
  357.   
  358.   for (slist = children; slist; slist = slist->next)
  359.     {
  360.       GtkWrapBoxChild *child = slist->data;
  361.       GtkRequisition child_requisition;
  362.       
  363.       n_children++;
  364.       if (child->hexpand)
  365.     n_expand_children++;
  366.       
  367.       get_child_requisition (wbox, child->widget, &child_requisition);
  368.       total_width += child_requisition.width;
  369.     }
  370.   
  371.   width = MAX (1, area->width - (n_children - 1) * wbox->hspacing);
  372.   if (width > total_width)
  373.     extra = width - total_width;
  374.   else
  375.     extra = 0;
  376.   have_expand_children = n_expand_children && extra;
  377.   
  378.   x = area->x;
  379.   if (wbox->homogeneous)
  380.     {
  381.       width = MAX (1, area->width - (children_per_line - 1) * wbox->hspacing);
  382.       width /= ((gdouble) children_per_line);
  383.       extra = 0;
  384.     }
  385.   else if (have_expand_children && wbox->justify != GTK_JUSTIFY_FILL)
  386.     {
  387.       width = extra;
  388.       extra /= ((gdouble) n_expand_children);
  389.     }
  390.   else
  391.     {
  392.       if (wbox->justify == GTK_JUSTIFY_FILL)
  393.     {
  394.       width = extra;
  395.       have_expand_children = TRUE;
  396.       n_expand_children = n_children;
  397.       extra /= ((gdouble) n_expand_children);
  398.     }
  399.       else if (wbox->justify == GTK_JUSTIFY_CENTER)
  400.     {
  401.       x += extra / 2;
  402.       width = 0;
  403.       extra = 0;
  404.     }
  405.       else if (wbox->justify == GTK_JUSTIFY_LEFT)
  406.     {
  407.       width = 0;
  408.       extra = 0;
  409.     }
  410.       else if (wbox->justify == GTK_JUSTIFY_RIGHT)
  411.     {
  412.       x += extra;
  413.       width = 0;
  414.       extra = 0;
  415.     }
  416.     }
  417.   
  418.   n_children = 0;
  419.   for (slist = children; slist; slist = slist->next)
  420.     {
  421.       GtkWrapBoxChild *child = slist->data;
  422.       
  423.       child_allocation.x = x;
  424.       child_allocation.y = area->y;
  425.       if (wbox->homogeneous)
  426.     {
  427.       child_allocation.height = area->height;
  428.       child_allocation.width = width;
  429.       x += child_allocation.width + wbox->hspacing;
  430.     }
  431.       else
  432.     {
  433.       GtkRequisition child_requisition;
  434.       
  435.       get_child_requisition (wbox, child->widget, &child_requisition);
  436.       
  437.       if (child_requisition.height >= area->height)
  438.         child_allocation.height = area->height;
  439.       else
  440.         {
  441.           child_allocation.height = child_requisition.height;
  442.           if (wbox->line_justify == GTK_JUSTIFY_FILL || child->vfill)
  443.         child_allocation.height = area->height;
  444.           else if (child->vexpand || wbox->line_justify == GTK_JUSTIFY_CENTER)
  445.         child_allocation.y += (area->height - child_requisition.height) / 2;
  446.           else if (wbox->line_justify == GTK_JUSTIFY_BOTTOM)
  447.         child_allocation.y += area->height - child_requisition.height;
  448.         }
  449.       
  450.       if (have_expand_children)
  451.         {
  452.           child_allocation.width = child_requisition.width;
  453.           if (child->hexpand || wbox->justify == GTK_JUSTIFY_FILL)
  454.         {
  455.           guint space;
  456.           
  457.           n_expand_children--;
  458.           space = extra * n_expand_children;
  459.           space = width - space;
  460.           width -= space;
  461.           if (child->hfill)
  462.             child_allocation.width += space;
  463.           else
  464.             {
  465.               child_allocation.x += space / 2;
  466.               x += space;
  467.             }
  468.         }
  469.         }
  470.       else
  471.         {
  472.           /* g_print ("child_allocation.x %d += %d * %f ",
  473.                child_allocation.x, n_children, extra); */
  474.           child_allocation.x += n_children * extra;
  475.           /* g_print ("= %d\n",
  476.                child_allocation.x); */
  477.           child_allocation.width = MIN (child_requisition.width,
  478.                         area->width - child_allocation.x + area->x);
  479.         }
  480.     }
  481.       
  482.       x += child_allocation.width + wbox->hspacing;
  483.       gtk_widget_size_allocate (child->widget, &child_allocation);
  484.       n_children++;
  485.     }
  486. }
  487.  
  488. typedef struct _Line Line;
  489. struct _Line
  490. {
  491.   GSList  *children;
  492.   guint16  min_size;
  493.   guint    expand : 1;
  494.   Line     *next;
  495. };
  496.  
  497. static void
  498. layout_rows (GtkWrapBox    *wbox,
  499.          GtkAllocation *area)
  500. {
  501.   GtkWrapBoxChild *next_child;
  502.   guint min_height;
  503.   gboolean vexpand;
  504.   GSList *slist;
  505.   Line *line_list = NULL;
  506.   guint total_height = 0, n_expand_lines = 0, n_lines = 0;
  507.   gfloat shrink_height;
  508.   guint children_per_line;
  509.   
  510.   next_child = wbox->children;
  511.   slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
  512.                                   &next_child,
  513.                                   area,
  514.                                   &min_height,
  515.                                   &vexpand);
  516.   slist = g_slist_reverse (slist);
  517.  
  518.   children_per_line = g_slist_length (slist);
  519.   while (slist)
  520.     {
  521.       Line *line = g_new (Line, 1);
  522.       
  523.       line->children = slist;
  524.       line->min_size = min_height;
  525.       total_height += min_height;
  526.       line->expand = vexpand;
  527.       if (vexpand)
  528.     n_expand_lines++;
  529.       line->next = line_list;
  530.       line_list = line;
  531.       n_lines++;
  532.       
  533.       slist = GTK_WRAP_BOX_GET_CLASS (wbox)->rlist_line_children (wbox,
  534.                                   &next_child,
  535.                                   area,
  536.                                   &min_height,
  537.                                   &vexpand);
  538.       slist = g_slist_reverse (slist);
  539.     }
  540.   
  541.   if (total_height > area->height)
  542.     shrink_height = total_height - area->height;
  543.   else
  544.     shrink_height = 0;
  545.   
  546.   if (1) /* reverse lines and shrink */
  547.     {
  548.       Line *prev = NULL, *last = NULL;
  549.       gfloat n_shrink_lines = n_lines;
  550.       
  551.       while (line_list)
  552.     {
  553.       Line *tmp = line_list->next;
  554.  
  555.       if (shrink_height)
  556.         {
  557.           Line *line = line_list;
  558.           guint shrink_fract = shrink_height / n_shrink_lines + 0.5;
  559.  
  560.           if (line->min_size > shrink_fract)
  561.         {
  562.           shrink_height -= shrink_fract;
  563.           line->min_size -= shrink_fract;
  564.         }
  565.           else
  566.         {
  567.           shrink_height -= line->min_size - 1;
  568.           line->min_size = 1;
  569.         }
  570.         }
  571.       n_shrink_lines--;
  572.  
  573.       last = line_list;
  574.       line_list->next = prev;
  575.       prev = line_list;
  576.       line_list = tmp;
  577.     }
  578.       line_list = last;
  579.     }
  580.   
  581.   if (n_lines)
  582.     {
  583.       Line *line;
  584.       gfloat y, height, extra = 0;
  585.       
  586.       height = area->height;
  587.       height = MAX (n_lines, height - (n_lines - 1) * wbox->vspacing);
  588.       
  589.       if (wbox->homogeneous)
  590.     height /= ((gdouble) n_lines);
  591.       else if (n_expand_lines)
  592.     {
  593.       height = MAX (0, height - total_height);
  594.       extra = height / ((gdouble) n_expand_lines);
  595.     }
  596.       else
  597.     height = 0;
  598.       
  599.       y = area->y;
  600.       line = line_list;
  601.       while (line)
  602.     {
  603.       GtkAllocation row_allocation;
  604.       Line *next_line = line->next;
  605.       
  606.       row_allocation.x = area->x;
  607.       row_allocation.width = area->width;
  608.       if (wbox->homogeneous)
  609.         row_allocation.height = height;
  610.       else
  611.         {
  612.           row_allocation.height = line->min_size;
  613.           
  614.           if (line->expand)
  615.         row_allocation.height += extra;
  616.         }
  617.       
  618.       row_allocation.y = y;
  619.       
  620.       y += row_allocation.height + wbox->vspacing;
  621.       layout_row (wbox,
  622.               &row_allocation,
  623.               line->children,
  624.               children_per_line,
  625.               line->expand);
  626.  
  627.       g_slist_free (line->children);
  628.       g_free (line);
  629.       line = next_line;
  630.     }
  631.     }
  632. }
  633.  
  634. static void
  635. gtk_hwrap_box_size_allocate (GtkWidget     *widget,
  636.                  GtkAllocation *allocation)
  637. {
  638.   GtkWrapBox *wbox = GTK_WRAP_BOX (widget);
  639.   GtkAllocation area;
  640.   guint border = GTK_CONTAINER (wbox)->border_width; /*<h2v-skip>*/
  641.   
  642.   widget->allocation = *allocation;
  643.   area.x = allocation->x + border;
  644.   area.y = allocation->y + border;
  645.   area.width = MAX (1, (gint) allocation->width - border * 2);
  646.   area.height = MAX (1, (gint) allocation->height - border * 2);
  647.   
  648.   /*<h2v-off>*/
  649.   /* g_print ("got: width %d, height %d\n",
  650.      allocation->width,
  651.      allocation->height);
  652.   */
  653.   /*<h2v-on>*/
  654.   
  655.   layout_rows (wbox, &area);
  656. }
  657.