"Fossies" - the Fresh Open Source Software Archive

Member "putty-0.73/unix/gtkcols.c" (22 Sep 2019, 36849 Bytes) of package /linux/misc/putty-0.73.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gtkcols.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.72_vs_0.73.

    1 /*
    2  * gtkcols.c - implementation of the `Columns' GTK layout container.
    3  */
    4 
    5 #include <assert.h>
    6 #include <gtk/gtk.h>
    7 #include "defs.h"
    8 #include "gtkcompat.h"
    9 #include "gtkcols.h"
   10 
   11 #if GTK_CHECK_VERSION(2,0,0)
   12 /* The "focus" method lives in GtkWidget from GTK 2 onwards, but it
   13  * was in GtkContainer in GTK 1 */
   14 #define FOCUS_METHOD_SUPERCLASS GtkWidget
   15 #define FOCUS_METHOD_LOCATION widget_class /* used in columns_init */
   16 #define CHILD_FOCUS(cont, dir) gtk_widget_child_focus(GTK_WIDGET(cont), dir)
   17 #else
   18 #define FOCUS_METHOD_SUPERCLASS GtkContainer
   19 #define FOCUS_METHOD_LOCATION container_class
   20 #define CHILD_FOCUS(cont, dir) gtk_container_focus(GTK_CONTAINER(cont), dir)
   21 #endif
   22 
   23 static void columns_init(Columns *cols);
   24 static void columns_class_init(ColumnsClass *klass);
   25 #if !GTK_CHECK_VERSION(2,0,0)
   26 static void columns_finalize(GtkObject *object);
   27 #else
   28 static void columns_finalize(GObject *object);
   29 #endif
   30 static void columns_map(GtkWidget *widget);
   31 static void columns_unmap(GtkWidget *widget);
   32 #if !GTK_CHECK_VERSION(2,0,0)
   33 static void columns_draw(GtkWidget *widget, GdkRectangle *area);
   34 static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);
   35 #endif
   36 static void columns_base_add(GtkContainer *container, GtkWidget *widget);
   37 static void columns_remove(GtkContainer *container, GtkWidget *widget);
   38 static void columns_forall(GtkContainer *container, gboolean include_internals,
   39                            GtkCallback callback, gpointer callback_data);
   40 static gint columns_focus(FOCUS_METHOD_SUPERCLASS *container,
   41                           GtkDirectionType dir);
   42 static GType columns_child_type(GtkContainer *container);
   43 #if GTK_CHECK_VERSION(3,0,0)
   44 static void columns_get_preferred_width(GtkWidget *widget,
   45                                         gint *min, gint *nat);
   46 static void columns_get_preferred_height(GtkWidget *widget,
   47                                          gint *min, gint *nat);
   48 static void columns_get_preferred_width_for_height(GtkWidget *widget,
   49                                                    gint height,
   50                                                    gint *min, gint *nat);
   51 static void columns_get_preferred_height_for_width(GtkWidget *widget,
   52                                                    gint width,
   53                                                    gint *min, gint *nat);
   54 #else
   55 static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
   56 #endif
   57 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
   58 
   59 static GtkContainerClass *parent_class = NULL;
   60 
   61 #if !GTK_CHECK_VERSION(2,0,0)
   62 GType columns_get_type(void)
   63 {
   64     static GType columns_type = 0;
   65 
   66     if (!columns_type) {
   67         static const GtkTypeInfo columns_info = {
   68             "Columns",
   69             sizeof(Columns),
   70             sizeof(ColumnsClass),
   71             (GtkClassInitFunc) columns_class_init,
   72             (GtkObjectInitFunc) columns_init,
   73             /* reserved_1 */ NULL,
   74             /* reserved_2 */ NULL,
   75             (GtkClassInitFunc) NULL,
   76         };
   77 
   78         columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info);
   79     }
   80 
   81     return columns_type;
   82 }
   83 #else
   84 GType columns_get_type(void)
   85 {
   86     static GType columns_type = 0;
   87 
   88     if (!columns_type) {
   89         static const GTypeInfo columns_info = {
   90             sizeof(ColumnsClass),
   91             NULL,
   92             NULL,
   93             (GClassInitFunc) columns_class_init,
   94             NULL,
   95             NULL,
   96             sizeof(Columns),
   97             0,
   98             (GInstanceInitFunc)columns_init,
   99         };
  100 
  101         columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",
  102                                               &columns_info, 0);
  103     }
  104 
  105     return columns_type;
  106 }
  107 #endif
  108 
  109 static gint (*columns_inherited_focus)(FOCUS_METHOD_SUPERCLASS *container,
  110                                        GtkDirectionType direction);
  111 
  112 static void columns_class_init(ColumnsClass *klass)
  113 {
  114 #if !GTK_CHECK_VERSION(2,0,0)
  115     GtkObjectClass *object_class = (GtkObjectClass *)klass;
  116     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
  117     GtkContainerClass *container_class = (GtkContainerClass *)klass;
  118 #else
  119     GObjectClass *object_class = G_OBJECT_CLASS(klass);
  120     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
  121     GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
  122 #endif
  123 
  124 #if !GTK_CHECK_VERSION(2,0,0)
  125     parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
  126 #else
  127     parent_class = g_type_class_peek_parent(klass);
  128 #endif
  129 
  130     object_class->finalize = columns_finalize;
  131     widget_class->map = columns_map;
  132     widget_class->unmap = columns_unmap;
  133 #if !GTK_CHECK_VERSION(2,0,0)
  134     widget_class->draw = columns_draw;
  135     widget_class->expose_event = columns_expose;
  136 #endif
  137 #if GTK_CHECK_VERSION(3,0,0)
  138     widget_class->get_preferred_width = columns_get_preferred_width;
  139     widget_class->get_preferred_height = columns_get_preferred_height;
  140     widget_class->get_preferred_width_for_height =
  141         columns_get_preferred_width_for_height;
  142     widget_class->get_preferred_height_for_width =
  143         columns_get_preferred_height_for_width;
  144 #else
  145     widget_class->size_request = columns_size_request;
  146 #endif
  147     widget_class->size_allocate = columns_size_allocate;
  148 
  149     container_class->add = columns_base_add;
  150     container_class->remove = columns_remove;
  151     container_class->forall = columns_forall;
  152     container_class->child_type = columns_child_type;
  153 
  154     /* Save the previous value of this method. */
  155     if (!columns_inherited_focus)
  156         columns_inherited_focus = FOCUS_METHOD_LOCATION->focus;
  157     FOCUS_METHOD_LOCATION->focus = columns_focus;
  158 }
  159 
  160 static void columns_init(Columns *cols)
  161 {
  162     gtk_widget_set_has_window(GTK_WIDGET(cols), false);
  163 
  164     cols->children = NULL;
  165     cols->spacing = 0;
  166 }
  167 
  168 static void columns_child_free(gpointer vchild)
  169 {
  170     ColumnsChild *child = (ColumnsChild *)vchild;
  171     if (child->percentages)
  172         g_free(child->percentages);
  173     g_free(child);
  174 }
  175 
  176 static void columns_finalize(
  177 #if !GTK_CHECK_VERSION(2,0,0)
  178     GtkObject *object
  179 #else
  180     GObject *object
  181 #endif
  182     )
  183 {
  184     Columns *cols;
  185 
  186     g_return_if_fail(object != NULL);
  187     g_return_if_fail(IS_COLUMNS(object));
  188 
  189     cols = COLUMNS(object);
  190 
  191 #if !GTK_CHECK_VERSION(2,0,0)
  192     {
  193         GList *node;
  194         for (node = cols->children; node; node = node->next)
  195             if (node->data)
  196                 columns_child_free(node->data);
  197     }
  198     g_list_free(cols->children);
  199 #else
  200     g_list_free_full(cols->children, columns_child_free);
  201 #endif
  202 
  203     cols->children = NULL;
  204 
  205 #if !GTK_CHECK_VERSION(2,0,0)
  206     GTK_OBJECT_CLASS(parent_class)->finalize(object);
  207 #else
  208     G_OBJECT_CLASS(parent_class)->finalize(object);
  209 #endif
  210 }
  211 
  212 /*
  213  * These appear to be thoroughly tedious functions; the only reason
  214  * we have to reimplement them at all is because we defined our own
  215  * format for our GList of children...
  216  */
  217 static void columns_map(GtkWidget *widget)
  218 {
  219     Columns *cols;
  220     ColumnsChild *child;
  221     GList *children;
  222 
  223     g_return_if_fail(widget != NULL);
  224     g_return_if_fail(IS_COLUMNS(widget));
  225 
  226     cols = COLUMNS(widget);
  227     gtk_widget_set_mapped(GTK_WIDGET(cols), true);
  228 
  229     for (children = cols->children;
  230          children && (child = children->data);
  231          children = children->next) {
  232         if (child->widget &&
  233             gtk_widget_get_visible(child->widget) &&
  234             !gtk_widget_get_mapped(child->widget))
  235             gtk_widget_map(child->widget);
  236     }
  237 }
  238 static void columns_unmap(GtkWidget *widget)
  239 {
  240     Columns *cols;
  241     ColumnsChild *child;
  242     GList *children;
  243 
  244     g_return_if_fail(widget != NULL);
  245     g_return_if_fail(IS_COLUMNS(widget));
  246 
  247     cols = COLUMNS(widget);
  248     gtk_widget_set_mapped(GTK_WIDGET(cols), false);
  249 
  250     for (children = cols->children;
  251          children && (child = children->data);
  252          children = children->next) {
  253         if (child->widget &&
  254             gtk_widget_get_visible(child->widget) &&
  255             gtk_widget_get_mapped(child->widget))
  256             gtk_widget_unmap(child->widget);
  257     }
  258 }
  259 #if !GTK_CHECK_VERSION(2,0,0)
  260 static void columns_draw(GtkWidget *widget, GdkRectangle *area)
  261 {
  262     Columns *cols;
  263     ColumnsChild *child;
  264     GList *children;
  265     GdkRectangle child_area;
  266 
  267     g_return_if_fail(widget != NULL);
  268     g_return_if_fail(IS_COLUMNS(widget));
  269 
  270     if (GTK_WIDGET_DRAWABLE(widget)) {
  271         cols = COLUMNS(widget);
  272 
  273         for (children = cols->children;
  274              children && (child = children->data);
  275              children = children->next) {
  276             if (child->widget &&
  277                 GTK_WIDGET_DRAWABLE(child->widget) &&
  278                 gtk_widget_intersect(child->widget, area, &child_area))
  279                 gtk_widget_draw(child->widget, &child_area);
  280         }
  281     }
  282 }
  283 static gint columns_expose(GtkWidget *widget, GdkEventExpose *event)
  284 {
  285     Columns *cols;
  286     ColumnsChild *child;
  287     GList *children;
  288     GdkEventExpose child_event;
  289 
  290     g_return_val_if_fail(widget != NULL, false);
  291     g_return_val_if_fail(IS_COLUMNS(widget), false);
  292     g_return_val_if_fail(event != NULL, false);
  293 
  294     if (GTK_WIDGET_DRAWABLE(widget)) {
  295         cols = COLUMNS(widget);
  296         child_event = *event;
  297 
  298         for (children = cols->children;
  299              children && (child = children->data);
  300              children = children->next) {
  301             if (child->widget &&
  302                 GTK_WIDGET_DRAWABLE(child->widget) &&
  303                 GTK_WIDGET_NO_WINDOW(child->widget) &&
  304                 gtk_widget_intersect(child->widget, &event->area,
  305                                      &child_event.area))
  306                 gtk_widget_event(child->widget, (GdkEvent *)&child_event);
  307         }
  308     }
  309     return false;
  310 }
  311 #endif
  312 
  313 static void columns_base_add(GtkContainer *container, GtkWidget *widget)
  314 {
  315     Columns *cols;
  316 
  317     g_return_if_fail(container != NULL);
  318     g_return_if_fail(IS_COLUMNS(container));
  319     g_return_if_fail(widget != NULL);
  320 
  321     cols = COLUMNS(container);
  322 
  323     /*
  324      * Default is to add a new widget spanning all columns.
  325      */
  326     columns_add(cols, widget, 0, 0);   /* 0 means ncols */
  327 }
  328 
  329 static void columns_remove(GtkContainer *container, GtkWidget *widget)
  330 {
  331     Columns *cols;
  332     ColumnsChild *child;
  333     GtkWidget *childw;
  334     GList *children;
  335     bool was_visible;
  336 
  337     g_return_if_fail(container != NULL);
  338     g_return_if_fail(IS_COLUMNS(container));
  339     g_return_if_fail(widget != NULL);
  340 
  341     cols = COLUMNS(container);
  342 
  343     for (children = cols->children;
  344          children && (child = children->data);
  345          children = children->next) {
  346         if (child->widget != widget)
  347             continue;
  348 
  349         was_visible = gtk_widget_get_visible(widget);
  350         gtk_widget_unparent(widget);
  351         cols->children = g_list_remove_link(cols->children, children);
  352         g_list_free(children);
  353 
  354         if (child->same_height_as) {
  355             g_return_if_fail(child->same_height_as->same_height_as == child);
  356             child->same_height_as->same_height_as = NULL;
  357             if (gtk_widget_get_visible(child->same_height_as->widget))
  358                 gtk_widget_queue_resize(GTK_WIDGET(container));
  359         }
  360 
  361         g_free(child);
  362         if (was_visible)
  363             gtk_widget_queue_resize(GTK_WIDGET(container));
  364         break;
  365     }
  366 
  367     for (children = cols->taborder;
  368          children && (childw = children->data);
  369          children = children->next) {
  370         if (childw != widget)
  371             continue;
  372 
  373         cols->taborder = g_list_remove_link(cols->taborder, children);
  374         g_list_free(children);
  375         break;
  376     }
  377 }
  378 
  379 static void columns_forall(GtkContainer *container, gboolean include_internals,
  380                            GtkCallback callback, gpointer callback_data)
  381 {
  382     Columns *cols;
  383     ColumnsChild *child;
  384     GList *children, *next;
  385 
  386     g_return_if_fail(container != NULL);
  387     g_return_if_fail(IS_COLUMNS(container));
  388     g_return_if_fail(callback != NULL);
  389 
  390     cols = COLUMNS(container);
  391 
  392     for (children = cols->children;
  393          children && (child = children->data);
  394          children = next) {
  395         /*
  396          * We can't wait until after the callback to assign
  397          * `children = children->next', because the callback might
  398          * be gtk_widget_destroy, which would remove the link
  399          * `children' from the list! So instead we must get our
  400          * hands on the value of the `next' pointer _before_ the
  401          * callback.
  402          */
  403         next = children->next;
  404         if (child->widget)
  405             callback(child->widget, callback_data);
  406     }
  407 }
  408 
  409 static GType columns_child_type(GtkContainer *container)
  410 {
  411     return GTK_TYPE_WIDGET;
  412 }
  413 
  414 GtkWidget *columns_new(gint spacing)
  415 {
  416     Columns *cols;
  417 
  418 #if !GTK_CHECK_VERSION(2,0,0)
  419     cols = gtk_type_new(columns_get_type());
  420 #else
  421     cols = g_object_new(TYPE_COLUMNS, NULL);
  422 #endif
  423 
  424     cols->spacing = spacing;
  425 
  426     return GTK_WIDGET(cols);
  427 }
  428 
  429 void columns_set_cols(Columns *cols, gint ncols, const gint *percentages)
  430 {
  431     ColumnsChild *childdata;
  432     gint i;
  433 
  434     g_return_if_fail(cols != NULL);
  435     g_return_if_fail(IS_COLUMNS(cols));
  436     g_return_if_fail(ncols > 0);
  437     g_return_if_fail(percentages != NULL);
  438 
  439     childdata = g_new(ColumnsChild, 1);
  440     childdata->widget = NULL;
  441     childdata->ncols = ncols;
  442     childdata->percentages = g_new(gint, ncols);
  443     childdata->force_left = false;
  444     for (i = 0; i < ncols; i++)
  445         childdata->percentages[i] = percentages[i];
  446 
  447     cols->children = g_list_append(cols->children, childdata);
  448 }
  449 
  450 void columns_add(Columns *cols, GtkWidget *child,
  451                  gint colstart, gint colspan)
  452 {
  453     ColumnsChild *childdata;
  454 
  455     g_return_if_fail(cols != NULL);
  456     g_return_if_fail(IS_COLUMNS(cols));
  457     g_return_if_fail(child != NULL);
  458     g_return_if_fail(gtk_widget_get_parent(child) == NULL);
  459 
  460     childdata = g_new(ColumnsChild, 1);
  461     childdata->widget = child;
  462     childdata->colstart = colstart;
  463     childdata->colspan = colspan;
  464     childdata->force_left = false;
  465     childdata->same_height_as = NULL;
  466     childdata->percentages = NULL;
  467 
  468     cols->children = g_list_append(cols->children, childdata);
  469     cols->taborder = g_list_append(cols->taborder, child);
  470 
  471     gtk_widget_set_parent(child, GTK_WIDGET(cols));
  472 
  473     if (gtk_widget_get_realized(GTK_WIDGET(cols)))
  474         gtk_widget_realize(child);
  475 
  476     if (gtk_widget_get_visible(GTK_WIDGET(cols)) &&
  477         gtk_widget_get_visible(child)) {
  478         if (gtk_widget_get_mapped(GTK_WIDGET(cols)))
  479             gtk_widget_map(child);
  480         gtk_widget_queue_resize(child);
  481     }
  482 }
  483 
  484 static ColumnsChild *columns_find_child(Columns *cols, GtkWidget *widget)
  485 {
  486     GList *children;
  487     ColumnsChild *child;
  488 
  489     for (children = cols->children;
  490          children && (child = children->data);
  491          children = children->next) {
  492 
  493         if (child->widget == widget)
  494             return child;
  495     }
  496 
  497     return NULL;
  498 }
  499 
  500 void columns_force_left_align(Columns *cols, GtkWidget *widget)
  501 {
  502     ColumnsChild *child;
  503 
  504     g_return_if_fail(cols != NULL);
  505     g_return_if_fail(IS_COLUMNS(cols));
  506     g_return_if_fail(widget != NULL);
  507 
  508     child = columns_find_child(cols, widget);
  509     g_return_if_fail(child != NULL);
  510 
  511     child->force_left = true;
  512     if (gtk_widget_get_visible(widget))
  513         gtk_widget_queue_resize(GTK_WIDGET(cols));
  514 }
  515 
  516 void columns_force_same_height(Columns *cols, GtkWidget *cw1, GtkWidget *cw2)
  517 {
  518     ColumnsChild *child1, *child2;
  519 
  520     g_return_if_fail(cols != NULL);
  521     g_return_if_fail(IS_COLUMNS(cols));
  522     g_return_if_fail(cw1 != NULL);
  523     g_return_if_fail(cw2 != NULL);
  524 
  525     child1 = columns_find_child(cols, cw1);
  526     g_return_if_fail(child1 != NULL);
  527     child2 = columns_find_child(cols, cw2);
  528     g_return_if_fail(child2 != NULL);
  529 
  530     child1->same_height_as = child2;
  531     child2->same_height_as = child1;
  532     if (gtk_widget_get_visible(cw1) || gtk_widget_get_visible(cw2))
  533         gtk_widget_queue_resize(GTK_WIDGET(cols));
  534 }
  535 
  536 void columns_taborder_last(Columns *cols, GtkWidget *widget)
  537 {
  538     GtkWidget *childw;
  539     GList *children;
  540 
  541     g_return_if_fail(cols != NULL);
  542     g_return_if_fail(IS_COLUMNS(cols));
  543     g_return_if_fail(widget != NULL);
  544 
  545     for (children = cols->taborder;
  546          children && (childw = children->data);
  547          children = children->next) {
  548         if (childw != widget)
  549             continue;
  550 
  551         cols->taborder = g_list_remove_link(cols->taborder, children);
  552         g_list_free(children);
  553         cols->taborder = g_list_append(cols->taborder, widget);
  554         break;
  555     }
  556 }
  557 
  558 /*
  559  * Override GtkContainer's focus movement so the user can
  560  * explicitly specify the tab order.
  561  */
  562 static gint columns_focus(FOCUS_METHOD_SUPERCLASS *super, GtkDirectionType dir)
  563 {
  564     Columns *cols;
  565     GList *pos;
  566     GtkWidget *focuschild;
  567 
  568     g_return_val_if_fail(super != NULL, false);
  569     g_return_val_if_fail(IS_COLUMNS(super), false);
  570 
  571     cols = COLUMNS(super);
  572 
  573     if (!gtk_widget_is_drawable(GTK_WIDGET(cols)) ||
  574         !gtk_widget_is_sensitive(GTK_WIDGET(cols)))
  575         return false;
  576 
  577     if (!gtk_widget_get_can_focus(GTK_WIDGET(cols)) &&
  578         (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {
  579 
  580         focuschild = gtk_container_get_focus_child(GTK_CONTAINER(cols));
  581         gtk_container_set_focus_child(GTK_CONTAINER(cols), NULL);
  582 
  583         if (dir == GTK_DIR_TAB_FORWARD)
  584             pos = cols->taborder;
  585         else
  586             pos = g_list_last(cols->taborder);
  587 
  588         while (pos) {
  589             GtkWidget *child = pos->data;
  590 
  591             if (focuschild) {
  592                 if (focuschild == child) {
  593                     focuschild = NULL; /* now we can start looking in here */
  594                     if (gtk_widget_is_drawable(child) &&
  595                         GTK_IS_CONTAINER(child) &&
  596                         !gtk_widget_has_focus(child)) {
  597                         if (CHILD_FOCUS(child, dir))
  598                             return true;
  599                     }
  600                 }
  601             } else if (gtk_widget_is_drawable(child)) {
  602                 if (GTK_IS_CONTAINER(child)) {
  603                     if (CHILD_FOCUS(child, dir))
  604                         return true;
  605                 } else if (gtk_widget_get_can_focus(child)) {
  606                     gtk_widget_grab_focus(child);
  607                     return true;
  608                 }
  609             }
  610 
  611             if (dir == GTK_DIR_TAB_FORWARD)
  612                 pos = pos->next;
  613             else
  614                 pos = pos->prev;
  615         }
  616 
  617         return false;
  618     } else
  619         return columns_inherited_focus(super, dir);
  620 }
  621 
  622 /*
  623  * Underlying parts of the layout algorithm, to compute the Columns
  624  * container's width or height given the widths or heights of its
  625  * children. These will be called in various ways with different
  626  * notions of width and height in use, so we abstract them out and
  627  * pass them a 'get width' or 'get height' function pointer.
  628  */
  629 
  630 typedef gint (*widget_dim_fn_t)(ColumnsChild *child);
  631 
  632 static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
  633 {
  634     ColumnsChild *child;
  635     GList *children;
  636     gint i, ncols, colspan, retwidth, childwidth;
  637     const gint *percentages;
  638     static const gint onecol[] = { 100 };
  639 
  640 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  641     printf("compute_width(%p): start\n", cols);
  642 #endif
  643 
  644     retwidth = 0;
  645 
  646     ncols = 1;
  647     percentages = onecol;
  648 
  649     for (children = cols->children;
  650          children && (child = children->data);
  651          children = children->next) {
  652 
  653         if (!child->widget) {
  654             /* Column reconfiguration. */
  655             ncols = child->ncols;
  656             percentages = child->percentages;
  657             continue;
  658         }
  659 
  660         /* Only take visible widgets into account. */
  661         if (!gtk_widget_get_visible(child->widget))
  662             continue;
  663 
  664         childwidth = get_width(child);
  665         colspan = child->colspan ? child->colspan : ncols-child->colstart;
  666         assert(colspan > 0);
  667 
  668 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  669         printf("compute_width(%p): ", cols);
  670         if (GTK_IS_LABEL(child->widget))
  671             printf("label %p '%s' wrap=%s: ", child->widget,
  672                    gtk_label_get_text(GTK_LABEL(child->widget)),
  673                    (gtk_label_get_line_wrap(GTK_LABEL(child->widget))
  674                     ? "true" : "false"));
  675         else
  676             printf("widget %p: ", child->widget);
  677         {
  678             gint min, nat;
  679             gtk_widget_get_preferred_width(child->widget, &min, &nat);
  680             printf("minwidth=%d natwidth=%d ", min, nat);
  681         }
  682         printf("thiswidth=%d span=%d\n", childwidth, colspan);
  683 #endif
  684 
  685         /*
  686          * To compute width: we know that childwidth + cols->spacing
  687          * needs to equal a certain percentage of the full width of
  688          * the container. So we work this value out, figure out how
  689          * wide the container will need to be to make that percentage
  690          * of it equal to that width, and ensure our returned width is
  691          * at least that much. Very simple really.
  692          */
  693         {
  694             int percent, thiswid, fullwid;
  695 
  696             percent = 0;
  697             for (i = 0; i < colspan; i++)
  698                 percent += percentages[child->colstart+i];
  699 
  700             thiswid = childwidth + cols->spacing;
  701             /*
  702              * Since childwidth is (at least sometimes) the _minimum_
  703              * size the child needs, we must ensure that it gets _at
  704              * least_ that size. Hence, when scaling thiswid up to
  705              * fullwid, we must round up, which means adding percent-1
  706              * before dividing by percent.
  707              */
  708             fullwid = (thiswid * 100 + percent - 1) / percent;
  709 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  710             printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n",
  711                    cols, child->widget, thiswid, fullwid);
  712 #endif
  713 
  714             /*
  715              * The above calculation assumes every widget gets
  716              * cols->spacing on the right. So we subtract
  717              * cols->spacing here to account for the extra load of
  718              * spacing on the right.
  719              */
  720             if (retwidth < fullwid - cols->spacing)
  721                 retwidth = fullwid - cols->spacing;
  722         }
  723     }
  724 
  725     retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
  726 
  727 #ifdef COLUMNS_WIDTH_DIAGNOSTICS
  728     printf("compute_width(%p): done, returning %d\n", cols, retwidth);
  729 #endif
  730 
  731     return retwidth;
  732 }
  733 
  734 static void columns_alloc_horiz(Columns *cols, gint ourwidth,
  735                                 widget_dim_fn_t get_width)
  736 {
  737     ColumnsChild *child;
  738     GList *children;
  739     gint i, ncols, colspan, border, *colxpos, childwidth;
  740 
  741     border = gtk_container_get_border_width(GTK_CONTAINER(cols));
  742 
  743     ncols = 1;
  744     /* colxpos gives the starting x position of each column.
  745      * We supply n+1 of them, so that we can find the RH edge easily.
  746      * All ending x positions are expected to be adjusted afterwards by
  747      * subtracting the spacing. */
  748     colxpos = g_new(gint, 2);
  749     colxpos[0] = 0;
  750     colxpos[1] = ourwidth - 2*border + cols->spacing;
  751 
  752     for (children = cols->children;
  753          children && (child = children->data);
  754          children = children->next) {
  755 
  756         if (!child->widget) {
  757             gint percent;
  758 
  759             /* Column reconfiguration. */
  760             ncols = child->ncols;
  761             colxpos = g_renew(gint, colxpos, ncols + 1);
  762             colxpos[0] = 0;
  763             percent = 0;
  764             for (i = 0; i < ncols; i++) {
  765                 percent += child->percentages[i];
  766                 colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing)
  767                                 * percent / 100);
  768             }
  769             continue;
  770         }
  771 
  772         /* Only take visible widgets into account. */
  773         if (!gtk_widget_get_visible(child->widget))
  774             continue;
  775 
  776         childwidth = get_width(child);
  777         colspan = child->colspan ? child->colspan : ncols-child->colstart;
  778 
  779         /*
  780          * Starting x position is cols[colstart].
  781          * Ending x position is cols[colstart+colspan] - spacing.
  782          *
  783          * Unless we're forcing left, in which case the width is
  784          * exactly the requisition width.
  785          */
  786         child->x = colxpos[child->colstart];
  787         if (child->force_left)
  788             child->w = childwidth;
  789         else
  790             child->w = (colxpos[child->colstart+colspan] -
  791                         colxpos[child->colstart] - cols->spacing);
  792     }
  793 
  794     g_free(colxpos);
  795 }
  796 
  797 static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
  798 {
  799     ColumnsChild *child;
  800     GList *children;
  801     gint i, ncols, colspan, *colypos, retheight, childheight;
  802 
  803     retheight = cols->spacing;
  804 
  805     ncols = 1;
  806     colypos = g_new(gint, 1);
  807     colypos[0] = 0;
  808 
  809     for (children = cols->children;
  810          children && (child = children->data);
  811          children = children->next) {
  812 
  813         if (!child->widget) {
  814             /* Column reconfiguration. */
  815             for (i = 1; i < ncols; i++) {
  816                 if (colypos[0] < colypos[i])
  817                     colypos[0] = colypos[i];
  818             }
  819             ncols = child->ncols;
  820             colypos = g_renew(gint, colypos, ncols);
  821             for (i = 1; i < ncols; i++)
  822                 colypos[i] = colypos[0];
  823             continue;
  824         }
  825 
  826         /* Only take visible widgets into account. */
  827         if (!gtk_widget_get_visible(child->widget))
  828             continue;
  829 
  830         childheight = get_height(child);
  831         if (child->same_height_as) {
  832             gint childheight2 = get_height(child->same_height_as);
  833             if (childheight < childheight2)
  834                 childheight = childheight2;
  835         }
  836         colspan = child->colspan ? child->colspan : ncols-child->colstart;
  837 
  838         /*
  839          * To compute height: the widget's top will be positioned at
  840          * the largest y value so far reached in any of the columns it
  841          * crosses. Then it will go down by childheight plus padding;
  842          * and the point it reaches at the bottom is the new y value
  843          * in all those columns, and minus the padding it is also a
  844          * lower bound on our own height.
  845          */
  846         {
  847             int topy, boty;
  848 
  849             topy = 0;
  850             for (i = 0; i < colspan; i++) {
  851                 if (topy < colypos[child->colstart+i])
  852                     topy = colypos[child->colstart+i];
  853             }
  854             boty = topy + childheight + cols->spacing;
  855             for (i = 0; i < colspan; i++) {
  856                 colypos[child->colstart+i] = boty;
  857             }
  858 
  859             if (retheight < boty - cols->spacing)
  860                 retheight = boty - cols->spacing;
  861         }
  862     }
  863 
  864     retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
  865 
  866     g_free(colypos);
  867 
  868     return retheight;
  869 }
  870 
  871 static void columns_alloc_vert(Columns *cols, gint ourheight,
  872                                widget_dim_fn_t get_height)
  873 {
  874     ColumnsChild *child;
  875     GList *children;
  876     gint i, ncols, colspan, *colypos, realheight, fakeheight;
  877 
  878     ncols = 1;
  879     /* As in size_request, colypos is the lowest y reached in each column. */
  880     colypos = g_new(gint, 1);
  881     colypos[0] = 0;
  882 
  883     for (children = cols->children;
  884          children && (child = children->data);
  885          children = children->next) {
  886         if (!child->widget) {
  887             /* Column reconfiguration. */
  888             for (i = 1; i < ncols; i++) {
  889                 if (colypos[0] < colypos[i])
  890                     colypos[0] = colypos[i];
  891             }
  892             ncols = child->ncols;
  893             colypos = g_renew(gint, colypos, ncols);
  894             for (i = 1; i < ncols; i++)
  895                 colypos[i] = colypos[0];
  896             continue;
  897         }
  898 
  899         /* Only take visible widgets into account. */
  900         if (!gtk_widget_get_visible(child->widget))
  901             continue;
  902 
  903         realheight = fakeheight = get_height(child);
  904         if (child->same_height_as) {
  905             gint childheight2 = get_height(child->same_height_as);
  906             if (fakeheight < childheight2)
  907                 fakeheight = childheight2;
  908         }
  909         colspan = child->colspan ? child->colspan : ncols-child->colstart;
  910 
  911         /*
  912          * To compute height: the widget's top will be positioned
  913          * at the largest y value so far reached in any of the
  914          * columns it crosses. Then it will go down by creq.height
  915          * plus padding; and the point it reaches at the bottom is
  916          * the new y value in all those columns.
  917          */
  918         {
  919             int topy, boty;
  920 
  921             topy = 0;
  922             for (i = 0; i < colspan; i++) {
  923                 if (topy < colypos[child->colstart+i])
  924                     topy = colypos[child->colstart+i];
  925             }
  926             child->y = topy + fakeheight/2 - realheight/2;
  927             child->h = realheight;
  928             boty = topy + fakeheight + cols->spacing;
  929             for (i = 0; i < colspan; i++) {
  930                 colypos[child->colstart+i] = boty;
  931             }
  932         }
  933     }
  934 
  935     g_free(colypos);
  936 }
  937 
  938 /*
  939  * Now here comes the interesting bit. The actual layout part is
  940  * done in the following two functions:
  941  *
  942  * columns_size_request() examines the list of widgets held in the
  943  * Columns, and returns a requisition stating the absolute minimum
  944  * size it can bear to be.
  945  *
  946  * columns_size_allocate() is given an allocation telling it what
  947  * size the whole container is going to be, and it calls
  948  * gtk_widget_size_allocate() on all of its (visible) children to
  949  * set their size and position relative to the top left of the
  950  * container.
  951  */
  952 
  953 #if !GTK_CHECK_VERSION(3,0,0)
  954 
  955 static gint columns_gtk2_get_width(ColumnsChild *child)
  956 {
  957     GtkRequisition creq;
  958     gtk_widget_size_request(child->widget, &creq);
  959     return creq.width;
  960 }
  961 
  962 static gint columns_gtk2_get_height(ColumnsChild *child)
  963 {
  964     GtkRequisition creq;
  965     gtk_widget_size_request(child->widget, &creq);
  966     return creq.height;
  967 }
  968 
  969 static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
  970 {
  971     Columns *cols;
  972 
  973     g_return_if_fail(widget != NULL);
  974     g_return_if_fail(IS_COLUMNS(widget));
  975     g_return_if_fail(req != NULL);
  976 
  977     cols = COLUMNS(widget);
  978 
  979     req->width = columns_compute_width(cols, columns_gtk2_get_width);
  980     req->height = columns_compute_height(cols, columns_gtk2_get_height);
  981 }
  982 
  983 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
  984 {
  985     Columns *cols;
  986     ColumnsChild *child;
  987     GList *children;
  988     gint border;
  989 
  990     g_return_if_fail(widget != NULL);
  991     g_return_if_fail(IS_COLUMNS(widget));
  992     g_return_if_fail(alloc != NULL);
  993 
  994     cols = COLUMNS(widget);
  995     gtk_widget_set_allocation(widget, alloc);
  996 
  997     border = gtk_container_get_border_width(GTK_CONTAINER(cols));
  998 
  999     columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
 1000     columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
 1001 
 1002     for (children = cols->children;
 1003          children && (child = children->data);
 1004          children = children->next) {
 1005         if (child->widget && gtk_widget_get_visible(child->widget)) {
 1006             GtkAllocation call;
 1007             call.x = alloc->x + border + child->x;
 1008             call.y = alloc->y + border + child->y;
 1009             call.width = child->w;
 1010             call.height = child->h;
 1011             gtk_widget_size_allocate(child->widget, &call);
 1012         }
 1013     }
 1014 }
 1015 
 1016 #else /* GTK_CHECK_VERSION(3,0,0) */
 1017 
 1018 static gint columns_gtk3_get_min_width(ColumnsChild *child)
 1019 {
 1020     gint ret;
 1021     gtk_widget_get_preferred_width(child->widget, &ret, NULL);
 1022     return ret;
 1023 }
 1024 
 1025 static gint columns_gtk3_get_nat_width(ColumnsChild *child)
 1026 {
 1027     gint ret;
 1028 
 1029     if ((GTK_IS_LABEL(child->widget) &&
 1030          gtk_label_get_line_wrap(GTK_LABEL(child->widget))) ||
 1031         GTK_IS_ENTRY(child->widget)) {
 1032         /*
 1033          * We treat wrapping GtkLabels as a special case in this
 1034          * layout class, because the whole point of those is that I
 1035          * _don't_ want them to take up extra horizontal space for
 1036          * long text, but instead to wrap it to whatever size is used
 1037          * by the rest of the layout.
 1038          *
 1039          * GtkEntry gets similar treatment, because in OS X GTK I've
 1040          * found that it requests a natural width regardless of the
 1041          * output of gtk_entry_set_width_chars.
 1042          */
 1043         gtk_widget_get_preferred_width(child->widget, &ret, NULL);
 1044     } else {
 1045         gtk_widget_get_preferred_width(child->widget, NULL, &ret);
 1046     }
 1047     return ret;
 1048 }
 1049 
 1050 static gint columns_gtk3_get_minfh_width(ColumnsChild *child)
 1051 {
 1052     gint ret;
 1053     gtk_widget_get_preferred_width_for_height(child->widget, child->h,
 1054                                               &ret, NULL);
 1055     return ret;
 1056 }
 1057 
 1058 static gint columns_gtk3_get_natfh_width(ColumnsChild *child)
 1059 {
 1060     gint ret;
 1061     gtk_widget_get_preferred_width_for_height(child->widget, child->h,
 1062                                               NULL, &ret);
 1063     return ret;
 1064 }
 1065 
 1066 static gint columns_gtk3_get_min_height(ColumnsChild *child)
 1067 {
 1068     gint ret;
 1069     gtk_widget_get_preferred_height(child->widget, &ret, NULL);
 1070     return ret;
 1071 }
 1072 
 1073 static gint columns_gtk3_get_nat_height(ColumnsChild *child)
 1074 {
 1075     gint ret;
 1076     gtk_widget_get_preferred_height(child->widget, NULL, &ret);
 1077     return ret;
 1078 }
 1079 
 1080 static gint columns_gtk3_get_minfw_height(ColumnsChild *child)
 1081 {
 1082     gint ret;
 1083     gtk_widget_get_preferred_height_for_width(child->widget, child->w,
 1084                                               &ret, NULL);
 1085     return ret;
 1086 }
 1087 
 1088 static gint columns_gtk3_get_natfw_height(ColumnsChild *child)
 1089 {
 1090     gint ret;
 1091     gtk_widget_get_preferred_height_for_width(child->widget, child->w,
 1092                                               NULL, &ret);
 1093     return ret;
 1094 }
 1095 
 1096 static void columns_get_preferred_width(GtkWidget *widget,
 1097                                         gint *min, gint *nat)
 1098 {
 1099     Columns *cols;
 1100 
 1101     g_return_if_fail(widget != NULL);
 1102     g_return_if_fail(IS_COLUMNS(widget));
 1103 
 1104     cols = COLUMNS(widget);
 1105 
 1106     if (min)
 1107         *min = columns_compute_width(cols, columns_gtk3_get_min_width);
 1108     if (nat)
 1109         *nat = columns_compute_width(cols, columns_gtk3_get_nat_width);
 1110 }
 1111 
 1112 static void columns_get_preferred_height(GtkWidget *widget,
 1113                                          gint *min, gint *nat)
 1114 {
 1115     Columns *cols;
 1116 
 1117     g_return_if_fail(widget != NULL);
 1118     g_return_if_fail(IS_COLUMNS(widget));
 1119 
 1120     cols = COLUMNS(widget);
 1121 
 1122     if (min)
 1123         *min = columns_compute_height(cols, columns_gtk3_get_min_height);
 1124     if (nat)
 1125         *nat = columns_compute_height(cols, columns_gtk3_get_nat_height);
 1126 }
 1127 
 1128 static void columns_get_preferred_width_for_height(GtkWidget *widget,
 1129                                                    gint height,
 1130                                                    gint *min, gint *nat)
 1131 {
 1132     Columns *cols;
 1133 
 1134     g_return_if_fail(widget != NULL);
 1135     g_return_if_fail(IS_COLUMNS(widget));
 1136 
 1137     cols = COLUMNS(widget);
 1138 
 1139     /* FIXME: which one should the get-height function here be? */
 1140     columns_alloc_vert(cols, height, columns_gtk3_get_nat_height);
 1141 
 1142     if (min)
 1143         *min = columns_compute_width(cols, columns_gtk3_get_minfh_width);
 1144     if (nat)
 1145         *nat = columns_compute_width(cols, columns_gtk3_get_natfh_width);
 1146 }
 1147 
 1148 static void columns_get_preferred_height_for_width(GtkWidget *widget,
 1149                                                    gint width,
 1150                                                    gint *min, gint *nat)
 1151 {
 1152     Columns *cols;
 1153 
 1154     g_return_if_fail(widget != NULL);
 1155     g_return_if_fail(IS_COLUMNS(widget));
 1156 
 1157     cols = COLUMNS(widget);
 1158 
 1159     /* FIXME: which one should the get-height function here be? */
 1160     columns_alloc_horiz(cols, width, columns_gtk3_get_nat_width);
 1161 
 1162     if (min)
 1163         *min = columns_compute_height(cols, columns_gtk3_get_minfw_height);
 1164     if (nat)
 1165         *nat = columns_compute_height(cols, columns_gtk3_get_natfw_height);
 1166 }
 1167 
 1168 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
 1169 {
 1170     Columns *cols;
 1171     ColumnsChild *child;
 1172     GList *children;
 1173     gint border;
 1174 
 1175     g_return_if_fail(widget != NULL);
 1176     g_return_if_fail(IS_COLUMNS(widget));
 1177     g_return_if_fail(alloc != NULL);
 1178 
 1179     cols = COLUMNS(widget);
 1180     gtk_widget_set_allocation(widget, alloc);
 1181 
 1182     border = gtk_container_get_border_width(GTK_CONTAINER(cols));
 1183 
 1184     columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width);
 1185     columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height);
 1186 
 1187     for (children = cols->children;
 1188          children && (child = children->data);
 1189          children = children->next) {
 1190         if (child->widget && gtk_widget_get_visible(child->widget)) {
 1191             GtkAllocation call;
 1192             call.x = alloc->x + border + child->x;
 1193             call.y = alloc->y + border + child->y;
 1194             call.width = child->w;
 1195             call.height = child->h;
 1196             gtk_widget_size_allocate(child->widget, &call);
 1197         }
 1198     }
 1199 }
 1200 
 1201 #endif