summaryrefslogtreecommitdiff
path: root/new/unix
diff options
context:
space:
mode:
Diffstat (limited to 'new/unix')
-rw-r--r--new/unix/alloc.c33
-rw-r--r--new/unix/button.c67
-rw-r--r--new/unix/checkbox.c89
-rw-r--r--new/unix/entry.c41
-rw-r--r--new/unix/init.c23
-rw-r--r--new/unix/label.c45
-rw-r--r--new/unix/main.c23
-rw-r--r--new/unix/newcontrol.c229
-rw-r--r--new/unix/parent.c183
-rw-r--r--new/unix/tab.c58
-rw-r--r--new/unix/util.c7
-rw-r--r--new/unix/window.c105
12 files changed, 903 insertions, 0 deletions
diff --git a/new/unix/alloc.c b/new/unix/alloc.c
new file mode 100644
index 0000000..33482b2
--- /dev/null
+++ b/new/unix/alloc.c
@@ -0,0 +1,33 @@
+// 7 april 2015
+#include <stdio.h>
+#include "uipriv_unix.h"
+
+void *uiAlloc(size_t size, const char *type)
+{
+ void *out;
+
+ out = g_malloc0(size);
+ if (options.debugLogAllocations)
+ fprintf(stderr, "%p alloc %s\n", out, type);
+ return out;
+}
+
+void *uiRealloc(void *p, size_t size, const char *type)
+{
+ void *out;
+
+ if (p == NULL)
+ return uiAlloc(size, type);
+ // TODO fill with 0s
+ out = g_realloc(p, size);
+ if (options.debugLogAllocations)
+ fprintf(stderr, "%p realloc %p\n", p, out);
+ return out;
+}
+
+void uiFree(void *p)
+{
+ g_free(p);
+ if (options.debugLogAllocations)
+ fprintf(stderr, "%p free\n", p);
+}
diff --git a/new/unix/button.c b/new/unix/button.c
new file mode 100644
index 0000000..da09082
--- /dev/null
+++ b/new/unix/button.c
@@ -0,0 +1,67 @@
+// 7 april 2015
+#include "uipriv_unix.h"
+
+struct button {
+ void (*onClicked)(uiControl *, void *);
+ void *onClickedData;
+};
+
+static void onClicked(GtkButton *button, gpointer data)
+{
+ uiControl *c = (uiControl *) data;
+ struct button *b = (struct button *) (c->data);
+
+ (*(b->onClicked))(c, b->onClickedData);
+}
+
+static void defaultOnClicked(uiControl *c, void *data)
+{
+ // do nothing
+}
+
+static void onDestroy(GtkWidget *widget, gpointer data)
+{
+ struct button *b = (struct button *) data;
+
+ uiFree(b);
+}
+
+uiControl *uiNewButton(const char *text)
+{
+ uiControl *c;
+ struct button *b;
+ GtkWidget *widget;
+
+ c = uiUnixNewControl(GTK_TYPE_BUTTON,
+ FALSE, FALSE,
+ "label", text,
+ NULL);
+
+ widget = GTK_WIDGET(uiControlHandle(c));
+ g_signal_connect(widget, "clicked", G_CALLBACK(onClicked), c);
+
+ b = uiNew(struct button);
+ g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), b);
+ b->onClicked = defaultOnClicked;
+ c->data = b;
+
+ return c;
+}
+
+char *uiButtonText(uiControl *c)
+{
+ return g_strdup(gtk_button_get_label(GTK_BUTTON(uiControlHandle(c))));
+}
+
+void uiButtonSetText(uiControl *c, const char *text)
+{
+ gtk_button_set_label(GTK_BUTTON(uiControlHandle(c)), text);
+}
+
+void uiButtonOnClicked(uiControl *c, void (*f)(uiControl *, void *), void *data)
+{
+ struct button *b = (struct button *) (c->data);
+
+ b->onClicked = f;
+ b->onClickedData = data;
+}
diff --git a/new/unix/checkbox.c b/new/unix/checkbox.c
new file mode 100644
index 0000000..ddf9eda
--- /dev/null
+++ b/new/unix/checkbox.c
@@ -0,0 +1,89 @@
+// 7 april 2015
+#include "uipriv_unix.h"
+
+struct checkbox {
+ void (*onToggled)(uiControl *, void *);
+ void *onToggledData;
+ gulong onToggledSignal;
+};
+
+static void onToggled(GtkToggleButton *b, gpointer data)
+{
+ uiControl *c = (uiControl *) data;
+ struct checkbox *cc = (struct checkbox *) (c->data);
+
+ (*(cc->onToggled))(c, cc->onToggledData);
+}
+
+static void defaultOnToggled(uiControl *c, void *data)
+{
+ // do nothing
+}
+
+static void onDestroy(GtkWidget *widget, gpointer data)
+{
+ struct checkbox *cc = (struct checkbox *) data;
+
+ uiFree(cc);
+}
+
+uiControl *uiNewCheckbox(const char *text)
+{
+ uiControl *c;
+ struct checkbox *cc;
+ GtkWidget *widget;
+
+ c = uiUnixNewControl(GTK_TYPE_CHECK_BUTTON,
+ FALSE, FALSE,
+ "label", text,
+ NULL);
+
+ widget = GTK_WIDGET(uiControlHandle(c));
+
+ cc = uiNew(struct checkbox);
+ g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), cc);
+ cc->onToggledSignal = g_signal_connect(widget, "toggled", G_CALLBACK(onToggled), c);
+ cc->onToggled = defaultOnToggled;
+ c->data = cc;
+
+ return c;
+}
+
+char *uiCheckboxText(uiControl *c)
+{
+ return g_strdup(gtk_button_get_label(GTK_BUTTON(uiControlHandle(c))));
+}
+
+void uiCheckboxSetText(uiControl *c, const char *text)
+{
+ gtk_button_set_label(GTK_BUTTON(uiControlHandle(c)), text);
+}
+
+void uiCheckboxOnToggled(uiControl *c, void (*f)(uiControl *, void *), void *data)
+{
+ struct checkbox *cc = (struct checkbox *) (c->data);
+
+ cc->onToggled = f;
+ cc->onToggledData = data;
+}
+
+int uiCheckboxChecked(uiControl *c)
+{
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uiControlHandle(c))) != FALSE;
+}
+
+void uiCheckboxSetChecked(uiControl *c, int checked)
+{
+ struct checkbox *cc = (struct checkbox *) (c->data);
+ GtkToggleButton *button;
+ gboolean active;
+
+ active = FALSE;
+ if (checked)
+ active = TRUE;
+ // we need to inhibit sending of ::toggled because this WILL send a ::toggled otherwise
+ button = GTK_TOGGLE_BUTTON(uiControlHandle(c));
+ g_signal_handler_block(button, cc->onToggledSignal);
+ gtk_toggle_button_set_active(button, active);
+ g_signal_handler_unblock(button, cc->onToggledSignal);
+}
diff --git a/new/unix/entry.c b/new/unix/entry.c
new file mode 100644
index 0000000..43caea3
--- /dev/null
+++ b/new/unix/entry.c
@@ -0,0 +1,41 @@
+// 8 april 2015
+#include "uipriv_unix.h"
+
+struct entry {
+};
+
+static void onDestroy(GtkWidget *widget, gpointer data)
+{
+ struct entry *e = (struct entry *) data;
+
+ uiFree(e);
+}
+
+uiControl *uiNewEntry(void)
+{
+ uiControl *c;
+ struct entry *e;
+ GtkWidget *widget;
+
+ c = uiUnixNewControl(GTK_TYPE_ENTRY,
+ FALSE, FALSE,
+ NULL);
+
+ widget = GTK_WIDGET(uiControlHandle(c));
+
+ e = uiNew(struct entry);
+ g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), e);
+ c->data = e;
+
+ return c;
+}
+
+char *uiEntryText(uiControl *c)
+{
+ return g_strdup(gtk_entry_get_text(GTK_ENTRY(uiControlHandle(c))));
+}
+
+void uiEntrySetText(uiControl *c, const char *text)
+{
+ gtk_entry_set_text(GTK_ENTRY(uiControlHandle(c)), text);
+}
diff --git a/new/unix/init.c b/new/unix/init.c
new file mode 100644
index 0000000..b10c05b
--- /dev/null
+++ b/new/unix/init.c
@@ -0,0 +1,23 @@
+// 6 april 2015
+#include "uipriv_unix.h"
+
+uiInitOptions options;
+
+const char *uiInit(uiInitOptions *o)
+{
+ GError *err = NULL;
+ const char *msg;
+
+ options = *o;
+ if (gtk_init_with_args(NULL, NULL, NULL, NULL, NULL, &err) == FALSE) {
+ msg = g_strdup(err->message);
+ g_error_free(err);
+ return msg;
+ }
+ return NULL;
+}
+
+void uiFreeInitError(const char *err)
+{
+ g_free((gpointer) err);
+}
diff --git a/new/unix/label.c b/new/unix/label.c
new file mode 100644
index 0000000..1f950d3
--- /dev/null
+++ b/new/unix/label.c
@@ -0,0 +1,45 @@
+// 11 april 2015
+#include "uipriv_unix.h"
+
+struct label {
+};
+
+static void onDestroy(GtkWidget *widget, gpointer data)
+{
+ struct label *l = (struct label *) data;
+
+ uiFree(l);
+}
+
+uiControl *uiNewLabel(const char *text)
+{
+ uiControl *c;
+ struct label *l;
+ GtkWidget *widget;
+
+ c = uiUnixNewControl(GTK_TYPE_LABEL,
+ FALSE, FALSE,
+ "label", text,
+ "xalign", 0.0, // note: must be a float constant, otherwise the ... will turn it into an int and we get segfaults on some platforms (thanks ebassi in irc.gimp.net/#gtk+)
+ // TODO yalign 0?
+ NULL);
+
+ widget = GTK_WIDGET(uiControlHandle(c));
+
+ l = uiNew(struct label);
+ g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), l);
+ c->data = l;
+
+ return c;
+}
+
+char *uiLabelText(uiControl *c)
+{
+ // TODO change g_strdup() to a wrapper function for export in ui_unix.h
+ return g_strdup(gtk_label_get_text(GTK_LABEL(uiControlHandle(c))));
+}
+
+void uiLabelSetText(uiControl *c, const char *text)
+{
+ gtk_label_set_text(GTK_LABEL(uiControlHandle(c)), text);
+}
diff --git a/new/unix/main.c b/new/unix/main.c
new file mode 100644
index 0000000..10af782
--- /dev/null
+++ b/new/unix/main.c
@@ -0,0 +1,23 @@
+// 6 april 2015
+#include "uipriv_unix.h"
+
+// #qo pkg-config: gtk+-3.0
+
+void uiMain(void)
+{
+ gtk_main();
+}
+
+// gtk_main_quit() may run immediately, or it may wait for other pending events; "it depends" (thanks mclasen in irc.gimp.net/#gtk+)
+// PostQuitMessage() on Windows always waits, so we must do so too
+// we'll do it by using an idle callback
+static gboolean quit(gpointer data)
+{
+ gtk_main_quit();
+ return FALSE;
+}
+
+void uiQuit(void)
+{
+ gdk_threads_add_idle(quit, NULL);
+}
diff --git a/new/unix/newcontrol.c b/new/unix/newcontrol.c
new file mode 100644
index 0000000..500798b
--- /dev/null
+++ b/new/unix/newcontrol.c
@@ -0,0 +1,229 @@
+// 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
+ uiParent *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, uiParent *parent)
+{
+ singleWidget *s = (singleWidget *) (c->internal);
+ uiParent *oldparent;
+
+ oldparent = s->parent;
+ s->parent = parent;
+ if (oldparent != NULL) {
+ gtk_container_remove(GTK_CONTAINER(uiParentHandle(oldparent)), s->immediate);
+ uiParentUpdate(oldparent);
+ }
+ if (s->parent != NULL) {
+ gtk_container_add(GTK_CONTAINER(uiParentHandle(s->parent)), s->immediate);
+ uiParentUpdate(s->parent);
+ }
+}
+
+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);
+ if (s->parent != NULL)
+ uiParentUpdate(s->parent);
+ }
+}
+
+static void singleHide(uiControl *c)
+{
+ singleWidget *s = (singleWidget *) (c->internal);
+
+ s->userHid = TRUE;
+ gtk_widget_hide(s->immediate);
+ if (s->parent != NULL)
+ uiParentUpdate(s->parent);
+}
+
+static void singleContainerShow(uiControl *c)
+{
+ singleWidget *s = (singleWidget *) (c->internal);
+
+ s->containerHid = FALSE;
+ if (!s->userHid) {
+ gtk_widget_show_all(s->immediate);
+ if (s->parent != NULL)
+ uiParentUpdate(s->parent);
+ }
+}
+
+static void singleContainerHide(uiControl *c)
+{
+ singleWidget *s = (singleWidget *) (c->internal);
+
+ s->containerHid = TRUE;
+ gtk_widget_hide(s->immediate);
+ if (s->parent != NULL)
+ uiParentUpdate(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->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);
+
+ // finally, call gtk_widget_show_all() here to set the initial visibility of the widget
+ gtk_widget_show_all(s->immediate);
+
+ c->internal = s;
+ return c;
+}
diff --git a/new/unix/parent.c b/new/unix/parent.c
new file mode 100644
index 0000000..54d5d06
--- /dev/null
+++ b/new/unix/parent.c
@@ -0,0 +1,183 @@
+// 13 august 2014
+#include "uipriv_unix.h"
+
+#define uipParentType (uipParent_get_type())
+#define uipParent(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), uipParentType, uipParent))
+#define uipIsParent(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), uipParentType))
+#define uipParentClass(class) (G_TYPE_CHECK_CLASS_CAST((class), uipParentType, uipParentClass))
+#define uipIsParentClass(class) (G_TYPE_CHECK_CLASS_TYPE((class), uipParent))
+#define uipGetParentClass(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), uipParentType, uipParentClass))
+
+typedef struct uipParent uipParent;
+typedef struct uipParentClass uipParentClass;
+
+struct uipParent {
+ GtkContainer parent_instance;
+ // this is what triggers the resizing of all the children
+ uiControl *child;
+ // these are the actual children widgets of the container as far as GTK+ is concerned
+ GPtrArray *children; // for forall()
+ intmax_t marginLeft;
+ intmax_t marginTop;
+ intmax_t marginRight;
+ intmax_t marginBottom;
+};
+
+struct uipParentClass {
+ GtkContainerClass parent_class;
+};
+
+G_DEFINE_TYPE(uipParent, uipParent, GTK_TYPE_CONTAINER)
+
+static void uipParent_init(uipParent *p)
+{
+ if (options.debugLogAllocations)
+ fprintf(stderr, "%p alloc uipParent\n", p);
+ p->children = g_ptr_array_new();
+ gtk_widget_set_has_window(GTK_WIDGET(p), FALSE);
+}
+
+// instead of having GtkContainer itself unref all our controls, we'll run our own uiControlDestroy() functions for child, which will do that and more
+// we still chain up because we need to, but by that point there will be no children for GtkContainer to free
+static void uipParent_dispose(GObject *obj)
+{
+ uipParent *p = uipParent(obj);
+
+ if (p->children != NULL) {
+ g_ptr_array_unref(p->children);
+ p->children = NULL;
+ }
+ if (p->child != NULL) {
+ uiControlDestroy(p->child);
+ p->child = NULL;
+ }
+ G_OBJECT_CLASS(uipParent_parent_class)->dispose(obj);
+}
+
+static void uipParent_finalize(GObject *obj)
+{
+ G_OBJECT_CLASS(uipParent_parent_class)->finalize(obj);
+ if (options.debugLogAllocations)
+ fprintf(stderr, "%p free\n", obj);
+}
+
+static void uipParent_add(GtkContainer *container, GtkWidget *widget)
+{
+ uipParent *p = uipParent(container);
+
+ gtk_widget_set_parent(widget, GTK_WIDGET(p));
+ if (p->children != NULL)
+ g_ptr_array_add(p->children, widget);
+}
+
+static void uipParent_remove(GtkContainer *container, GtkWidget *widget)
+{
+ uipParent *p = uipParent(container);
+
+ gtk_widget_unparent(widget);
+ if (p->children != NULL)
+ g_ptr_array_remove(p->children, widget);
+}
+
+#define gtkXPadding 12
+#define gtkYPadding 6
+
+static void uipParent_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
+{
+ uipParent *p = uipParent(widget);
+ uiSizing d;
+ intmax_t x, y, width, height;
+
+ gtk_widget_set_allocation(GTK_WIDGET(p), allocation);
+ if (p->child == NULL)
+ return;
+ x = allocation->x + p->marginLeft;
+ y = allocation->y + p->marginTop;
+ width = allocation->width - (p->marginLeft + p->marginRight);
+ height = allocation->height - (p->marginTop + p->marginBottom);
+ d.xPadding = gtkXPadding;
+ d.yPadding = gtkYPadding;
+ uiControlResize(p->child, x, y, width, height, &d);
+}
+
+struct forall {
+ GtkCallback callback;
+ gpointer data;
+};
+
+static void doforall(gpointer obj, gpointer data)
+{
+ struct forall *s = (struct forall *) data;
+
+ (*(s->callback))(GTK_WIDGET(obj), s->data);
+}
+
+static void uipParent_forall(GtkContainer *container, gboolean includeInternals, GtkCallback callback, gpointer data)
+{
+ uipParent *p = uipParent(container);
+ struct forall s;
+
+ s.callback = callback;
+ s.data = data;
+ if (p->children != NULL)
+ g_ptr_array_foreach(p->children, doforall, &s);
+}
+
+static void uipParent_class_init(uipParentClass *class)
+{
+ G_OBJECT_CLASS(class)->dispose = uipParent_dispose;
+ G_OBJECT_CLASS(class)->finalize = uipParent_finalize;
+ GTK_WIDGET_CLASS(class)->size_allocate = uipParent_size_allocate;
+ GTK_CONTAINER_CLASS(class)->add = uipParent_add;
+ GTK_CONTAINER_CLASS(class)->remove = uipParent_remove;
+ GTK_CONTAINER_CLASS(class)->forall = uipParent_forall;
+}
+
+static uintptr_t parentHandle(uiParent *p)
+{
+ uipParent *pp = uipParent(p->Internal);
+
+ return (uintptr_t) pp;
+}
+
+static void parentSetChild(uiParent *p, uiControl *child)
+{
+ uipParent *pp = uipParent(p->Internal);
+
+ pp->child = child;
+ if (pp->child != NULL)
+ uiControlSetParent(child, p);
+}
+
+static void parentSetMargins(uiParent *p, intmax_t left, intmax_t top, intmax_t right, intmax_t bottom)
+{
+ uipParent *pp = uipParent(p->Internal);
+
+ pp->marginLeft = left;
+ pp->marginTop = top;
+ pp->marginRight = right;
+ pp->marginBottom = bottom;
+}
+
+static void parentUpdate(uiParent *p)
+{
+ uipParent *pp = uipParent(p->Internal);
+
+ gtk_widget_queue_resize(GTK_WIDGET(pp));
+}
+
+uiParent *uiNewParent(uintptr_t osParent)
+{
+ uiParent *p;
+
+ p = uiNew(uiParent);
+ p->Internal = g_object_new(uipParentType, NULL);
+ p->Handle = parentHandle;
+ p->SetChild = parentSetChild;
+ p->SetMargins = parentSetMargins;
+ p->Update = parentUpdate;
+ gtk_container_add(GTK_CONTAINER(osParent), GTK_WIDGET(p->Internal));
+ // and make it visible by default
+ gtk_widget_show_all(GTK_WIDGET(p->Internal));
+ return p;
+}
diff --git a/new/unix/tab.c b/new/unix/tab.c
new file mode 100644
index 0000000..dd69e4c
--- /dev/null
+++ b/new/unix/tab.c
@@ -0,0 +1,58 @@
+// 12 april 2015
+#include "uipriv_unix.h"
+
+struct tab {
+ uiParent **pages;
+ uintmax_t len;
+ uintmax_t cap;
+};
+
+static void onDestroy(GtkWidget *widget, gpointer data)
+{
+ struct tab *t = (struct tab *) data;
+
+ uiFree(t->pages);
+ uiFree(t);
+}
+
+uiControl *uiNewTab(void)
+{
+ uiControl *c;
+ struct tab *t;
+ GtkWidget *widget;
+
+ c = uiUnixNewControl(GTK_TYPE_NOTEBOOK,
+ FALSE, FALSE,
+ NULL);
+
+ widget = GTK_WIDGET(uiControlHandle(c));
+
+ t = uiNew(struct tab);
+ g_signal_connect(widget, "destroy", G_CALLBACK(onDestroy), t);
+ c->data = t;
+
+ return c;
+}
+
+#define tabCapGrow 32
+
+void uiTabAddPage(uiControl *c, const char *name, uiControl *child)
+{
+ struct tab *t = (struct tab *) (c->data);
+ GtkWidget *notebook;
+ uiParent *content;
+
+ if (t->len >= t->cap) {
+ t->cap += tabCapGrow;
+ t->pages = (uiParent **) uiRealloc(t->pages, t->cap * sizeof (uiParent *), "uiParent *[]");
+ }
+
+ notebook = GTK_WIDGET(uiControlHandle(c));
+ content = uiNewParent((uintptr_t) notebook);
+ uiParentSetChild(content, child);
+ uiParentUpdate(content);
+ gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(notebook), GTK_WIDGET(uiParentHandle(content)), name);
+
+ t->pages[t->len] = content;
+ t->len++;
+}
diff --git a/new/unix/util.c b/new/unix/util.c
new file mode 100644
index 0000000..72b71df
--- /dev/null
+++ b/new/unix/util.c
@@ -0,0 +1,7 @@
+// 9 april 2015
+#include "uipriv_unix.h"
+
+void uiFreeText(char *t)
+{
+ g_free(t);
+}
diff --git a/new/unix/window.c b/new/unix/window.c
new file mode 100644
index 0000000..e3fe6f3
--- /dev/null
+++ b/new/unix/window.c
@@ -0,0 +1,105 @@
+// 6 april 2015
+#include "uipriv_unix.h"
+
+struct uiWindow {
+ GtkWidget *widget;
+ uiParent *content;
+ int (*onClosing)(uiWindow *, void *);
+ void *onClosingData;
+ int margined;
+};
+
+static gboolean onClosing(GtkWidget *win, GdkEvent *e, gpointer data)
+{
+ uiWindow *w = (uiWindow *) data;
+
+ // return exact values just in case
+ if ((*(w->onClosing))(w, w->onClosingData))
+ return FALSE;
+ return TRUE;
+}
+
+static int defaultOnClosing(uiWindow *w, void *data)
+{
+ return 1;
+}
+
+static void onDestroy(GtkWidget *widget, gpointer data)
+{
+ uiWindow *w = (uiWindow *) data;
+
+ uiFree(w);
+}
+
+uiWindow *uiNewWindow(char *title, int width, int height)
+{
+ uiWindow *w;
+
+ w = uiNew(uiWindow);
+ w->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(w->widget), title);
+ gtk_window_resize(GTK_WINDOW(w->widget), width, height);
+ g_signal_connect(w->widget, "delete-event", G_CALLBACK(onClosing), w);
+ g_signal_connect(w->widget, "destroy", G_CALLBACK(onDestroy), w);
+ w->content = uiNewParent((uintptr_t) (w->widget));
+ w->onClosing = defaultOnClosing;
+ return w;
+}
+
+void uiWindowDestroy(uiWindow *w)
+{
+ gtk_widget_destroy(w->widget);
+}
+
+uintptr_t uiWindowHandle(uiWindow *w)
+{
+ return (uintptr_t) (w->widget);
+}
+
+char *uiWindowTitle(uiWindow *w)
+{
+ return g_strdup(gtk_window_get_title(GTK_WINDOW(w->widget)));
+}
+
+void uiWindowSetTitle(uiWindow *w, const char *title)
+{
+ gtk_window_set_title(GTK_WINDOW(w->widget), title);
+}
+
+void uiWindowShow(uiWindow *w)
+{
+ // don't use gtk_widget_show_all(); that will override user hidden settings
+ gtk_widget_show(w->widget);
+}
+
+void uiWindowHide(uiWindow *w)
+{
+ gtk_widget_hide(w->widget);
+}
+
+void uiWindowOnClosing(uiWindow *w, int (*f)(uiWindow *, void *), void *data)
+{
+ w->onClosing = f;
+ w->onClosingData = data;
+}
+
+void uiWindowSetChild(uiWindow *w, uiControl *c)
+{
+ uiParentSetChild(w->content, c);
+ uiParentUpdate(w->content);
+}
+
+int uiWindowMargined(uiWindow *w)
+{
+ return w->margined;
+}
+
+void uiWindowSetMargined(uiWindow *w, int margined)
+{
+ w->margined = margined;
+ if (w->margined)
+ uiParentSetMargins(w->content, gtkXMargin, gtkYMargin, gtkXMargin, gtkYMargin);
+ else
+ uiParentSetMargins(w->content, 0, 0, 0, 0);
+ uiParentUpdate(w->content);
+}