]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - unix/gtkcols.c
Merge Ben's branch on which he's been fuzzing PuTTY.
[PuTTY.git] / unix / gtkcols.c
index e989106369d3db3a879bdce0b5e5631e61693c7d..e8223a726e3bab89fb6357419ccf4807632dddb1 100644 (file)
@@ -27,8 +27,15 @@ static void columns_get_preferred_width(GtkWidget *widget,
                                         gint *min, gint *nat);
 static void columns_get_preferred_height(GtkWidget *widget,
                                          gint *min, gint *nat);
-#endif
+static void columns_get_preferred_width_for_height(GtkWidget *widget,
+                                                   gint height,
+                                                   gint *min, gint *nat);
+static void columns_get_preferred_height_for_width(GtkWidget *widget,
+                                                   gint width,
+                                                   gint *min, gint *nat);
+#else
 static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
+#endif
 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
 
 static GtkContainerClass *parent_class = NULL;
@@ -113,6 +120,10 @@ static void columns_class_init(ColumnsClass *klass)
 #if GTK_CHECK_VERSION(3,0,0)
     widget_class->get_preferred_width = columns_get_preferred_width;
     widget_class->get_preferred_height = columns_get_preferred_height;
+    widget_class->get_preferred_width_for_height =
+        columns_get_preferred_width_for_height;
+    widget_class->get_preferred_height_for_width =
+        columns_get_preferred_height_for_width;
 #else
     widget_class->size_request = columns_size_request;
 #endif
@@ -279,6 +290,14 @@ static void columns_remove(GtkContainer *container, GtkWidget *widget)
         gtk_widget_unparent(widget);
         cols->children = g_list_remove_link(cols->children, children);
         g_list_free(children);
+
+        if (child->same_height_as) {
+            g_return_if_fail(child->same_height_as->same_height_as == child);
+            child->same_height_as->same_height_as = NULL;
+            if (gtk_widget_get_visible(child->same_height_as->widget))
+                gtk_widget_queue_resize(GTK_WIDGET(container));
+        }
+
         g_free(child);
         if (was_visible)
             gtk_widget_queue_resize(GTK_WIDGET(container));
@@ -386,6 +405,7 @@ void columns_add(Columns *cols, GtkWidget *child,
     childdata->colstart = colstart;
     childdata->colspan = colspan;
     childdata->force_left = FALSE;
+    childdata->same_height_as = NULL;
 
     cols->children = g_list_append(cols->children, childdata);
     cols->taborder = g_list_append(cols->taborder, child);
@@ -407,26 +427,56 @@ void columns_add(Columns *cols, GtkWidget *child,
     }
 }
 
+static ColumnsChild *columns_find_child(Columns *cols, GtkWidget *widget)
+{
+    GList *children;
+    ColumnsChild *child;
+
+    for (children = cols->children;
+         children && (child = children->data);
+         children = children->next) {
+
+        if (child->widget == widget)
+            return child;
+    }
+
+    return NULL;
+}
+
 void columns_force_left_align(Columns *cols, GtkWidget *widget)
 {
     ColumnsChild *child;
-    GList *children;
 
     g_return_if_fail(cols != NULL);
     g_return_if_fail(IS_COLUMNS(cols));
     g_return_if_fail(widget != NULL);
 
-    for (children = cols->children;
-         children && (child = children->data);
-         children = children->next) {
-        if (child->widget != widget)
-            continue;
+    child = columns_find_child(cols, widget);
+    g_return_if_fail(child != NULL);
 
-       child->force_left = TRUE;
-        if (gtk_widget_get_visible(widget))
-            gtk_widget_queue_resize(GTK_WIDGET(cols));
-        break;
-    }
+    child->force_left = TRUE;
+    if (gtk_widget_get_visible(widget))
+        gtk_widget_queue_resize(GTK_WIDGET(cols));
+}
+
+void columns_force_same_height(Columns *cols, GtkWidget *cw1, GtkWidget *cw2)
+{
+    ColumnsChild *child1, *child2;
+
+    g_return_if_fail(cols != NULL);
+    g_return_if_fail(IS_COLUMNS(cols));
+    g_return_if_fail(cw1 != NULL);
+    g_return_if_fail(cw2 != NULL);
+
+    child1 = columns_find_child(cols, cw1);
+    g_return_if_fail(child1 != NULL);
+    child2 = columns_find_child(cols, cw2);
+    g_return_if_fail(child2 != NULL);
+
+    child1->same_height_as = child2;
+    child2->same_height_as = child1;
+    if (gtk_widget_get_visible(cw1) || gtk_widget_get_visible(cw2))
+        gtk_widget_queue_resize(GTK_WIDGET(cols));
 }
 
 void columns_taborder_last(Columns *cols, GtkWidget *widget)
@@ -538,6 +588,10 @@ static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
     const gint *percentages;
     static const gint onecol[] = { 100 };
 
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+    printf("compute_width(%p): start\n", cols);
+#endif
+
     retwidth = 0;
 
     ncols = 1;
@@ -561,6 +615,23 @@ static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
         childwidth = get_width(child);
        colspan = child->colspan ? child->colspan : ncols-child->colstart;
 
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+        printf("compute_width(%p): ", cols);
+        if (GTK_IS_LABEL(child->widget))
+            printf("label %p '%s' wrap=%s: ", child->widget,
+                   gtk_label_get_text(GTK_LABEL(child->widget)),
+                   (gtk_label_get_line_wrap(GTK_LABEL(child->widget))
+                    ? "TRUE" : "FALSE"));
+        else
+            printf("widget %p: ", child->widget);
+        {
+            gint min, nat;
+            gtk_widget_get_preferred_width(child->widget, &min, &nat);
+            printf("minwidth=%d natwidth=%d ", min, nat);
+        }
+        printf("thiswidth=%d span=%d\n", childwidth, colspan);
+#endif
+
         /*
          * To compute width: we know that childwidth + cols->spacing
          * needs to equal a certain percentage of the full width of
@@ -585,6 +656,10 @@ static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
              * before dividing by percent.
              */
             fullwid = (thiswid * 100 + percent - 1) / percent;
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+            printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n",
+                   cols, child->widget, thiswid, fullwid);
+#endif
 
             /*
              * The above calculation assumes every widget gets
@@ -599,6 +674,10 @@ static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
 
     retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
 
+#ifdef COLUMNS_WIDTH_DIAGNOSTICS
+    printf("compute_width(%p): done, returning %d\n", cols, retwidth);
+#endif
+
     return retwidth;
 }
 
@@ -703,6 +782,11 @@ static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
             continue;
 
         childheight = get_height(child);
+        if (child->same_height_as) {
+            gint childheight2 = get_height(child->same_height_as);
+            if (childheight < childheight2)
+                childheight = childheight2;
+        }
        colspan = child->colspan ? child->colspan : ncols-child->colstart;
 
         /*
@@ -743,7 +827,7 @@ static void columns_alloc_vert(Columns *cols, gint ourheight,
 {
     ColumnsChild *child;
     GList *children;
-    gint i, ncols, colspan, *colypos, childheight;
+    gint i, ncols, colspan, *colypos, realheight, fakeheight;
 
     ncols = 1;
     /* As in size_request, colypos is the lowest y reached in each column. */
@@ -770,7 +854,12 @@ static void columns_alloc_vert(Columns *cols, gint ourheight,
         if (!gtk_widget_get_visible(child->widget))
             continue;
 
-        childheight = get_height(child);
+        realheight = fakeheight = get_height(child);
+        if (child->same_height_as) {
+            gint childheight2 = get_height(child->same_height_as);
+            if (fakeheight < childheight2)
+                fakeheight = childheight2;
+        }
        colspan = child->colspan ? child->colspan : ncols-child->colstart;
 
         /*
@@ -788,9 +877,9 @@ static void columns_alloc_vert(Columns *cols, gint ourheight,
                 if (topy < colypos[child->colstart+i])
                     topy = colypos[child->colstart+i];
             }
-            child->y = topy;
-            child->h = childheight;
-            boty = topy + childheight + cols->spacing;
+            child->y = topy + fakeheight/2 - realheight/2;
+            child->h = realheight;
+            boty = topy + fakeheight + cols->spacing;
             for (i = 0; i < colspan; i++) {
                 colypos[child->colstart+i] = boty;
             }
@@ -815,6 +904,8 @@ static void columns_alloc_vert(Columns *cols, gint ourheight,
  * container.
  */
 
+#if !GTK_CHECK_VERSION(3,0,0)
+
 static gint columns_gtk2_get_width(ColumnsChild *child)
 {
     GtkRequisition creq;
@@ -843,23 +934,190 @@ static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
     req->height = columns_compute_height(cols, columns_gtk2_get_height);
 }
 
-#if GTK_CHECK_VERSION(3,0,0)
+static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
+{
+    Columns *cols;
+    ColumnsChild *child;
+    GList *children;
+    gint border;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+    g_return_if_fail(alloc != NULL);
+
+    cols = COLUMNS(widget);
+    gtk_widget_set_allocation(widget, alloc);
+
+    border = gtk_container_get_border_width(GTK_CONTAINER(cols));
+
+    columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
+    columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
+
+    for (children = cols->children;
+         children && (child = children->data);
+         children = children->next) {
+        if (child->widget && gtk_widget_get_visible(child->widget)) {
+            GtkAllocation call;
+            call.x = alloc->x + border + child->x;
+            call.y = alloc->y + border + child->y;
+            call.width = child->w;
+            call.height = child->h;
+            gtk_widget_size_allocate(child->widget, &call);
+        }
+    }
+}
+
+#else /* GTK_CHECK_VERSION(3,0,0) */
+
+static gint columns_gtk3_get_min_width(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_width(child->widget, &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_nat_width(ColumnsChild *child)
+{
+    gint ret;
+
+    if ((GTK_IS_LABEL(child->widget) &&
+         gtk_label_get_line_wrap(GTK_LABEL(child->widget))) ||
+        GTK_IS_ENTRY(child->widget)) {
+        /*
+         * We treat wrapping GtkLabels as a special case in this
+         * layout class, because the whole point of those is that I
+         * _don't_ want them to take up extra horizontal space for
+         * long text, but instead to wrap it to whatever size is used
+         * by the rest of the layout.
+         *
+         * GtkEntry gets similar treatment, because in OS X GTK I've
+         * found that it requests a natural width regardless of the
+         * output of gtk_entry_set_width_chars.
+         */
+        gtk_widget_get_preferred_width(child->widget, &ret, NULL);
+    } else {
+        gtk_widget_get_preferred_width(child->widget, NULL, &ret);
+    }
+    return ret;
+}
+
+static gint columns_gtk3_get_minfh_width(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_width_for_height(child->widget, child->h,
+                                              &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_natfh_width(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_width_for_height(child->widget, child->h,
+                                              NULL, &ret);
+    return ret;
+}
+
+static gint columns_gtk3_get_min_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height(child->widget, &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_nat_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height(child->widget, NULL, &ret);
+    return ret;
+}
+
+static gint columns_gtk3_get_minfw_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height_for_width(child->widget, child->w,
+                                              &ret, NULL);
+    return ret;
+}
+
+static gint columns_gtk3_get_natfw_height(ColumnsChild *child)
+{
+    gint ret;
+    gtk_widget_get_preferred_height_for_width(child->widget, child->w,
+                                              NULL, &ret);
+    return ret;
+}
+
 static void columns_get_preferred_width(GtkWidget *widget,
                                         gint *min, gint *nat)
 {
-    GtkRequisition req;
-    columns_size_request(widget, &req);
-    *min = *nat = req.width;
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    if (min)
+        *min = columns_compute_width(cols, columns_gtk3_get_min_width);
+    if (nat)
+        *nat = columns_compute_width(cols, columns_gtk3_get_nat_width);
 }
 
 static void columns_get_preferred_height(GtkWidget *widget,
-                                        gint *min, gint *nat)
+                                         gint *min, gint *nat)
 {
-    GtkRequisition req;
-    columns_size_request(widget, &req);
-    *min = *nat = req.height;
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    if (min)
+        *min = columns_compute_height(cols, columns_gtk3_get_min_height);
+    if (nat)
+        *nat = columns_compute_height(cols, columns_gtk3_get_nat_height);
+}
+
+static void columns_get_preferred_width_for_height(GtkWidget *widget,
+                                                   gint height,
+                                                   gint *min, gint *nat)
+{
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    /* FIXME: which one should the get-height function here be? */
+    columns_alloc_vert(cols, height, columns_gtk3_get_nat_height);
+
+    if (min)
+        *min = columns_compute_width(cols, columns_gtk3_get_minfh_width);
+    if (nat)
+        *nat = columns_compute_width(cols, columns_gtk3_get_natfh_width);
+}
+
+static void columns_get_preferred_height_for_width(GtkWidget *widget,
+                                                   gint width,
+                                                   gint *min, gint *nat)
+{
+    Columns *cols;
+
+    g_return_if_fail(widget != NULL);
+    g_return_if_fail(IS_COLUMNS(widget));
+
+    cols = COLUMNS(widget);
+
+    /* FIXME: which one should the get-height function here be? */
+    columns_alloc_horiz(cols, width, columns_gtk3_get_nat_width);
+
+    if (min)
+        *min = columns_compute_height(cols, columns_gtk3_get_minfw_height);
+    if (nat)
+        *nat = columns_compute_height(cols, columns_gtk3_get_natfw_height);
 }
-#endif
 
 static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
 {
@@ -877,8 +1135,8 @@ static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
 
     border = gtk_container_get_border_width(GTK_CONTAINER(cols));
 
-    columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width);
-    columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height);
+    columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width);
+    columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height);
 
     for (children = cols->children;
          children && (child = children->data);
@@ -893,3 +1151,5 @@ static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc)
         }
     }
 }
+
+#endif