1 /*
2 ** 1998-07-12 -	Various little helper routines for building/managing GUIs.
3 ** 1999-03-13 -	Adapted for new dialog module.
4 */
5 
6 #include "gentoo.h"
7 
8 #include <string.h>
9 
10 #include "dialog.h"
11 #include "guiutil.h"
12 
13 /* ----------------------------------------------------------------------------------------- */
14 
15 typedef struct {
16 	Dialog		*dlg;
17 	GtkWidget	*vbox;
18 	GtkWidget	*label;
19 	GtkWidget	*obj;
20 	GtkWidget	*bar;
21 	gboolean	terminate;
22 } PBar;
23 
24 typedef struct
25 {
26 	GObject	*object;
27 	gulong	handler;
28 } SigHandler;
29 
30 struct GuiHandlerGroup
31 {
32 	GList	*handlers;
33 };
34 
35 /* ----------------------------------------------------------------------------------------- */
36 
37 /* 1999-04-26 -	Create a button with a "details" icon on it. This is typically a magnifying
38 **		class. This button is a graphical replacement for a button labeled "...".
39 ** 2009-04-23 -	Happy ten year anniversary, gui_details_button_new()! W00t! Rewritten to use
40 **		GTK+ 2.0 stock image, GTK_STOCK_FIND is what we use. It used to be a magnifying
41 **		glass, but in current GTK+ it's a pair of binoculars. Oh well.
42 */
gui_details_button_new(void)43 GtkWidget * gui_details_button_new(void)
44 {
45 	return gtk_button_new_from_icon_name("edit-find", GTK_ICON_SIZE_MENU);
46 }
47 
48 /* ----------------------------------------------------------------------------------------- */
49 
50 /* 2011-07-24 -	Build a regular entry widget, for use in a dialog (so with default-handling). */
gui_dialog_entry_new(void)51 GtkWidget * gui_dialog_entry_new(void)
52 {
53 	GtkWidget	*ent;
54 
55 	ent = gtk_entry_new();
56 	gtk_entry_set_activates_default(GTK_ENTRY(ent), TRUE);
57 
58 	return ent;
59 }
60 
61 /* ----------------------------------------------------------------------------------------- */
62 
63 /* 1998-07-30 -	Build a group of radio buttons with the given labels.
64 ** 1999-05-15 -	Now also sets the GTK+ object user data to the index of each button.
65 */
gui_radio_group_new(guint num,const gchar ** label,GtkWidget * but[])66 GSList * gui_radio_group_new(guint num, const gchar **label, GtkWidget *but[])
67 {
68 	GSList	*list = NULL;
69 	guint	i;
70 
71 	for(i = 0; i < num; i++)
72 	{
73 		but[i] = gtk_radio_button_new_with_label(list, _(label[i]));
74 		list = gtk_radio_button_get_group(GTK_RADIO_BUTTON(but[i]));
75 		g_object_set_data(G_OBJECT(but[i]), "user", GUINT_TO_POINTER(i));
76 	}
77 	return list;
78 }
79 
80 /* ----------------------------------------------------------------------------------------- */
81 
82 /* 1998-09-05 -	This gets called by the dialog module if the user hits the button. */
callback_done(gint button,gpointer user)83 static void callback_done(gint button, gpointer user)
84 {
85 	PBar	*pbar = user;
86 
87 	pbar->terminate = TRUE;
88 }
89 
90 /* 1998-09-05 -	Initialize a user-abortable progress "session". Opens up a dialog window
91 **		with the given <body> text and <button>, as well as a progress bar widget
92 **		and a "current object name" indicator (a bare label).
93 **		Returns an "anchor"; a pointer to some private data. You use this pointer
94 **		in later calls to identify the session. Progress sessions don't nest!
95 **		Use calls to gui_progress_update() to make the bar move, and also to learn
96 **		about any button actions.
97 */
gui_progress_begin(const gchar * body,const gchar * button)98 gpointer gui_progress_begin(const gchar *body, const gchar *button)
99 {
100 	static PBar	pbar;
101 
102 	pbar.vbox  = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
103 	pbar.label = gtk_label_new(body);
104 	gtk_box_pack_start(GTK_BOX(pbar.vbox), pbar.label, TRUE, TRUE, 0);
105 	pbar.obj = gtk_label_new("");
106 	gtk_box_pack_start(GTK_BOX(pbar.vbox), pbar.obj, TRUE, TRUE, 0);
107 	pbar.bar = gtk_progress_bar_new();
108 	gtk_box_pack_start(GTK_BOX(pbar.vbox), pbar.bar, TRUE, TRUE, 5);
109 	pbar.terminate = FALSE;
110 
111 	pbar.dlg = dlg_dialog_async_new(pbar.vbox, _("Progress"), button, callback_done, &pbar);
112 
113 	return &pbar;
114 }
115 
116 /* 1998-09-05 -	Update the progress bar to a new value (which should be between 0.0 and 1.0).
117 **		Also allows you to change the indication of the current object.
118 **		Returns TRUE if the user has hit the button in the window, typically indicating
119 **		that the entire operation should be aborted ASAP.
120 */
gui_progress_update(gpointer anchor,gfloat value,const gchar * obj)121 gboolean gui_progress_update(gpointer anchor, gfloat value, const gchar *obj)
122 {
123 	PBar	*pbar = anchor;
124 
125 	if(!pbar->terminate)
126 	{
127 		gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pbar->bar), value);
128 		if((obj != NULL) && (pbar->obj != NULL))
129 			gtk_label_set_text(GTK_LABEL(pbar->obj), obj);
130 		gui_events_flush();
131 	}
132 	return pbar->terminate;
133 }
134 
135 /* 1998-09-05 -	Close down the progress session, since the time-consuming operation has been
136 **		finished.
137 */
gui_progress_end(gpointer anchor)138 void gui_progress_end(gpointer anchor)
139 {
140 	PBar	*pbar = anchor;
141 
142 	if(!pbar->terminate)
143 		dlg_dialog_async_close(pbar->dlg, -1);
144 	memset(pbar, 0, sizeof *pbar);		/* Makes sure we notice any foul play. */
145 }
146 
147 /* ----------------------------------------------------------------------------------------- */
148 
149 /* 1998-06-22 -	Just a utility function to help the config modules to their thing. This one
150 **		builds a menu from a NULL-terminated vector of item texts. Very simple, but
151 **		also very useful. Like so many other good things. :) Each menu item will
152 **		have the given <func> connected to it, which will be called with <data> as
153 **		the user data (as usual). Also, to make it possible for the callback to
154 **		determine which item was selected, each item will have its index (from 0)
155 **		set as object's user data. Not pretty, but still simple and useful.
156 */
gui_build_menu(const gchar * text[],GCallback func,gpointer user)157 GtkWidget * gui_build_menu(const gchar *text[], GCallback func, gpointer user)
158 {
159 	GtkWidget	*menu, *item;
160 	guint		i;
161 
162 	menu = gtk_menu_new();
163 	for(i = 0; text[i] != NULL; i++)
164 	{
165 		item = gtk_menu_item_new_with_label(_(text[i]));
166 		if(func != NULL)
167 		{
168 			g_object_set_data(G_OBJECT(item), "user", GUINT_TO_POINTER(i));
169 			g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(func), user);
170 		}
171 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
172 		gtk_widget_show(item);
173 	}
174 	return menu;
175 }
176 
177 
178 /* 2010-05-10 -	Trampoline for the gui_build_combo_box()-constructed widgets. Computes the
179 **		index that was selected, and hands that to the user-supplied callback.
180 */
evt_combo_changed(GtkWidget * wid,gpointer user)181 static void evt_combo_changed(GtkWidget *wid, gpointer user)
182 {
183 	const gint	item = gtk_combo_box_get_active(GTK_COMBO_BOX(wid));
184 
185 	if(item >= 0)
186 	{
187 		GCallback	func = g_object_get_data(G_OBJECT(wid), "callback");
188 
189 		if(func != NULL)
190 			((void(*)(GtkWidget*, guint, gpointer))func)(wid, item, user);
191 	}
192 }
193 
194 /* 2010-05-09 -	Merry birthday, me! :D This is a replacement for the old gui_build_menu().
195 **		It builds a text-based GtkComboBox, and calls the given function like so:
196 **			void callback(GtkWidget *combo, guint index, gpointer user);
197 **		Simple, and friendly.
198 */
gui_build_combo_box(const gchar * text[],GCallback func,gpointer user)199 GtkWidget * gui_build_combo_box(const gchar *text[], GCallback func, gpointer user)
200 {
201 	GtkWidget	*cbox;
202 	gulong		handler;
203 
204 	cbox = gtk_combo_box_text_new();
205 	g_object_set_data(G_OBJECT(cbox), "callback", func);
206 	for(; *text != NULL; text++)
207 		gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(cbox), _(*text));
208 	handler = g_signal_connect(G_OBJECT(cbox), "changed", G_CALLBACK(evt_combo_changed), user);
209 	g_object_set_data(G_OBJECT(cbox), "handler", GINT_TO_POINTER(handler));
210 
211 	return cbox;
212 }
213 
214 /* 2010-11-02 -	Block the signal handler. Since it's a bit encapsulated, a function here is needed. */
gui_combo_box_set_blocked(GtkWidget * widget,gboolean blocked)215 void gui_combo_box_set_blocked(GtkWidget *widget, gboolean blocked)
216 {
217 	const gulong	handler = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "handler"));
218 
219 	if(blocked)
220 		g_signal_handler_block(G_OBJECT(widget), handler);
221 	else
222 		g_signal_handler_unblock(G_OBJECT(widget), handler);
223 }
224 
225 /* ----------------------------------------------------------------------------------------- */
226 
gui_handler_group_new(void)227 GuiHandlerGroup * gui_handler_group_new(void)
228 {
229 	GuiHandlerGroup	*g;
230 
231 	g = g_slice_new(GuiHandlerGroup);
232 	if(g != NULL)
233 	{
234 		g->handlers = NULL;
235 	}
236 	return g;
237 }
238 
gui_handler_group_connect(GuiHandlerGroup * g,GObject * obj,const gchar * signal,GCallback cb,gpointer user)239 gulong gui_handler_group_connect(GuiHandlerGroup *g, GObject *obj, const gchar *signal, GCallback cb, gpointer user)
240 {
241 	SigHandler	*sh;
242 
243 	if(g == NULL || obj == NULL || signal == NULL || cb == NULL)
244 		return 0ul;
245 
246 	if((sh = g_slice_new(SigHandler)) == NULL)
247 		return 0ul;
248 	sh->object = obj;
249 	sh->handler = g_signal_connect(obj, signal, cb, user);
250 	/* Prepend, for O(1) performance; order doesn't really matter. */
251 	g->handlers = g_list_prepend(g->handlers, sh);
252 
253 	/* Return handler ID, if user code wants to play with it. */
254 	return sh->handler;
255 }
256 
gui_handler_group_block(const GuiHandlerGroup * g)257 void gui_handler_group_block(const GuiHandlerGroup *g)
258 {
259 	const GList	*iter;
260 
261 	if(g == NULL)
262 		return;
263 	for(iter = g->handlers; iter != NULL; iter = g_list_next(iter))
264 	{
265 		const SigHandler *sh = iter->data;
266 		g_signal_handler_block(sh->object, sh->handler);
267 	}
268 }
269 
gui_handler_group_unblock(const GuiHandlerGroup * g)270 void gui_handler_group_unblock(const GuiHandlerGroup *g)
271 {
272 	const GList	*iter;
273 
274 	if(g == NULL)
275 		return;
276 	for(iter = g->handlers; iter != NULL; iter = g_list_next(iter))
277 	{
278 		const SigHandler *sh = iter->data;
279 		g_signal_handler_unblock(sh->object, sh->handler);
280 	}
281 }
282 
283 /* ----------------------------------------------------------------------------------------- */
284 
gui_set_main_title(MainInfo * min,const gchar * title)285 void gui_set_main_title(MainInfo *min, const gchar *title)
286 {
287 	gchar	buf[256];
288 
289 	if(title != NULL && *title != '\0')
290 	{
291 		g_snprintf(buf, sizeof buf, "gentoo - %s", title);
292 		win_window_set_title(min->gui->window, buf);
293 	}
294 	else
295 		win_window_set_title(min->gui->window, "gentoo");
296 }
297 
298 /* 2013-06-15 -	Converts a modern RGBA color to an old-school one. */
gui_color_from_rgba(GdkColor * color,const GdkRGBA * rgba)299 void gui_color_from_rgba(GdkColor *color, const GdkRGBA *rgba)
300 {
301 	color->red = 65535.0f * rgba->red;
302 	color->green = 65535.0f * rgba->green;
303 	color->blue = 65535.0f * rgba->blue;
304 	color->pixel = 0;
305 }
306 
gui_rgba_from_color(GdkRGBA * rgba,const GdkColor * color)307 void gui_rgba_from_color(GdkRGBA *rgba, const GdkColor *color)
308 {
309 	rgba->red   = color->red / 65535.;
310 	rgba->green = color->green / 65535.;
311 	rgba->blue  = color->blue / 65535.;
312 	rgba->alpha = 1.0;
313 }
314 
315 /* ----------------------------------------------------------------------------------------- */
316 
317 /* 2008-07-04 -	Flush pending GTK+ events.
318 ** 2010-03-04 -	Comments trimmed, legacy code removed. Just the events, thanks.
319 */
gui_events_flush(void)320 void gui_events_flush(void)
321 {
322 	while(gtk_events_pending())
323 		gtk_main_iteration();
324 }
325