// 7 april 2015 #include "uipriv_unix.h" typedef struct singleWidget singleWidget; struct singleWidget { GtkWidget *widget; GtkWidget *scrolledWindow; GtkWidget *immediate; // the widget that is added to the parent container; either widget or scrolledWindow uintptr_t parent; gboolean userHid; gboolean containerHid; gboolean userDisabled; gboolean containerDisabled; }; static void singleDestroy(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); gtk_widget_destroy(s->immediate); } static uintptr_t singleHandle(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); return (uintptr_t) (s->widget); } static void singleSetParent(uiControl *c, uintptr_t parent) { singleWidget *s = (singleWidget *) (c->internal); s->parent = parent; gtk_container_add(GTK_CONTAINER(s->parent), s->immediate); updateParent(s->parent); } static void singleRemoveParent(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); uintptr_t oldparent; oldparent = s->parent; s->parent = 0; gtk_container_remove(GTK_CONTAINER(oldparent), s->immediate); updateParent(oldparent); } static void singlePreferredSize(uiControl *c, uiSizing *d, intmax_t *width, intmax_t *height) { singleWidget *s = (singleWidget *) (c->internal); GtkRequisition natural; // use the natural size as the minimum size is an *absolute* minimum // for example, if a label has ellipsizing on, it can be the width of the ellipses, not the text // there is a warning about height-for-width sizing, but in my tests this isn't an issue gtk_widget_get_preferred_size(s->widget, NULL, &natural); *width = natural.width; *height = natural.height; } static void singleResize(uiControl *c, intmax_t x, intmax_t y, intmax_t width, intmax_t height, uiSizing *d) { singleWidget *s = (singleWidget *) (c->internal); GtkAllocation a; a.x = x; a.y = y; a.width = width; a.height = height; gtk_widget_size_allocate(s->immediate, &a); } static int singleVisible(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); if (s->userHid) return 0; return 1; } static void singleShow(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->userHid = FALSE; if (!s->containerHid) { gtk_widget_show_all(s->immediate); updateParent(s->parent); } } static void singleHide(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->userHid = TRUE; gtk_widget_hide(s->immediate); updateParent(s->parent); } static void singleContainerShow(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->containerHid = FALSE; if (!s->userHid) { gtk_widget_show_all(s->immediate); updateParent(s->parent); } } static void singleContainerHide(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->containerHid = TRUE; gtk_widget_hide(s->immediate); updateParent(s->parent); } static void singleEnable(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->userDisabled = FALSE; if (!s->containerDisabled) gtk_widget_set_sensitive(s->immediate, TRUE); } static void singleDisable(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->userDisabled = TRUE; gtk_widget_set_sensitive(s->immediate, FALSE); } static void singleContainerEnable(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->containerDisabled = FALSE; if (!s->userDisabled) gtk_widget_set_sensitive(s->immediate, TRUE); } static void singleContainerDisable(uiControl *c) { singleWidget *s = (singleWidget *) (c->internal); s->containerDisabled = TRUE; gtk_widget_set_sensitive(s->immediate, FALSE); } static void onDestroy(GtkWidget *widget, gpointer data) { uiControl *c = (uiControl *) data; singleWidget *s = (singleWidget *) (c->internal); uiFree(s); uiFree(c); } uiControl *uiUnixNewControl(GType type, gboolean inScrolledWindow, gboolean scrolledWindowHasBorder, const char *firstProperty, ...) { uiControl *c; singleWidget *s; va_list ap; s = uiNew(singleWidget); va_start(ap, firstProperty); s->widget = GTK_WIDGET(g_object_new_valist(type, firstProperty, ap)); va_end(ap); s->immediate = s->widget; if (inScrolledWindow) { s->scrolledWindow = gtk_scrolled_window_new(NULL, NULL); if (!GTK_IS_SCROLLABLE(s->widget)) gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(s->scrolledWindow), s->widget); else gtk_container_add(GTK_CONTAINER(s->scrolledWindow), s->widget); if (scrolledWindowHasBorder) gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(s->scrolledWindow), GTK_SHADOW_IN); s->immediate = s->scrolledWindow; } // we need to keep an extra reference on the immediate widget // this is so uiControlDestroy() can work regardless of when it is called and who calls it // without this: // - end user call works (only one ref) // - call in uiContainer destructor fails (uiContainer ref freed) // with this: // - end user call works (shoudn't be in any container) // - call in uiContainer works (both refs freed) // this also ensures singleRemoveParent() works properly g_object_ref_sink(s->immediate); c = uiNew(uiControl); // assign s later; we still need it for one more thing c->destroy = singleDestroy; c->handle = singleHandle; c->setParent = singleSetParent; c->removeParent = singleRemoveParent; c->preferredSize = singlePreferredSize; c->resize = singleResize; c->visible = singleVisible; c->show = singleShow; c->hide = singleHide; c->containerShow = singleContainerShow; c->containerHide = singleContainerHide; c->enable = singleEnable; c->disable = singleDisable; c->containerEnable = singleContainerEnable; c->containerDisable = singleContainerDisable; // and let's free everything with the immediate widget g_signal_connect(s->immediate, "destroy", G_CALLBACK(onDestroy), c); c->internal = s; return c; }