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