1 /*
2 ** Custom button widget, that supports having a secondary click-function associated with being
3 ** clicked by the middle mouse button. Very much inspired by Directory Opus on the Amiga.
4 **
5 ** Original GTK+ 1.2.x version by Johan Hanson, re-written for GTK+ 2.0 and 3.0 by Emil Brink.
6 */
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 
11 #include "config.h"
12 
13 #include <gtk/gtk.h>
14 
15 #include "odmultibutton.h"
16 
17 static void	od_multibutton_class_init(ODMultiButtonClass *mbc);
18 static void	od_multibutton_init(ODMultiButton *mb);
19 
20 /* This seems to be customary in GTK+-land. I find it a bit... weird. */
21 static GtkButtonClass	*button_class = NULL;
22 
od_multibutton_set_trace(ODMultiButton * widget,unsigned int trace_mask)23 void od_multibutton_set_trace(ODMultiButton *widget, unsigned int trace_mask)
24 {
25 	g_return_if_fail(widget != NULL);
26 	g_return_if_fail(OD_IS_MULTIBUTTON(widget));
27 
28 	widget->trace_mask = trace_mask;
29 }
30 
od_multibutton_get_type(void)31 GType od_multibutton_get_type(void)
32 {
33 	static GType	od_multibutton_type = 0;
34 
35 	if(od_multibutton_type == 0)
36 	{
37 		static const GTypeInfo mb_info =
38 		{
39 			sizeof (ODMultiButtonClass),
40 			NULL,
41 			NULL,
42 			(GClassInitFunc) od_multibutton_class_init,
43 			NULL,
44 			NULL,
45 			sizeof (ODMultiButton),
46 			8,
47 			(GInstanceInitFunc) od_multibutton_init,
48 		};
49 		od_multibutton_type = g_type_register_static(GTK_TYPE_BUTTON, "ODMultiButton", &mb_info, 0);
50 	}
51 	return od_multibutton_type;
52 }
53 
54 /* Set which "page" should be displayed. Typically called as user clicks on the button widget. */
od_multibutton_set_page(GtkWidget * widget,guint index)55 static void od_multibutton_set_page(GtkWidget *widget, guint index)
56 {
57 	ODMultiButton	*mb;
58 	GtkWidget	*p, *op;
59 
60 	g_return_if_fail(widget != NULL);
61 	g_return_if_fail(index < sizeof mb->page / sizeof *mb->page);
62 	g_return_if_fail(OD_IS_MULTIBUTTON(widget));
63 
64 	mb = OD_MULTIBUTTON(widget);
65 
66 	p  = mb->page[index].widget;
67 	op = gtk_bin_get_child(GTK_BIN(mb));
68 
69 	if(op != p)
70 	{
71 		if(op != NULL)
72 		{
73 			g_object_ref(G_OBJECT(op));
74 			gtk_container_remove(GTK_CONTAINER(mb), op);
75 		}
76 		if(p != NULL)
77 		{
78 			GtkWidget	*pparent = gtk_widget_get_parent(p);
79 
80 			if(gtk_widget_get_state_flags(widget) != gtk_widget_get_state_flags(p))
81 				gtk_widget_set_state_flags(p, gtk_widget_get_state_flags(widget), TRUE);
82 			gtk_widget_show(p);
83 			if(pparent != NULL)
84 			{
85 				/* Manual reparenting. Semi-legacy code, not sure when this is needed at all. :/ */
86 				g_object_ref(p);
87 				gtk_container_remove(GTK_CONTAINER(pparent), p);
88 				gtk_container_add(GTK_CONTAINER(widget), p);
89 				g_object_unref(p);
90 			}
91 			else
92 			{
93 				gtk_container_add(GTK_CONTAINER(mb), p);
94 				g_object_unref(G_OBJECT(p));
95 			}
96 		}
97 		mb->last_index = index;
98 	}
99 	if(gtk_widget_is_drawable(widget))
100 		gtk_widget_queue_draw(widget);
101 }
102 
103 /* Compute size of the page widgets, and set width & height members to the maximum page size. */
od_multibutton_page_size_calc(ODMultiButton * mb)104 static void od_multibutton_page_size_calc(ODMultiButton *mb)
105 {
106 	guint	i;
107 
108 	mb->width = mb->height = 0;
109 	for(i = 0; i < sizeof mb->page / sizeof *mb->page; i++)
110 	{
111 		GtkWidget	*w;
112 
113 		if((w = mb->page[i].widget) != NULL)
114 		{
115 			GtkRequisition	req_min, req_max;
116 
117 			gtk_widget_get_preferred_size(w, &req_min, &req_max);
118 			mb->width  = MAX(mb->width, req_min.width);
119 			mb->height = MAX(mb->height, req_min.height);
120 		}
121 	}
122 }
123 
od_multibutton_get_preferred_width(GtkWidget * widget,gint * min_width,gint * max_width)124 static void od_multibutton_get_preferred_width(GtkWidget *widget, gint *min_width, gint *max_width)
125 {
126 	ODMultiButton	*mb;
127 	gint		width;
128 
129 	g_return_if_fail(widget != NULL);
130 	g_return_if_fail(OD_IS_MULTIBUTTON(widget));
131 
132 	mb = OD_MULTIBUTTON(widget);
133 	od_multibutton_page_size_calc(mb);
134 	width = mb->width;
135 
136 	width += 2 * gtk_container_get_border_width(GTK_CONTAINER(widget));
137 	/* FIXME: Not sure if we need to dig up CSS box model spacing here (margin/padding). */
138 
139 	if(min_width != NULL)
140 		*min_width = width;
141 	if(max_width != NULL)
142 		*max_width = width;
143 }
144 
145 /* Paint the "dog ear" that indicates second-mouse-button binding. This code is pretty much lifted
146 ** from Johan Hanson's original ODEmilButton widget, although not a straight copy and paste.
147 */
od_multibutton_paint_dog_ear(GtkWidget * widget,cairo_t * cr,const GtkAllocation * alloc,gint bw)148 static void od_multibutton_paint_dog_ear(GtkWidget *widget, cairo_t *cr, const GtkAllocation *alloc, gint bw)
149 {
150 	const GtkStateFlags	sflags = gtk_widget_get_state_flags(widget);
151 
152 	if(gtk_widget_is_drawable(widget) && sflags != GTK_STATE_FLAG_ACTIVE)
153 	{
154 		const gint	EAR_SIZE = 5;
155 
156 		cairo_move_to(cr, alloc->width - 2 * bw - (EAR_SIZE + 1), 0);
157 		cairo_rel_line_to(cr, 0, EAR_SIZE);
158 		cairo_rel_line_to(cr, EAR_SIZE, 0);
159 		cairo_close_path(cr);
160 		cairo_set_line_width(cr, 0.5);
161 		cairo_stroke(cr);
162 	}
163 }
164 
od_multibutton_paint_foreground(GtkWidget * widget,cairo_t * cr)165 static void od_multibutton_paint_foreground(GtkWidget *widget, cairo_t *cr)
166 {
167 	g_return_if_fail(widget != NULL);
168 	g_return_if_fail(cr != NULL);
169 	g_return_if_fail(OD_IS_MULTIBUTTON(widget));
170 
171 	if(gtk_widget_is_drawable(widget))
172 	{
173 		const ODMultiButton	*mb = OD_MULTIBUTTON(widget);
174 		GtkAllocation		alloc;
175 		const gint		bw = gtk_container_get_border_width(GTK_CONTAINER(widget));
176 
177 		gtk_widget_get_allocation(widget, &alloc);
178 		if(mb->page[1].widget != NULL)
179 			od_multibutton_paint_dog_ear(widget, cr, &alloc, bw);
180 		if(mb->config && mb->config_down)
181 		{
182 			GtkStyleContext	*sctx = gtk_widget_get_style_context(widget);
183 
184 			/* Trivial border-rendering, instead of faked sunken-in. */
185 			gtk_render_focus(sctx, cr, 0, 0, alloc.width - 2 * bw, alloc.height - 2 * bw);
186 		}
187 	}
188 }
189 
190 
191 /* This is the core expose handler. It simply relies on the superclass (GtkButton) to do most
192 ** of the drawing, taking care to fool it into doing what we want by poking a little. We also
193 ** have our own foreground painting routine, for that oh-so-cute dog ear.
194 */
od_multibutton_draw(GtkWidget * widget,cairo_t * cr)195 static gboolean od_multibutton_draw(GtkWidget *widget, cairo_t *cr)
196 {
197 	g_return_val_if_fail(widget != NULL, FALSE);
198 	g_return_val_if_fail(cr != NULL, FALSE);
199 	g_return_val_if_fail(OD_IS_MULTIBUTTON(widget), FALSE);
200 
201 	/* First let GtkButton draw, getting the basic button painted. */
202 	GTK_WIDGET_CLASS(button_class)->draw(widget, cr);
203 	/* Then paint our own modifying graphics on top, to get the dog ear and config border. */
204 	od_multibutton_paint_foreground(widget, cr);
205 
206 	return FALSE;
207 }
208 
209 /* (Mouse) button press handler. Switch to page 2 if it's the middle button. */
od_multibutton_button_press_event(GtkWidget * widget,GdkEventButton * evt)210 static gboolean od_multibutton_button_press_event(GtkWidget *widget, GdkEventButton *evt)
211 {
212 	ODMultiButton	*mb;
213 
214 	g_return_val_if_fail(widget != NULL, FALSE);
215 	g_return_val_if_fail(evt != NULL, FALSE);
216 	g_return_val_if_fail(OD_IS_MULTIBUTTON(widget), FALSE);
217 
218 	if(evt->button >= 3)
219 		return FALSE;
220 
221 	mb = OD_MULTIBUTTON(widget);
222 
223 	/* Ignore presses of middle button in config mode. Otherwise, don't be picky about page being defined. */
224 	if(mb->config)
225 	{
226 		if(evt->button > 1)
227 			return FALSE;
228 	}
229 	else if(mb->page[evt->button - 1].widget != NULL)
230 	{
231 		od_multibutton_set_page(widget, evt->button - 1);
232 		/* GtkButton only handles button-1-presses, so fool it. :) */
233 		evt->button = 1;
234 	}
235 	else
236 		return FALSE;
237 	return GTK_WIDGET_CLASS(button_class)->button_press_event(widget, evt);
238 }
239 
240 /* (Mouse) button release handler. Doesn't do much. */
od_multibutton_button_release_event(GtkWidget * widget,GdkEventButton * evt)241 static gboolean od_multibutton_button_release_event(GtkWidget *widget, GdkEventButton *evt)
242 {
243 	g_return_val_if_fail(widget != NULL, FALSE);
244 	g_return_val_if_fail(evt != NULL, FALSE);
245 	g_return_val_if_fail(OD_IS_MULTIBUTTON(widget), FALSE);
246 
247 	if(evt->button > 2)
248 		return FALSE;
249 
250 	/* In config mode, toggle lock on release. */
251 	if(OD_MULTIBUTTON(widget)->config)
252 	{
253 		if(evt->button > 1)
254 			return FALSE;
255 		OD_MULTIBUTTON(widget)->config_down ^= 1/*GTK_BUTTON(widget)->in_button*/;	/*FIXME?*/
256 	}
257 
258 	/* GtkButton wants to believe button 1 was pressed, so let's make it so. */
259 	evt->button = 1;
260 	GTK_WIDGET_CLASS(button_class)->button_release_event(widget, evt);
261 	/* Reset to initial page. */
262 	od_multibutton_set_page(widget, 0);
263 
264 	return FALSE;
265 }
266 
267 /* Initialize class. */
od_multibutton_class_init(ODMultiButtonClass * mbc)268 static void od_multibutton_class_init(ODMultiButtonClass *mbc)
269 {
270 	GtkWidgetClass	*widget = (GtkWidgetClass *) mbc;
271 
272 	/* It seems common practice to keep a superclass reference around as a global. */
273 	button_class = g_type_class_peek_parent(mbc);
274 
275 	/* Override methods. */
276 	widget->draw			= od_multibutton_draw;
277 	widget->get_preferred_width	= od_multibutton_get_preferred_width;
278 
279 	widget->button_press_event	= od_multibutton_button_press_event;
280 	widget->button_release_event	= od_multibutton_button_release_event;
281 }
282 
283 /* Initialize a brand new multibutton instance. */
od_multibutton_init(ODMultiButton * mb)284 static void od_multibutton_init(ODMultiButton *mb)
285 {
286 	guint	i;
287 
288 	gtk_widget_set_can_focus(GTK_WIDGET(mb), FALSE);
289 	gtk_widget_set_can_default(GTK_WIDGET(mb), FALSE);
290 	gtk_widget_set_receives_default(GTK_WIDGET(mb), FALSE);
291 
292 	for(i = 0; i < sizeof mb->page / sizeof *mb->page; i++)
293 	{
294 		mb->page[i].widget = NULL;
295 		mb->page[i].user   = NULL;
296 	}
297 	mb->width	= mb->height = 0;
298 	mb->config	= FALSE;
299 	mb->config_down = FALSE;
300 	mb->last_index	= 0;
301 }
302 
303 /* I like GTK+ 2 constructors. They're short and sweet, and just pass the buck. */
od_multibutton_new(void)304 GtkWidget * od_multibutton_new(void)
305 {
306 	return g_object_new(OD_MULTIBUTTON_TYPE, NULL);
307 }
308 
309 /* Enable or disable the special togglebutton-like "configuration mode". Used in gentoo's config UI. */
od_multibutton_set_config(ODMultiButton * mb,gboolean config)310 void od_multibutton_set_config(ODMultiButton *mb, gboolean config)
311 {
312 	g_return_if_fail(mb != NULL);
313 	g_return_if_fail(OD_IS_MULTIBUTTON(mb));
314 
315 	if(mb->config && !config)
316 		od_multibutton_set_config_selected(mb, FALSE);
317 	mb->config = config;
318 }
319 
320 /* Set the config toggle state. In practice (in gentoo), this is only ever used to deselect a button. */
od_multibutton_set_config_selected(ODMultiButton * mb,gboolean selected)321 void od_multibutton_set_config_selected(ODMultiButton *mb, gboolean selected)
322 {
323 	g_return_if_fail(mb != NULL);
324 	g_return_if_fail(OD_IS_MULTIBUTTON(mb));
325 	g_return_if_fail(OD_MULTIBUTTON(mb)->config);
326 
327 	if(mb->config && selected != mb->config_down)
328 	{
329 		mb->config_down = selected;
330 		gtk_widget_queue_draw(GTK_WIDGET(mb));
331 	}
332 }
333 
od_multibutton_remove_widget(ODMultiButton * mb,guint index)334 static void od_multibutton_remove_widget(ODMultiButton *mb, guint index)
335 {
336 	GtkWidget	*widget;
337 
338 	g_return_if_fail(mb != NULL);
339 	g_return_if_fail(OD_IS_MULTIBUTTON(mb));
340 	g_return_if_fail(index > sizeof mb->page / sizeof *mb->page);
341 
342 	widget = mb->page[index].widget;
343 	if(gtk_bin_get_child(GTK_BIN(mb)) == widget)
344 		gtk_container_remove(GTK_CONTAINER(mb), widget);
345 	else
346 		g_object_unref(G_OBJECT(widget));
347 	gtk_widget_queue_draw(GTK_WIDGET(mb));
348 }
349 
od_multibutton_set_widget(ODMultiButton * mb,guint index,GtkWidget * widget,gpointer user)350 static void od_multibutton_set_widget(ODMultiButton *mb, guint index, GtkWidget *widget, gpointer user)
351 {
352 	g_return_if_fail(mb != NULL);
353 	g_return_if_fail(OD_IS_MULTIBUTTON(mb));
354 	g_return_if_fail(index < sizeof mb->page / sizeof *mb->page);
355 
356 	if(mb->page[index].widget != widget)
357 	{
358 		if(mb->page[index].widget != NULL)
359 			od_multibutton_remove_widget(mb, index);
360 
361 		mb->page[index].widget = widget;
362 		g_object_ref(G_OBJECT(widget));
363 	}
364 	mb->page[index].user = user;
365 
366 	if(index == 0)
367 		od_multibutton_set_page(GTK_WIDGET(mb), 0);
368 }
369 
370 /* Build a color attribute, suitable for a Pango markup <span> element, of the given name. */
format_color_attribute(gchar * buf,gsize bufsize,const gchar * name,const GdkColor * color)371 static gboolean format_color_attribute(gchar *buf, gsize bufsize, const gchar *name, const GdkColor *color)
372 {
373 	if(buf == NULL || name == NULL || color == NULL)
374 		return FALSE;
375 	return g_snprintf(buf, bufsize, "%s=\"#%02X%02X%02X\"",
376 		name, color->red >> 8, color->green >> 8, color->blue >> 8) < bufsize;
377 }
378 
379 /* Re-set the label text for the given button's indicated face. This rebuilds the formatting
380 ** markup in the label to accomodate colors, if set.
381 */
od_multibutton_reset_label(const ODMultiButton * mb,guint index,GtkLabel * label,const gchar * text,const GdkColor * bg,const GdkColor * fg)382 static void od_multibutton_reset_label(const ODMultiButton *mb, guint index, GtkLabel *label, const gchar *text, const GdkColor *bg, const GdkColor *fg)
383 {
384 	if(bg != NULL || fg != NULL)
385 	{
386 		gchar	tmp[1024], bga[32], fga[32];
387 
388 		/* Warm up by building attributes. */
389 		bga[0] = fga[0] = '\0';
390 		if(bg != NULL)
391 			format_color_attribute(bga, sizeof bga, " background", bg);
392 		if(fg != NULL)
393 			format_color_attribute(fga, sizeof fga, " color", fg);
394 		/* Then build final label, ignoring any incoming span. Non-used colors are empty. */
395 		g_snprintf(tmp, sizeof tmp, "<span%s%s>%s</span>", fga, bga, text);
396 		gtk_label_set_markup_with_mnemonic(label, tmp);
397 	}
398 	else
399 		gtk_label_set_text_with_mnemonic(label, text);
400 }
401 
402 /* Set the textual content of one of the faces of the button. The userdata is handy when clicked. */
od_multibutton_set_text(ODMultiButton * mb,guint index,const gchar * text,const GdkColor * bg,const GdkColor * fg,gpointer user)403 void od_multibutton_set_text(ODMultiButton *mb, guint index, const gchar *text, const GdkColor *bg, const GdkColor *fg, gpointer user)
404 {
405 	GtkWidget	*w;
406 
407 	g_return_if_fail(mb != NULL);
408 	g_return_if_fail(OD_IS_MULTIBUTTON(mb));
409 	g_return_if_fail(index < sizeof mb->page / sizeof *mb->page);
410 
411 	/* If there is a widget set, and it's a label: just change it in place. */
412 	if((w = mb->page[index].widget) != NULL)
413 	{
414 		if(GTK_IS_LABEL(w))
415 		{
416 			od_multibutton_reset_label(mb, index, GTK_LABEL(w), text, bg, fg);
417 			if(gtk_widget_get_parent(GTK_WIDGET(mb)))
418 				gtk_widget_queue_resize(GTK_WIDGET(mb));
419 			if(gtk_widget_is_drawable(GTK_WIDGET(mb)))
420 				gtk_widget_queue_draw(GTK_WIDGET(mb));
421 		}
422 	}
423 	else	/* If no widget set, create label. */
424 	{
425 		w = gtk_label_new("");
426 		od_multibutton_reset_label(mb, index, GTK_LABEL(w), text, bg, fg);
427 	}
428 	if(GTK_IS_LABEL(w))
429 	{
430 		gtk_label_set_xalign(GTK_LABEL(w), 0.5f);
431 		gtk_label_set_yalign(GTK_LABEL(w), 0.5f);
432 	}
433 	od_multibutton_set_widget(mb, index, w, user);
434 }
435 
436 /* Returns index of last active page. */
od_multibutton_get_index(const ODMultiButton * mb)437 gint od_multibutton_get_index(const ODMultiButton *mb)
438 {
439 	g_return_val_if_fail(mb != NULL, -1);
440 	g_return_val_if_fail(OD_IS_MULTIBUTTON(mb), -1);
441 
442 	return OD_MULTIBUTTON(mb)->last_index;
443 }
444 
od_multibutton_get_userdata_last(const ODMultiButton * mb)445 gpointer od_multibutton_get_userdata_last(const ODMultiButton *mb)
446 {
447 	g_return_val_if_fail(mb != NULL, NULL);
448 	g_return_val_if_fail(OD_IS_MULTIBUTTON(mb), NULL);
449 
450 	return OD_MULTIBUTTON(mb)->page[OD_MULTIBUTTON(mb)->last_index].user;
451 }
452 
453 /* ------------------------------------------------------------------------------------------------------------------------- */
454 
455 #if defined ODMULTIBUTTON_STANDALONE
456 
evt_clicked(GtkWidget * wid,gpointer user)457 static void evt_clicked(GtkWidget *wid, gpointer user)
458 {
459 	g_printf("clicked, index=%d, data=%p\n", od_multibutton_get_index(OD_MULTIBUTTON(wid)),
460 		 od_multibutton_get_userdata_last(OD_MULTIBUTTON(wid)));
461 }
462 
mb_random_color(GtkWidget * wid,unsigned int face)463 static void mb_random_color(GtkWidget *wid, unsigned int face)
464 {
465 	GdkColor	col;
466 
467 	col.red   = rand();
468 	col.green = rand();
469 	col.blue  = rand();
470 	od_multibutton_set_background(OD_MULTIBUTTON(wid), face, &col);
471 }
472 
evt_color_clicked(GtkWidget * wid,gpointer user)473 static void evt_color_clicked(GtkWidget *wid, gpointer user)
474 {
475 	mb_random_color(user, 0);
476 	mb_random_color(user, 1);
477 }
478 
evt_clear_clicked(GtkWidget * wid,gpointer user)479 static void evt_clear_clicked(GtkWidget *wid, gpointer user)
480 {
481 	od_multibutton_set_config_selected(OD_MULTIBUTTON(user), FALSE);
482 }
483 
evt_exit_config_clicked(GtkWidget * wid,gpointer user)484 static void evt_exit_config_clicked(GtkWidget *wid, gpointer user)
485 {
486 	od_multibutton_set_config(OD_MULTIBUTTON(user), FALSE);
487 }
488 
main(int argc,char * argv[])489 int main(int argc, char *argv[])
490 {
491 	GtkWidget	*win, *box, *test, *btn;
492 	GdkColormap	*cmap;
493 	guint		i;
494 
495 	gtk_init(&argc, &argv);
496 
497 	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
498 	box = gtk_hbox_new(FALSE, 0);
499 
500 	btn = gtk_button_new_with_label("Filler");
501 	gtk_box_pack_start(GTK_BOX(box), btn, TRUE, TRUE, 0);
502 
503 	test = od_multibutton_new();
504 	od_multibutton_set_config(OD_MULTIBUTTON(test), TRUE);
505 //	gtk_container_set_border_width(GTK_CONTAINER(test), 5);
506 //	od_multibutton_set_widget_text(OD_MULTIBUTTON(test), 0, "Achtung", NULL);
507 	od_multibutton_set_widget_text(OD_MULTIBUTTON(test), 0, "Delete", NULL);
508 	od_multibutton_set_widget_text(OD_MULTIBUTTON(test), 1, "Copy <span color=\"#ff0000\">As</span>", NULL);
509 	g_signal_connect(G_OBJECT(test), "clicked",  G_CALLBACK(evt_clicked), NULL);
510 	gtk_box_pack_start(GTK_BOX(box), test, TRUE, TRUE, 0);
511 
512 	btn = gtk_button_new_with_label("Random Color");
513 	g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(evt_color_clicked), test);
514 	gtk_box_pack_start(GTK_BOX(box), btn, TRUE, TRUE, 0);
515 
516 	btn = gtk_button_new_with_label("Clear Lock");
517 	g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(evt_clear_clicked), test);
518 	gtk_box_pack_start(GTK_BOX(box), btn, TRUE, TRUE, 0);
519 
520 	{
521 		GtkWidget	*btn, *evb, *lab;
522 		GdkColor	test;
523 
524 		lab = gtk_label_new("Event Test");
525 		evb = gtk_event_box_new();
526 		gtk_container_add(GTK_CONTAINER(evb), lab);
527 		gdk_color_parse("red", &test);
528 		gtk_widget_modify_bg(evb, GTK_STATE_NORMAL, &test);
529 		gtk_widget_modify_bg(evb, GTK_STATE_PRELIGHT, &test);
530 		gtk_widget_modify_bg(evb, GTK_STATE_ACTIVE, &test);
531 		btn = gtk_button_new();
532 		gtk_container_add(GTK_CONTAINER(btn), evb);
533 		gtk_box_pack_start(GTK_BOX(box), btn, TRUE, TRUE, 0);
534 	}
535 
536 /*	btn = od_multibutton_new();
537 	od_multibutton_set_config(OD_MULTIBUTTON(btn), TRUE);
538 	od_multibutton_set_widget_text(OD_MULTIBUTTON(btn), 0, "Test Test", NULL);
539 	mb_random_colors(btn, 0);
540 	gtk_box_pack_start(GTK_BOX(box), btn, TRUE, TRUE, 0);
541 */
542 	btn = gtk_button_new_with_label("Exit Config");
543 	g_signal_connect(G_OBJECT(btn), "clicked", G_CALLBACK(evt_exit_config_clicked), test);
544 	gtk_box_pack_start(GTK_BOX(box), btn, TRUE, TRUE, 0);
545 
546 	gtk_container_add(GTK_CONTAINER(win), box);
547 
548 	gtk_widget_show_all(win);
549 	gtk_main();
550 
551 	return EXIT_SUCCESS;
552 }
553 
554 #endif		/* ODMULTIBUTTON_STANDALONE */
555