2003-03-13 19:52:28 +00:00
|
|
|
/*
|
|
|
|
* gtkcols.c - implementation of the `Columns' GTK layout container.
|
|
|
|
*/
|
|
|
|
|
2018-12-01 14:13:37 +00:00
|
|
|
#include <assert.h>
|
2007-01-25 19:33:29 +00:00
|
|
|
#include <gtk/gtk.h>
|
2018-10-29 19:50:29 +00:00
|
|
|
#include "defs.h"
|
2015-08-08 14:53:43 +00:00
|
|
|
#include "gtkcompat.h"
|
2021-04-23 05:19:05 +00:00
|
|
|
#include "columns.h"
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-10-28 09:14:53 +00:00
|
|
|
#if GTK_CHECK_VERSION(2,0,0)
|
|
|
|
/* The "focus" method lives in GtkWidget from GTK 2 onwards, but it
|
|
|
|
* was in GtkContainer in GTK 1 */
|
|
|
|
#define FOCUS_METHOD_SUPERCLASS GtkWidget
|
|
|
|
#define FOCUS_METHOD_LOCATION widget_class /* used in columns_init */
|
|
|
|
#define CHILD_FOCUS(cont, dir) gtk_widget_child_focus(GTK_WIDGET(cont), dir)
|
|
|
|
#else
|
|
|
|
#define FOCUS_METHOD_SUPERCLASS GtkContainer
|
|
|
|
#define FOCUS_METHOD_LOCATION container_class
|
|
|
|
#define CHILD_FOCUS(cont, dir) gtk_container_focus(GTK_CONTAINER(cont), dir)
|
|
|
|
#endif
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
static void columns_init(Columns *cols);
|
|
|
|
static void columns_class_init(ColumnsClass *klass);
|
2017-11-26 08:45:45 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
static void columns_finalize(GtkObject *object);
|
|
|
|
#else
|
|
|
|
static void columns_finalize(GObject *object);
|
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
static void columns_map(GtkWidget *widget);
|
|
|
|
static void columns_unmap(GtkWidget *widget);
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-03-13 19:52:28 +00:00
|
|
|
static void columns_draw(GtkWidget *widget, GdkRectangle *area);
|
|
|
|
static gint columns_expose(GtkWidget *widget, GdkEventExpose *event);
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
static void columns_base_add(GtkContainer *container, GtkWidget *widget);
|
|
|
|
static void columns_remove(GtkContainer *container, GtkWidget *widget);
|
|
|
|
static void columns_forall(GtkContainer *container, gboolean include_internals,
|
|
|
|
GtkCallback callback, gpointer callback_data);
|
2018-10-28 09:14:53 +00:00
|
|
|
static gint columns_focus(FOCUS_METHOD_SUPERCLASS *container,
|
|
|
|
GtkDirectionType dir);
|
2015-08-08 16:29:02 +00:00
|
|
|
static GType columns_child_type(GtkContainer *container);
|
2015-08-16 13:16:08 +00:00
|
|
|
#if GTK_CHECK_VERSION(3,0,0)
|
|
|
|
static void columns_get_preferred_width(GtkWidget *widget,
|
|
|
|
gint *min, gint *nat);
|
|
|
|
static void columns_get_preferred_height(GtkWidget *widget,
|
|
|
|
gint *min, gint *nat);
|
2015-08-23 13:50:47 +00:00
|
|
|
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
|
2003-03-13 19:52:28 +00:00
|
|
|
static void columns_size_request(GtkWidget *widget, GtkRequisition *req);
|
2015-08-23 13:50:47 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc);
|
|
|
|
|
|
|
|
static GtkContainerClass *parent_class = NULL;
|
|
|
|
|
2008-04-02 17:04:21 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2015-08-08 16:29:02 +00:00
|
|
|
GType columns_get_type(void)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
2015-08-08 16:29:02 +00:00
|
|
|
static GType columns_type = 0;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
if (!columns_type) {
|
|
|
|
static const GtkTypeInfo columns_info = {
|
|
|
|
"Columns",
|
|
|
|
sizeof(Columns),
|
|
|
|
sizeof(ColumnsClass),
|
|
|
|
(GtkClassInitFunc) columns_class_init,
|
|
|
|
(GtkObjectInitFunc) columns_init,
|
|
|
|
/* reserved_1 */ NULL,
|
|
|
|
/* reserved_2 */ NULL,
|
|
|
|
(GtkClassInitFunc) NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info);
|
|
|
|
}
|
|
|
|
|
|
|
|
return columns_type;
|
|
|
|
}
|
2008-04-02 17:04:21 +00:00
|
|
|
#else
|
|
|
|
GType columns_get_type(void)
|
|
|
|
{
|
|
|
|
static GType columns_type = 0;
|
|
|
|
|
|
|
|
if (!columns_type) {
|
|
|
|
static const GTypeInfo columns_info = {
|
|
|
|
sizeof(ColumnsClass),
|
2019-09-08 19:29:00 +00:00
|
|
|
NULL,
|
|
|
|
NULL,
|
2008-04-02 17:04:21 +00:00
|
|
|
(GClassInitFunc) columns_class_init,
|
2019-09-08 19:29:00 +00:00
|
|
|
NULL,
|
|
|
|
NULL,
|
2008-04-02 17:04:21 +00:00
|
|
|
sizeof(Columns),
|
2019-09-08 19:29:00 +00:00
|
|
|
0,
|
2008-04-02 17:04:21 +00:00
|
|
|
(GInstanceInitFunc)columns_init,
|
|
|
|
};
|
|
|
|
|
|
|
|
columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns",
|
2019-09-08 19:29:00 +00:00
|
|
|
&columns_info, 0);
|
2008-04-02 17:04:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return columns_type;
|
|
|
|
}
|
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-10-28 09:14:53 +00:00
|
|
|
static gint (*columns_inherited_focus)(FOCUS_METHOD_SUPERCLASS *container,
|
2019-09-08 19:29:00 +00:00
|
|
|
GtkDirectionType direction);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
static void columns_class_init(ColumnsClass *klass)
|
|
|
|
{
|
2008-04-02 17:04:21 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2017-11-26 08:45:45 +00:00
|
|
|
GtkObjectClass *object_class = (GtkObjectClass *)klass;
|
2008-04-02 17:04:21 +00:00
|
|
|
GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
|
|
|
|
GtkContainerClass *container_class = (GtkContainerClass *)klass;
|
|
|
|
#else
|
2017-11-26 08:45:45 +00:00
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS(klass);
|
2008-04-02 17:04:21 +00:00
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
|
|
|
|
GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass);
|
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2008-04-02 17:04:21 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-03-13 19:52:28 +00:00
|
|
|
parent_class = gtk_type_class(GTK_TYPE_CONTAINER);
|
2008-04-04 10:56:26 +00:00
|
|
|
#else
|
|
|
|
parent_class = g_type_class_peek_parent(klass);
|
2008-04-02 17:04:21 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2017-11-26 08:45:45 +00:00
|
|
|
object_class->finalize = columns_finalize;
|
2003-03-13 19:52:28 +00:00
|
|
|
widget_class->map = columns_map;
|
|
|
|
widget_class->unmap = columns_unmap;
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-03-13 19:52:28 +00:00
|
|
|
widget_class->draw = columns_draw;
|
|
|
|
widget_class->expose_event = columns_expose;
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2015-08-16 13:16:08 +00:00
|
|
|
#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;
|
2015-08-23 13:50:47 +00:00
|
|
|
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;
|
2015-08-16 13:16:08 +00:00
|
|
|
#else
|
2003-03-13 19:52:28 +00:00
|
|
|
widget_class->size_request = columns_size_request;
|
2015-08-16 13:16:08 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
widget_class->size_allocate = columns_size_allocate;
|
|
|
|
|
|
|
|
container_class->add = columns_base_add;
|
|
|
|
container_class->remove = columns_remove;
|
|
|
|
container_class->forall = columns_forall;
|
|
|
|
container_class->child_type = columns_child_type;
|
2018-10-28 09:14:53 +00:00
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
/* Save the previous value of this method. */
|
|
|
|
if (!columns_inherited_focus)
|
2019-09-08 19:29:00 +00:00
|
|
|
columns_inherited_focus = FOCUS_METHOD_LOCATION->focus;
|
2018-10-28 09:14:53 +00:00
|
|
|
FOCUS_METHOD_LOCATION->focus = columns_focus;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void columns_init(Columns *cols)
|
|
|
|
{
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_widget_set_has_window(GTK_WIDGET(cols), false);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
cols->children = NULL;
|
|
|
|
cols->spacing = 0;
|
|
|
|
}
|
|
|
|
|
2017-11-26 08:45:45 +00:00
|
|
|
static void columns_child_free(gpointer vchild)
|
|
|
|
{
|
|
|
|
ColumnsChild *child = (ColumnsChild *)vchild;
|
|
|
|
if (child->percentages)
|
|
|
|
g_free(child->percentages);
|
|
|
|
g_free(child);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void columns_finalize(
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
GtkObject *object
|
|
|
|
#else
|
|
|
|
GObject *object
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
|
|
|
|
g_return_if_fail(object != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(object));
|
|
|
|
|
|
|
|
cols = COLUMNS(object);
|
|
|
|
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
{
|
|
|
|
GList *node;
|
|
|
|
for (node = cols->children; node; node = node->next)
|
|
|
|
if (node->data)
|
|
|
|
columns_child_free(node->data);
|
|
|
|
}
|
|
|
|
g_list_free(cols->children);
|
|
|
|
#else
|
|
|
|
g_list_free_full(cols->children, columns_child_free);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
cols->children = NULL;
|
|
|
|
|
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
|
|
|
GTK_OBJECT_CLASS(parent_class)->finalize(object);
|
|
|
|
#else
|
|
|
|
G_OBJECT_CLASS(parent_class)->finalize(object);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
/*
|
|
|
|
* These appear to be thoroughly tedious functions; the only reason
|
|
|
|
* we have to reimplement them at all is because we defined our own
|
|
|
|
* format for our GList of children...
|
|
|
|
*/
|
|
|
|
static void columns_map(GtkWidget *widget)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
|
|
|
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(widget));
|
|
|
|
|
|
|
|
cols = COLUMNS(widget);
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_widget_set_mapped(GTK_WIDGET(cols), true);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
if (child->widget &&
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_get_visible(child->widget) &&
|
2015-08-08 14:53:43 +00:00
|
|
|
!gtk_widget_get_mapped(child->widget))
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_map(child->widget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void columns_unmap(GtkWidget *widget)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
|
|
|
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(widget));
|
|
|
|
|
|
|
|
cols = COLUMNS(widget);
|
2018-10-29 19:50:29 +00:00
|
|
|
gtk_widget_set_mapped(GTK_WIDGET(cols), false);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
if (child->widget &&
|
2019-09-08 19:29:00 +00:00
|
|
|
gtk_widget_get_visible(child->widget) &&
|
2015-08-08 14:53:43 +00:00
|
|
|
gtk_widget_get_mapped(child->widget))
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_unmap(child->widget);
|
|
|
|
}
|
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-03-13 19:52:28 +00:00
|
|
|
static void columns_draw(GtkWidget *widget, GdkRectangle *area)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
|
|
|
GdkRectangle child_area;
|
|
|
|
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(widget));
|
|
|
|
|
|
|
|
if (GTK_WIDGET_DRAWABLE(widget)) {
|
|
|
|
cols = COLUMNS(widget);
|
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
if (child->widget &&
|
2019-09-08 19:29:00 +00:00
|
|
|
GTK_WIDGET_DRAWABLE(child->widget) &&
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_intersect(child->widget, area, &child_area))
|
|
|
|
gtk_widget_draw(child->widget, &child_area);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static gint columns_expose(GtkWidget *widget, GdkEventExpose *event)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
|
|
|
GdkEventExpose child_event;
|
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
g_return_val_if_fail(widget != NULL, false);
|
|
|
|
g_return_val_if_fail(IS_COLUMNS(widget), false);
|
|
|
|
g_return_val_if_fail(event != NULL, false);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
if (GTK_WIDGET_DRAWABLE(widget)) {
|
|
|
|
cols = COLUMNS(widget);
|
|
|
|
child_event = *event;
|
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
if (child->widget &&
|
2019-09-08 19:29:00 +00:00
|
|
|
GTK_WIDGET_DRAWABLE(child->widget) &&
|
2003-03-13 19:52:28 +00:00
|
|
|
GTK_WIDGET_NO_WINDOW(child->widget) &&
|
|
|
|
gtk_widget_intersect(child->widget, &event->area,
|
|
|
|
&child_event.area))
|
|
|
|
gtk_widget_event(child->widget, (GdkEvent *)&child_event);
|
|
|
|
}
|
|
|
|
}
|
2018-10-29 19:50:29 +00:00
|
|
|
return false;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
2007-01-25 19:33:29 +00:00
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
static void columns_base_add(GtkContainer *container, GtkWidget *widget)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
|
|
|
|
g_return_if_fail(container != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(container));
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
|
|
|
|
cols = COLUMNS(container);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Default is to add a new widget spanning all columns.
|
|
|
|
*/
|
|
|
|
columns_add(cols, widget, 0, 0); /* 0 means ncols */
|
|
|
|
}
|
|
|
|
|
|
|
|
static void columns_remove(GtkContainer *container, GtkWidget *widget)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
ColumnsChild *child;
|
|
|
|
GtkWidget *childw;
|
|
|
|
GList *children;
|
Convert a lot of 'int' variables to 'bool'.
My normal habit these days, in new code, is to treat int and bool as
_almost_ completely separate types. I'm still willing to use C's
implicit test for zero on an integer (e.g. 'if (!blob.len)' is fine,
no need to spell it out as blob.len != 0), but generally, if a
variable is going to be conceptually a boolean, I like to declare it
bool and assign to it using 'true' or 'false' rather than 0 or 1.
PuTTY is an exception, because it predates the C99 bool, and I've
stuck to its existing coding style even when adding new code to it.
But it's been annoying me more and more, so now that I've decided C99
bool is an acceptable thing to require from our toolchain in the first
place, here's a quite thorough trawl through the source doing
'boolification'. Many variables and function parameters are now typed
as bool rather than int; many assignments of 0 or 1 to those variables
are now spelled 'true' or 'false'.
I managed this thorough conversion with the help of a custom clang
plugin that I wrote to trawl the AST and apply heuristics to point out
where things might want changing. So I've even managed to do a decent
job on parts of the code I haven't looked at in years!
To make the plugin's work easier, I pushed platform front ends
generally in the direction of using standard 'bool' in preference to
platform-specific boolean types like Windows BOOL or GTK's gboolean;
I've left the platform booleans in places they _have_ to be for the
platform APIs to work right, but variables only used by my own code
have been converted wherever I found them.
In a few places there are int values that look very like booleans in
_most_ of the places they're used, but have a rarely-used third value,
or a distinction between different nonzero values that most users
don't care about. In these cases, I've _removed_ uses of 'true' and
'false' for the return values, to emphasise that there's something
more subtle going on than a simple boolean answer:
- the 'multisel' field in dialog.h's list box structure, for which
the GTK front end in particular recognises a difference between 1
and 2 but nearly everything else treats as boolean
- the 'urgent' parameter to plug_receive, where 1 vs 2 tells you
something about the specific location of the urgent pointer, but
most clients only care about 0 vs 'something nonzero'
- the return value of wc_match, where -1 indicates a syntax error in
the wildcard.
- the return values from SSH-1 RSA-key loading functions, which use
-1 for 'wrong passphrase' and 0 for all other failures (so any
caller which already knows it's not loading an _encrypted private_
key can treat them as boolean)
- term->esc_query, and the 'query' parameter in toggle_mode in
terminal.c, which _usually_ hold 0 for ESC[123h or 1 for ESC[?123h,
but can also hold -1 for some other intervening character that we
don't support.
In a few places there's an integer that I haven't turned into a bool
even though it really _can_ only take values 0 or 1 (and, as above,
tried to make the call sites consistent in not calling those values
true and false), on the grounds that I thought it would make it more
confusing to imply that the 0 value was in some sense 'negative' or
bad and the 1 positive or good:
- the return value of plug_accepting uses the POSIXish convention of
0=success and nonzero=error; I think if I made it bool then I'd
also want to reverse its sense, and that's a job for a separate
piece of work.
- the 'screen' parameter to lineptr() in terminal.c, where 0 and 1
represent the default and alternate screens. There's no obvious
reason why one of those should be considered 'true' or 'positive'
or 'success' - they're just indices - so I've left it as int.
ssh_scp_recv had particularly confusing semantics for its previous int
return value: its call sites used '<= 0' to check for error, but it
never actually returned a negative number, just 0 or 1. Now the
function and its call sites agree that it's a bool.
In a couple of places I've renamed variables called 'ret', because I
don't like that name any more - it's unclear whether it means the
return value (in preparation) for the _containing_ function or the
return value received from a subroutine call, and occasionally I've
accidentally used the same variable for both and introduced a bug. So
where one of those got in my way, I've renamed it to 'toret' or 'retd'
(the latter short for 'returned') in line with my usual modern
practice, but I haven't done a thorough job of finding all of them.
Finally, one amusing side effect of doing this is that I've had to
separate quite a few chained assignments. It used to be perfectly fine
to write 'a = b = c = TRUE' when a,b,c were int and TRUE was just a
the 'true' defined by stdbool.h, that idiom provokes a warning from
gcc: 'suggest parentheses around assignment used as truth value'!
2018-11-02 19:23:19 +00:00
|
|
|
bool was_visible;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
g_return_if_fail(container != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(container));
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
|
|
|
|
cols = COLUMNS(container);
|
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
if (child->widget != widget)
|
|
|
|
continue;
|
|
|
|
|
2015-08-08 14:53:43 +00:00
|
|
|
was_visible = gtk_widget_get_visible(widget);
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_unparent(widget);
|
|
|
|
cols->children = g_list_remove_link(cols->children, children);
|
|
|
|
g_list_free(children);
|
2015-08-24 18:23:32 +00:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
g_free(child);
|
|
|
|
if (was_visible)
|
|
|
|
gtk_widget_queue_resize(GTK_WIDGET(container));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (children = cols->taborder;
|
|
|
|
children && (childw = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
if (childw != widget)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cols->taborder = g_list_remove_link(cols->taborder, children);
|
|
|
|
g_list_free(children);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void columns_forall(GtkContainer *container, gboolean include_internals,
|
|
|
|
GtkCallback callback, gpointer callback_data)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
ColumnsChild *child;
|
2003-03-31 11:22:06 +00:00
|
|
|
GList *children, *next;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
g_return_if_fail(container != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(container));
|
|
|
|
g_return_if_fail(callback != NULL);
|
|
|
|
|
|
|
|
cols = COLUMNS(container);
|
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
2003-03-31 11:22:06 +00:00
|
|
|
children = next) {
|
|
|
|
/*
|
|
|
|
* We can't wait until after the callback to assign
|
|
|
|
* `children = children->next', because the callback might
|
|
|
|
* be gtk_widget_destroy, which would remove the link
|
|
|
|
* `children' from the list! So instead we must get our
|
|
|
|
* hands on the value of the `next' pointer _before_ the
|
|
|
|
* callback.
|
|
|
|
*/
|
|
|
|
next = children->next;
|
2019-09-08 19:29:00 +00:00
|
|
|
if (child->widget)
|
|
|
|
callback(child->widget, callback_data);
|
2003-03-31 11:22:06 +00:00
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2015-08-08 16:29:02 +00:00
|
|
|
static GType columns_child_type(GtkContainer *container)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
|
|
|
return GTK_TYPE_WIDGET;
|
|
|
|
}
|
|
|
|
|
|
|
|
GtkWidget *columns_new(gint spacing)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
|
2008-04-02 17:04:21 +00:00
|
|
|
#if !GTK_CHECK_VERSION(2,0,0)
|
2003-03-13 19:52:28 +00:00
|
|
|
cols = gtk_type_new(columns_get_type());
|
2008-04-02 17:04:21 +00:00
|
|
|
#else
|
|
|
|
cols = g_object_new(TYPE_COLUMNS, NULL);
|
|
|
|
#endif
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
cols->spacing = spacing;
|
|
|
|
|
|
|
|
return GTK_WIDGET(cols);
|
|
|
|
}
|
|
|
|
|
|
|
|
void columns_set_cols(Columns *cols, gint ncols, const gint *percentages)
|
|
|
|
{
|
|
|
|
ColumnsChild *childdata;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
g_return_if_fail(cols != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(cols));
|
|
|
|
g_return_if_fail(ncols > 0);
|
|
|
|
g_return_if_fail(percentages != NULL);
|
|
|
|
|
|
|
|
childdata = g_new(ColumnsChild, 1);
|
|
|
|
childdata->widget = NULL;
|
|
|
|
childdata->ncols = ncols;
|
|
|
|
childdata->percentages = g_new(gint, ncols);
|
2018-10-29 19:50:29 +00:00
|
|
|
childdata->force_left = false;
|
2003-03-13 19:52:28 +00:00
|
|
|
for (i = 0; i < ncols; i++)
|
|
|
|
childdata->percentages[i] = percentages[i];
|
|
|
|
|
|
|
|
cols->children = g_list_append(cols->children, childdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
void columns_add(Columns *cols, GtkWidget *child,
|
|
|
|
gint colstart, gint colspan)
|
|
|
|
{
|
|
|
|
ColumnsChild *childdata;
|
|
|
|
|
|
|
|
g_return_if_fail(cols != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(cols));
|
|
|
|
g_return_if_fail(child != NULL);
|
2015-08-08 14:53:43 +00:00
|
|
|
g_return_if_fail(gtk_widget_get_parent(child) == NULL);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
childdata = g_new(ColumnsChild, 1);
|
|
|
|
childdata->widget = child;
|
|
|
|
childdata->colstart = colstart;
|
|
|
|
childdata->colspan = colspan;
|
2018-10-29 19:50:29 +00:00
|
|
|
childdata->force_left = false;
|
2015-08-24 18:23:32 +00:00
|
|
|
childdata->same_height_as = NULL;
|
2017-11-26 08:45:45 +00:00
|
|
|
childdata->percentages = NULL;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
cols->children = g_list_append(cols->children, childdata);
|
|
|
|
cols->taborder = g_list_append(cols->taborder, child);
|
|
|
|
|
|
|
|
gtk_widget_set_parent(child, GTK_WIDGET(cols));
|
|
|
|
|
2015-08-08 14:53:43 +00:00
|
|
|
if (gtk_widget_get_realized(GTK_WIDGET(cols)))
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_realize(child);
|
|
|
|
|
2015-08-08 14:53:43 +00:00
|
|
|
if (gtk_widget_get_visible(GTK_WIDGET(cols)) &&
|
|
|
|
gtk_widget_get_visible(child)) {
|
|
|
|
if (gtk_widget_get_mapped(GTK_WIDGET(cols)))
|
2003-03-13 19:52:28 +00:00
|
|
|
gtk_widget_map(child);
|
|
|
|
gtk_widget_queue_resize(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-24 18:12:29 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
void columns_force_left_align(Columns *cols, GtkWidget *widget)
|
|
|
|
{
|
|
|
|
ColumnsChild *child;
|
|
|
|
|
|
|
|
g_return_if_fail(cols != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(cols));
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
|
2015-08-24 18:12:29 +00:00
|
|
|
child = columns_find_child(cols, widget);
|
|
|
|
g_return_if_fail(child != NULL);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
child->force_left = true;
|
2015-08-24 18:12:29 +00:00
|
|
|
if (gtk_widget_get_visible(widget))
|
|
|
|
gtk_widget_queue_resize(GTK_WIDGET(cols));
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
2015-08-24 18:23:32 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
void columns_taborder_last(Columns *cols, GtkWidget *widget)
|
|
|
|
{
|
|
|
|
GtkWidget *childw;
|
|
|
|
GList *children;
|
|
|
|
|
|
|
|
g_return_if_fail(cols != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(cols));
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
|
|
|
|
for (children = cols->taborder;
|
|
|
|
children && (childw = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
if (childw != widget)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
cols->taborder = g_list_remove_link(cols->taborder, children);
|
|
|
|
g_list_free(children);
|
2019-09-08 19:29:00 +00:00
|
|
|
cols->taborder = g_list_append(cols->taborder, widget);
|
2003-03-13 19:52:28 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Override GtkContainer's focus movement so the user can
|
|
|
|
* explicitly specify the tab order.
|
|
|
|
*/
|
2018-10-28 09:14:53 +00:00
|
|
|
static gint columns_focus(FOCUS_METHOD_SUPERCLASS *super, GtkDirectionType dir)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
GList *pos;
|
|
|
|
GtkWidget *focuschild;
|
|
|
|
|
2018-10-29 19:50:29 +00:00
|
|
|
g_return_val_if_fail(super != NULL, false);
|
|
|
|
g_return_val_if_fail(IS_COLUMNS(super), false);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-10-28 09:14:53 +00:00
|
|
|
cols = COLUMNS(super);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-10-28 09:14:53 +00:00
|
|
|
if (!gtk_widget_is_drawable(GTK_WIDGET(cols)) ||
|
2019-09-08 19:29:00 +00:00
|
|
|
!gtk_widget_is_sensitive(GTK_WIDGET(cols)))
|
|
|
|
return false;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2018-10-28 09:14:53 +00:00
|
|
|
if (!gtk_widget_get_can_focus(GTK_WIDGET(cols)) &&
|
2019-09-08 19:29:00 +00:00
|
|
|
(dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {
|
|
|
|
|
|
|
|
focuschild = gtk_container_get_focus_child(GTK_CONTAINER(cols));
|
|
|
|
gtk_container_set_focus_child(GTK_CONTAINER(cols), NULL);
|
|
|
|
|
|
|
|
if (dir == GTK_DIR_TAB_FORWARD)
|
|
|
|
pos = cols->taborder;
|
|
|
|
else
|
|
|
|
pos = g_list_last(cols->taborder);
|
|
|
|
|
|
|
|
while (pos) {
|
|
|
|
GtkWidget *child = pos->data;
|
|
|
|
|
|
|
|
if (focuschild) {
|
|
|
|
if (focuschild == child) {
|
|
|
|
focuschild = NULL; /* now we can start looking in here */
|
|
|
|
if (gtk_widget_is_drawable(child) &&
|
|
|
|
GTK_IS_CONTAINER(child) &&
|
|
|
|
!gtk_widget_has_focus(child)) {
|
|
|
|
if (CHILD_FOCUS(child, dir))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (gtk_widget_is_drawable(child)) {
|
|
|
|
if (GTK_IS_CONTAINER(child)) {
|
|
|
|
if (CHILD_FOCUS(child, dir))
|
|
|
|
return true;
|
|
|
|
} else if (gtk_widget_get_can_focus(child)) {
|
|
|
|
gtk_widget_grab_focus(child);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dir == GTK_DIR_TAB_FORWARD)
|
|
|
|
pos = pos->next;
|
|
|
|
else
|
|
|
|
pos = pos->prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
2003-03-13 19:52:28 +00:00
|
|
|
} else
|
2019-09-08 19:29:00 +00:00
|
|
|
return columns_inherited_focus(super, dir);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-08-23 13:49:01 +00:00
|
|
|
* Underlying parts of the layout algorithm, to compute the Columns
|
|
|
|
* container's width or height given the widths or heights of its
|
|
|
|
* children. These will be called in various ways with different
|
|
|
|
* notions of width and height in use, so we abstract them out and
|
|
|
|
* pass them a 'get width' or 'get height' function pointer.
|
2003-03-13 19:52:28 +00:00
|
|
|
*/
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
typedef gint (*widget_dim_fn_t)(ColumnsChild *child);
|
|
|
|
|
|
|
|
static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
2015-08-23 13:49:01 +00:00
|
|
|
gint i, ncols, colspan, retwidth, childwidth;
|
2003-03-13 19:52:28 +00:00
|
|
|
const gint *percentages;
|
|
|
|
static const gint onecol[] = { 100 };
|
|
|
|
|
2015-08-27 17:32:41 +00:00
|
|
|
#ifdef COLUMNS_WIDTH_DIAGNOSTICS
|
|
|
|
printf("compute_width(%p): start\n", cols);
|
|
|
|
#endif
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
retwidth = 0;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
ncols = 1;
|
|
|
|
percentages = onecol;
|
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (!child->widget) {
|
|
|
|
/* Column reconfiguration. */
|
|
|
|
ncols = child->ncols;
|
|
|
|
percentages = child->percentages;
|
|
|
|
continue;
|
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
/* Only take visible widgets into account. */
|
2015-08-08 14:53:43 +00:00
|
|
|
if (!gtk_widget_get_visible(child->widget))
|
2003-03-13 19:52:28 +00:00
|
|
|
continue;
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
childwidth = get_width(child);
|
2019-09-08 19:29:00 +00:00
|
|
|
colspan = child->colspan ? child->colspan : ncols-child->colstart;
|
2018-12-01 14:13:37 +00:00
|
|
|
assert(colspan > 0);
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2015-08-27 17:32:41 +00:00
|
|
|
#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))
|
2018-10-29 19:50:29 +00:00
|
|
|
? "true" : "false"));
|
2015-08-27 17:32:41 +00:00
|
|
|
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
|
|
|
|
|
2003-03-13 19:52:28 +00:00
|
|
|
/*
|
2015-08-23 13:49:01 +00:00
|
|
|
* To compute width: we know that childwidth + cols->spacing
|
|
|
|
* needs to equal a certain percentage of the full width of
|
|
|
|
* the container. So we work this value out, figure out how
|
|
|
|
* wide the container will need to be to make that percentage
|
|
|
|
* of it equal to that width, and ensure our returned width is
|
|
|
|
* at least that much. Very simple really.
|
2003-03-13 19:52:28 +00:00
|
|
|
*/
|
|
|
|
{
|
|
|
|
int percent, thiswid, fullwid;
|
|
|
|
|
|
|
|
percent = 0;
|
|
|
|
for (i = 0; i < colspan; i++)
|
|
|
|
percent += percentages[child->colstart+i];
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
thiswid = childwidth + cols->spacing;
|
2003-03-13 19:52:28 +00:00
|
|
|
/*
|
2015-08-23 13:49:01 +00:00
|
|
|
* Since childwidth is (at least sometimes) the _minimum_
|
|
|
|
* size the child needs, we must ensure that it gets _at
|
|
|
|
* least_ that size. Hence, when scaling thiswid up to
|
|
|
|
* fullwid, we must round up, which means adding percent-1
|
|
|
|
* before dividing by percent.
|
2003-03-13 19:52:28 +00:00
|
|
|
*/
|
|
|
|
fullwid = (thiswid * 100 + percent - 1) / percent;
|
2015-08-27 17:32:41 +00:00
|
|
|
#ifdef COLUMNS_WIDTH_DIAGNOSTICS
|
|
|
|
printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n",
|
|
|
|
cols, child->widget, thiswid, fullwid);
|
|
|
|
#endif
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The above calculation assumes every widget gets
|
|
|
|
* cols->spacing on the right. So we subtract
|
|
|
|
* cols->spacing here to account for the extra load of
|
|
|
|
* spacing on the right.
|
|
|
|
*/
|
2015-08-23 13:49:01 +00:00
|
|
|
if (retwidth < fullwid - cols->spacing)
|
|
|
|
retwidth = fullwid - cols->spacing;
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2015-08-27 17:32:41 +00:00
|
|
|
#ifdef COLUMNS_WIDTH_DIAGNOSTICS
|
|
|
|
printf("compute_width(%p): done, returning %d\n", cols, retwidth);
|
|
|
|
#endif
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
return retwidth;
|
2015-08-16 13:16:08 +00:00
|
|
|
}
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
static void columns_alloc_horiz(Columns *cols, gint ourwidth,
|
|
|
|
widget_dim_fn_t get_width)
|
2003-03-13 19:52:28 +00:00
|
|
|
{
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
2015-08-23 13:49:01 +00:00
|
|
|
gint i, ncols, colspan, border, *colxpos, childwidth;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
2015-08-08 14:53:43 +00:00
|
|
|
border = gtk_container_get_border_width(GTK_CONTAINER(cols));
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
ncols = 1;
|
|
|
|
/* colxpos gives the starting x position of each column.
|
|
|
|
* We supply n+1 of them, so that we can find the RH edge easily.
|
|
|
|
* All ending x positions are expected to be adjusted afterwards by
|
|
|
|
* subtracting the spacing. */
|
|
|
|
colxpos = g_new(gint, 2);
|
|
|
|
colxpos[0] = 0;
|
2015-08-23 13:49:01 +00:00
|
|
|
colxpos[1] = ourwidth - 2*border + cols->spacing;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (!child->widget) {
|
|
|
|
gint percent;
|
|
|
|
|
|
|
|
/* Column reconfiguration. */
|
|
|
|
ncols = child->ncols;
|
|
|
|
colxpos = g_renew(gint, colxpos, ncols + 1);
|
|
|
|
colxpos[0] = 0;
|
|
|
|
percent = 0;
|
|
|
|
for (i = 0; i < ncols; i++) {
|
|
|
|
percent += child->percentages[i];
|
|
|
|
colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing)
|
|
|
|
* percent / 100);
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
/* Only take visible widgets into account. */
|
2015-08-08 14:53:43 +00:00
|
|
|
if (!gtk_widget_get_visible(child->widget))
|
2003-03-13 19:52:28 +00:00
|
|
|
continue;
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
childwidth = get_width(child);
|
2019-09-08 19:29:00 +00:00
|
|
|
colspan = child->colspan ? child->colspan : ncols-child->colstart;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Starting x position is cols[colstart].
|
|
|
|
* Ending x position is cols[colstart+colspan] - spacing.
|
2019-09-08 19:29:00 +00:00
|
|
|
*
|
|
|
|
* Unless we're forcing left, in which case the width is
|
|
|
|
* exactly the requisition width.
|
2003-03-13 19:52:28 +00:00
|
|
|
*/
|
2015-08-23 13:49:01 +00:00
|
|
|
child->x = colxpos[child->colstart];
|
2019-09-08 19:29:00 +00:00
|
|
|
if (child->force_left)
|
|
|
|
child->w = childwidth;
|
|
|
|
else
|
|
|
|
child->w = (colxpos[child->colstart+colspan] -
|
2015-08-23 13:49:01 +00:00
|
|
|
colxpos[child->colstart] - cols->spacing);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(colxpos);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height)
|
|
|
|
{
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
|
|
|
gint i, ncols, colspan, *colypos, retheight, childheight;
|
|
|
|
|
|
|
|
retheight = cols->spacing;
|
|
|
|
|
|
|
|
ncols = 1;
|
|
|
|
colypos = g_new(gint, 1);
|
|
|
|
colypos[0] = 0;
|
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
if (!child->widget) {
|
|
|
|
/* Column reconfiguration. */
|
|
|
|
for (i = 1; i < ncols; i++) {
|
|
|
|
if (colypos[0] < colypos[i])
|
|
|
|
colypos[0] = colypos[i];
|
|
|
|
}
|
|
|
|
ncols = child->ncols;
|
|
|
|
colypos = g_renew(gint, colypos, ncols);
|
|
|
|
for (i = 1; i < ncols; i++)
|
|
|
|
colypos[i] = colypos[0];
|
|
|
|
continue;
|
|
|
|
}
|
2015-08-23 13:49:01 +00:00
|
|
|
|
|
|
|
/* Only take visible widgets into account. */
|
|
|
|
if (!gtk_widget_get_visible(child->widget))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
childheight = get_height(child);
|
2015-08-24 18:23:32 +00:00
|
|
|
if (child->same_height_as) {
|
|
|
|
gint childheight2 = get_height(child->same_height_as);
|
|
|
|
if (childheight < childheight2)
|
|
|
|
childheight = childheight2;
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
colspan = child->colspan ? child->colspan : ncols-child->colstart;
|
2015-08-23 13:49:01 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* To compute height: the widget's top will be positioned at
|
|
|
|
* the largest y value so far reached in any of the columns it
|
|
|
|
* crosses. Then it will go down by childheight plus padding;
|
|
|
|
* and the point it reaches at the bottom is the new y value
|
|
|
|
* in all those columns, and minus the padding it is also a
|
|
|
|
* lower bound on our own height.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int topy, boty;
|
|
|
|
|
|
|
|
topy = 0;
|
|
|
|
for (i = 0; i < colspan; i++) {
|
|
|
|
if (topy < colypos[child->colstart+i])
|
|
|
|
topy = colypos[child->colstart+i];
|
|
|
|
}
|
|
|
|
boty = topy + childheight + cols->spacing;
|
|
|
|
for (i = 0; i < colspan; i++) {
|
|
|
|
colypos[child->colstart+i] = boty;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retheight < boty - cols->spacing)
|
|
|
|
retheight = boty - cols->spacing;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols));
|
|
|
|
|
|
|
|
g_free(colypos);
|
|
|
|
|
|
|
|
return retheight;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void columns_alloc_vert(Columns *cols, gint ourheight,
|
|
|
|
widget_dim_fn_t get_height)
|
|
|
|
{
|
|
|
|
ColumnsChild *child;
|
|
|
|
GList *children;
|
2015-08-24 18:23:32 +00:00
|
|
|
gint i, ncols, colspan, *colypos, realheight, fakeheight;
|
2015-08-23 13:49:01 +00:00
|
|
|
|
|
|
|
ncols = 1;
|
|
|
|
/* As in size_request, colypos is the lowest y reached in each column. */
|
|
|
|
colypos = g_new(gint, 1);
|
|
|
|
colypos[0] = 0;
|
|
|
|
|
|
|
|
for (children = cols->children;
|
|
|
|
children && (child = children->data);
|
|
|
|
children = children->next) {
|
2019-09-08 19:29:00 +00:00
|
|
|
if (!child->widget) {
|
|
|
|
/* Column reconfiguration. */
|
|
|
|
for (i = 1; i < ncols; i++) {
|
|
|
|
if (colypos[0] < colypos[i])
|
|
|
|
colypos[0] = colypos[i];
|
|
|
|
}
|
|
|
|
ncols = child->ncols;
|
|
|
|
colypos = g_renew(gint, colypos, ncols);
|
|
|
|
for (i = 1; i < ncols; i++)
|
|
|
|
colypos[i] = colypos[0];
|
|
|
|
continue;
|
|
|
|
}
|
2015-08-23 13:49:01 +00:00
|
|
|
|
|
|
|
/* Only take visible widgets into account. */
|
|
|
|
if (!gtk_widget_get_visible(child->widget))
|
|
|
|
continue;
|
|
|
|
|
2015-08-24 18:23:32 +00:00
|
|
|
realheight = fakeheight = get_height(child);
|
|
|
|
if (child->same_height_as) {
|
|
|
|
gint childheight2 = get_height(child->same_height_as);
|
|
|
|
if (fakeheight < childheight2)
|
|
|
|
fakeheight = childheight2;
|
|
|
|
}
|
2019-09-08 19:29:00 +00:00
|
|
|
colspan = child->colspan ? child->colspan : ncols-child->colstart;
|
2003-03-13 19:52:28 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* To compute height: the widget's top will be positioned
|
|
|
|
* at the largest y value so far reached in any of the
|
|
|
|
* columns it crosses. Then it will go down by creq.height
|
|
|
|
* plus padding; and the point it reaches at the bottom is
|
|
|
|
* the new y value in all those columns.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int topy, boty;
|
|
|
|
|
|
|
|
topy = 0;
|
|
|
|
for (i = 0; i < colspan; i++) {
|
|
|
|
if (topy < colypos[child->colstart+i])
|
|
|
|
topy = colypos[child->colstart+i];
|
|
|
|
}
|
2015-08-24 18:23:32 +00:00
|
|
|
child->y = topy + fakeheight/2 - realheight/2;
|
|
|
|
child->h = realheight;
|
|
|
|
boty = topy + fakeheight + cols->spacing;
|
2003-03-13 19:52:28 +00:00
|
|
|
for (i = 0; i < colspan; i++) {
|
|
|
|
colypos[child->colstart+i] = boty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-08 19:29:00 +00:00
|
|
|
g_free(colypos);
|
2003-03-13 19:52:28 +00:00
|
|
|
}
|
2015-08-23 13:49:01 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now here comes the interesting bit. The actual layout part is
|
|
|
|
* done in the following two functions:
|
|
|
|
*
|
|
|
|
* columns_size_request() examines the list of widgets held in the
|
|
|
|
* Columns, and returns a requisition stating the absolute minimum
|
|
|
|
* size it can bear to be.
|
|
|
|
*
|
|
|
|
* columns_size_allocate() is given an allocation telling it what
|
|
|
|
* size the whole container is going to be, and it calls
|
|
|
|
* gtk_widget_size_allocate() on all of its (visible) children to
|
|
|
|
* set their size and position relative to the top left of the
|
|
|
|
* container.
|
|
|
|
*/
|
|
|
|
|
2015-08-23 13:50:47 +00:00
|
|
|
#if !GTK_CHECK_VERSION(3,0,0)
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
static gint columns_gtk2_get_width(ColumnsChild *child)
|
|
|
|
{
|
|
|
|
GtkRequisition creq;
|
|
|
|
gtk_widget_size_request(child->widget, &creq);
|
|
|
|
return creq.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gint columns_gtk2_get_height(ColumnsChild *child)
|
|
|
|
{
|
|
|
|
GtkRequisition creq;
|
|
|
|
gtk_widget_size_request(child->widget, &creq);
|
|
|
|
return creq.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void columns_size_request(GtkWidget *widget, GtkRequisition *req)
|
|
|
|
{
|
|
|
|
Columns *cols;
|
|
|
|
|
|
|
|
g_return_if_fail(widget != NULL);
|
|
|
|
g_return_if_fail(IS_COLUMNS(widget));
|
|
|
|
g_return_if_fail(req != NULL);
|
|
|
|
|
|
|
|
cols = COLUMNS(widget);
|
|
|
|
|
|
|
|
req->width = columns_compute_width(cols, columns_gtk2_get_width);
|
|
|
|
req->height = columns_compute_height(cols, columns_gtk2_get_height);
|
|
|
|
}
|
|
|
|
|
2015-08-23 13:50:47 +00:00
|
|
|
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;
|
|
|
|
|
2015-08-27 17:59:24 +00:00
|
|
|
if ((GTK_IS_LABEL(child->widget) &&
|
|
|
|
gtk_label_get_line_wrap(GTK_LABEL(child->widget))) ||
|
|
|
|
GTK_IS_ENTRY(child->widget)) {
|
2015-08-23 13:50:47 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
2015-08-27 17:59:24 +00:00
|
|
|
*
|
|
|
|
* 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.
|
2015-08-23 13:50:47 +00:00
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-08-23 13:49:01 +00:00
|
|
|
static void columns_get_preferred_width(GtkWidget *widget,
|
|
|
|
gint *min, gint *nat)
|
|
|
|
{
|
2015-08-23 13:50:47 +00:00
|
|
|
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);
|
2015-08-23 13:49:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void columns_get_preferred_height(GtkWidget *widget,
|
2015-08-23 13:50:47 +00:00
|
|
|
gint *min, gint *nat)
|
|
|
|
{
|
|
|
|
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)
|
2015-08-23 13:49:01 +00:00
|
|
|
{
|
2015-08-23 13:50:47 +00:00
|
|
|
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);
|
2015-08-23 13:49:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
2015-08-23 13:50:47 +00:00
|
|
|
columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width);
|
|
|
|
columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height);
|
2015-08-23 13:49:01 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-08-23 13:50:47 +00:00
|
|
|
|
|
|
|
#endif
|