summaryrefslogtreecommitdiff
path: root/new/newcontrol_unix.c
blob: 500798b54715c7e00268c9c2c7ce7b66ce483654 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
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;
}