1 
2 /*
3  * The Real SoundTracker - GUI support routines
4  *
5  * Copyright (C) 1998-2019 Michael Krause
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21 
22 #include <config.h>
23 
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <gdk/gdkkeysyms.h>
29 #include <glib/gi18n.h>
30 
31 #include "extspinbutton.h"
32 #include "gui-subs.h"
33 #include "gui.h"
34 
35 const gint SIZES_MENU_TOOLBOX[] = {16, 22, 0};
36 const gint SIZES_MENU[] = {16, 0};
37 
find_current_toggle(GtkWidget ** widgets,int count)38 int find_current_toggle(GtkWidget** widgets, int count)
39 {
40     int i;
41     for (i = 0; i < count; i++) {
42         if (GTK_TOGGLE_BUTTON(*widgets++)->active) {
43             return i;
44         }
45     }
46     return -1;
47 }
48 
add_empty_hbox(GtkWidget * tobox)49 void add_empty_hbox(GtkWidget* tobox)
50 {
51     GtkWidget* thing = gtk_hbox_new(FALSE, 0);
52     gtk_widget_show(thing);
53     gtk_box_pack_start(GTK_BOX(tobox), thing, TRUE, TRUE, 0);
54 }
55 
add_empty_vbox(GtkWidget * tobox)56 void add_empty_vbox(GtkWidget* tobox)
57 {
58     GtkWidget* thing = gtk_vbox_new(FALSE, 0);
59     gtk_widget_show(thing);
60     gtk_box_pack_start(GTK_BOX(tobox), thing, TRUE, TRUE, 0);
61 }
62 
make_radio_group_full_num(const char * labels[],GtkWidget * tobox,GtkWidget * saveptr[],gint t1,gint t2,void (* sigfunc)(void),gpointer data,gboolean end,guint num)63 void make_radio_group_full_num(const char* labels[],
64     GtkWidget* tobox,
65     GtkWidget* saveptr[],
66     gint t1,
67     gint t2,
68     void (*sigfunc)(void),
69     gpointer data,
70     gboolean end,
71     guint num)
72 {
73     guint i;
74     GtkWidget* thing = NULL;
75 
76     for(i = 0; i < num; i++) {
77         thing = gtk_radio_button_new_with_label((thing
78                                                  ? gtk_radio_button_get_group(GTK_RADIO_BUTTON(thing))
79                                                  : NULL),
80             gettext(labels[end ? num - i - 1: i]));
81         saveptr[end ? num - i - 1: i] = thing;
82         gtk_widget_show(thing);
83         (end ? gtk_box_pack_end : gtk_box_pack_start)(GTK_BOX(tobox), thing, t1, t2, 0);
84         if (sigfunc) {
85             g_signal_connect(thing, "clicked", G_CALLBACK(sigfunc), data);
86         }
87     }
88 }
89 
90 GtkWidget*
make_labelled_radio_group_box_full_num(const char * title,const char * labels[],GtkWidget * saveptr[],void (* sigfunc)(),gpointer data,guint num)91 make_labelled_radio_group_box_full_num(const char* title,
92     const char* labels[],
93     GtkWidget* saveptr[],
94     void (*sigfunc)(),
95     gpointer data,
96     guint num)
97 {
98     GtkWidget *box, *thing;
99 
100     box = gtk_hbox_new(FALSE, 4);
101 
102     thing = gtk_label_new(title);
103     gtk_widget_show(thing);
104     gtk_box_pack_start(GTK_BOX(box), thing, FALSE, TRUE, 0);
105 
106     make_radio_group_full_num(labels, box, saveptr, FALSE, TRUE, sigfunc, data, FALSE, num);
107 
108     return box;
109 }
110 
111 GtkWidget*
gui_labelled_spin_button_new(const char * title,int min,int max,GtkWidget ** spin,void (* callback)(),void * callbackdata,gboolean in_mainwindow,gint * handler_id)112 gui_labelled_spin_button_new(const char* title,
113     int min,
114     int max,
115     GtkWidget** spin,
116     void (*callback)(),
117     void* callbackdata,
118     gboolean in_mainwindow,
119     gint* handler_id)
120 {
121     GtkWidget *hbox, *thing;
122 
123     hbox = gtk_hbox_new(FALSE, 4);
124     gtk_widget_show(hbox);
125 
126     thing = gtk_label_new(title);
127     gtk_box_pack_start(GTK_BOX(hbox), thing, FALSE, TRUE, 0);
128     gtk_widget_show(thing);
129 
130     *spin = extspinbutton_new(GTK_ADJUSTMENT(gtk_adjustment_new(min, min, max, 1.0, 5.0, 0.0)), 0, 0, in_mainwindow);
131     gtk_box_pack_end(GTK_BOX(hbox), *spin, FALSE, TRUE, 0);
132     gtk_widget_show(*spin);
133     if (callback) {
134         gint tmp_id = g_signal_connect(*spin, "value-changed",
135             G_CALLBACK(callback), callbackdata);
136 
137         if (handler_id)
138             *handler_id = tmp_id;
139     }
140 
141     return hbox;
142 }
143 
144 GtkWidget*
gui_subs_create_slider_full(const gchar * title,const gdouble min,const gdouble max,void (* changedfunc)(),GtkAdjustment ** adj,gboolean in_mainwindow,gint * tag)145 gui_subs_create_slider_full(const gchar* title,
146     const gdouble min,
147     const gdouble max,
148     void (*changedfunc)(),
149     GtkAdjustment** adj,
150     gboolean in_mainwindow,
151     gint* tag)
152 {
153     GtkWidget *thing, *box;
154     gint id;
155 
156     box = gtk_hbox_new(FALSE, 4);
157 
158     thing = gtk_label_new(title);
159     gtk_widget_show(thing);
160     gtk_box_pack_start(GTK_BOX(box), thing, FALSE, TRUE, 0);
161 
162     *adj = GTK_ADJUSTMENT(gtk_adjustment_new(min, min, max, 1.0, (max - min) / 10.0, 0.0));
163     thing = gtk_hscale_new(*adj);
164     gtk_scale_set_draw_value(GTK_SCALE(thing), FALSE);
165     gtk_widget_show(thing);
166     gtk_box_pack_start(GTK_BOX(box), thing, TRUE, TRUE, 0);
167 
168     thing = extspinbutton_new(*adj, 0.0, 0, in_mainwindow);
169     gtk_box_pack_start(GTK_BOX(box), thing, FALSE, TRUE, 0);
170     gtk_widget_show(thing);
171 
172     id = g_signal_connect(*adj, "value_changed",
173         G_CALLBACK(changedfunc), NULL);
174     if (tag)
175         *tag = id;
176 
177     return box;
178 }
179 
180 GtkWidget*
gui_stringlist_in_scrolled_window(const int n,const gchar * const * tp,GtkWidget * hbox,gboolean expandfill)181 gui_stringlist_in_scrolled_window(const int n, const gchar* const* tp, GtkWidget* hbox, gboolean expandfill)
182 {
183     GType* types;
184     GtkWidget* list;
185     guint i;
186 
187     types = g_new(GType, n);
188     for (i = 0; i < n; i++)
189         types[i] = G_TYPE_STRING;
190     list = gui_list_in_scrolled_window(n, tp, hbox, types, NULL, NULL, GTK_SELECTION_BROWSE, expandfill, expandfill);
191     g_free(types);
192     return list;
193 }
194 
195 inline void
gui_list_clear(GtkWidget * list)196 gui_list_clear(GtkWidget* list)
197 {
198     gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(list))));
199 }
200 
201 inline void
gui_list_clear_with_model(GtkTreeModel * model)202 gui_list_clear_with_model(GtkTreeModel* model)
203 {
204     gtk_list_store_clear(GTK_LIST_STORE(model));
205 }
206 
207 GtkTreeModel*
gui_list_freeze(GtkWidget * list)208 gui_list_freeze(GtkWidget* list)
209 {
210     GtkTreeModel* model;
211 
212     model = gtk_tree_view_get_model(GTK_TREE_VIEW(list));
213     g_object_ref(model);
214     gtk_tree_view_set_model(GTK_TREE_VIEW(list), NULL);
215 
216     return model;
217 }
218 
gui_list_thaw(GtkWidget * list,GtkTreeModel * model)219 void gui_list_thaw(GtkWidget* list, GtkTreeModel* model)
220 {
221     gtk_tree_view_set_model(GTK_TREE_VIEW(list), model);
222     g_object_unref(model);
223 }
224 
hover_changed(GtkTreeView * widget,GdkEvent * event,gpointer data)225 static gboolean hover_changed(GtkTreeView* widget, GdkEvent* event, gpointer data)
226 {
227     gboolean is_hover = data != NULL;
228     gtk_tree_view_set_hover_selection(widget, is_hover);
229     return FALSE;
230 }
231 
232 GtkWidget*
gui_list_in_scrolled_window_full(const int n,const gchar * const * tp,GtkWidget * hbox,GType * types,const gfloat * alignments,const gboolean * expands,GtkSelectionMode mode,gboolean expand,gboolean fill,GtkWidget ** scrolledwindow,GtkPolicyType hpolicy,GtkPolicyType vpolicy)233 gui_list_in_scrolled_window_full(const int n, const gchar* const* tp, GtkWidget* hbox,
234     GType* types, const gfloat* alignments, const gboolean* expands,
235     GtkSelectionMode mode, gboolean expand, gboolean fill, GtkWidget** scrolledwindow,
236     GtkPolicyType hpolicy, GtkPolicyType vpolicy)
237 {
238     GtkWidget* list;
239     GtkWidget* sw;
240     guint i;
241     GtkListStore* list_store;
242     GtkTreeViewColumn* column;
243     GtkCellRenderer* renderer;
244     GtkTreeSelection* sel;
245 
246     list_store = gtk_list_store_newv(n, types);
247     list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
248     for (i = 0; i < n; i++) {
249         renderer = gtk_cell_renderer_text_new();
250         column = gtk_tree_view_column_new_with_attributes(_(tp[i]), renderer, "text", i, NULL);
251         if (alignments) {
252             g_object_set(G_OBJECT(renderer), "xalign", alignments[i], NULL);
253             gtk_tree_view_column_set_alignment(column, alignments[i]);
254         }
255         g_object_set(G_OBJECT(renderer), "ypad", 0, NULL);
256         if (expands)
257             gtk_tree_view_column_set_expand(column, expands[i]);
258         gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
259     }
260 
261     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
262     gtk_tree_selection_set_mode(sel, mode);
263 
264     sw = gtk_scrolled_window_new(NULL, NULL);
265     if (scrolledwindow)
266         *scrolledwindow = sw;
267     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
268     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), hpolicy, vpolicy);
269     gtk_container_add(GTK_CONTAINER(sw), list);
270 
271     /* Making the pointer following the cursor when the button is pressed (like in gtk+-1) */
272     /* TODO: enabling autoscrolling when the pointer is moved up/down */
273     g_signal_connect(list, "button-press-event", G_CALLBACK(hover_changed), GINT_TO_POINTER(TRUE));
274     g_signal_connect(list, "button-release-event", G_CALLBACK(hover_changed), NULL);
275     g_signal_connect(list, "leave-notify-event", G_CALLBACK(hover_changed), NULL);
276 
277     gtk_box_pack_start(GTK_BOX(hbox), sw, expand, fill, 0);
278     /* According to Gtk+ documentation this is not recommended but lists are not strippy by default...*/
279     gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list), TRUE);
280 
281     return list;
282 }
283 
gui_list_handle_selection(GtkWidget * list,GCallback handler,gpointer data)284 void gui_list_handle_selection(GtkWidget* list, GCallback handler, gpointer data)
285 {
286     GtkTreeSelection* sel;
287 
288     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
289     g_signal_connect_after(sel, "changed", handler, data);
290 }
291 
gui_list_get_selection_index(GtkTreeSelection * sel)292 gint gui_list_get_selection_index(GtkTreeSelection* sel)
293 {
294     GtkTreeModel* mdl;
295     GtkTreeIter iter;
296     gchar* str;
297 
298     if (gtk_tree_selection_get_selected(sel, &mdl, &iter)) {
299         gint row = atoi(str = gtk_tree_model_get_string_from_iter(mdl, &iter));
300         g_free(str);
301 
302         return row;
303     } else
304         return -1;
305 }
306 
gui_string_list_set_text(GtkWidget * list,guint row,guint col,const gchar * string)307 void gui_string_list_set_text(GtkWidget* list, guint row, guint col, const gchar* string)
308 {
309     GtkTreeIter iter;
310     GtkListStore* list_store;
311 
312     if (gui_list_get_iter(row, list_store = GUI_GET_LIST_STORE(list), &iter))
313         gtk_list_store_set(list_store, &iter, col, string, -1);
314 }
315 
gui_list_select(GtkWidget * list,guint row,gboolean use_align,gfloat align)316 void gui_list_select(GtkWidget* list, guint row, gboolean use_align, gfloat align)
317 {
318     gchar* path_string;
319     GtkTreePath* path;
320     GtkTreeIter iter;
321     GtkTreeSelection* sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
322 
323     if (!gui_list_get_iter(row, GUI_GET_LIST_STORE(list), &iter))
324         return;
325     gtk_tree_selection_select_iter(sel, &iter);
326     path_string = g_strdup_printf("%u", row);
327     path = gtk_tree_path_new_from_string(path_string);
328     gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(list), path, NULL,
329         use_align, align, 0.0);
330 
331     g_free(path_string);
332     gtk_tree_path_free(path);
333 }
334 
335 GtkWidget*
gui_button(GtkWidget * win,char * stock,void * callback,gpointer userdata,GtkWidget * box)336 gui_button(GtkWidget* win, char* stock,
337     void* callback, gpointer userdata, GtkWidget* box)
338 {
339     GtkWidget* button;
340 
341     button = gtk_button_new_from_stock(stock);
342     g_signal_connect(button, "clicked",
343         G_CALLBACK(callback), userdata);
344     gtk_widget_show(button);
345 
346     if (box)
347         gtk_container_add(GTK_CONTAINER(box), button);
348 
349     return button;
350 }
351 
gui_message_dialog(GtkWidget ** dialog,const gchar * text,GtkMessageType type,const gchar * title,gboolean need_update)352 void gui_message_dialog(GtkWidget** dialog, const gchar* text, GtkMessageType type, const gchar* title, gboolean need_update)
353 {
354     if (!*dialog) {
355         *dialog = gtk_message_dialog_new(GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, type,
356             GTK_BUTTONS_CLOSE, "%s", text);
357         gtk_window_set_title(GTK_WINDOW(*dialog), _(title));
358     } else if (need_update) {
359         gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(*dialog), text);
360     }
361 
362     gtk_dialog_run(GTK_DIALOG(*dialog));
363     gtk_widget_hide(*dialog);
364 }
365 
366 void
gui_errno_dialog(GtkWidget * parent,const gchar * text,const int err)367 gui_errno_dialog(GtkWidget *parent, const gchar *text, const int err)
368 {
369     gchar *buf = g_strdup_printf("%s: %s", text, g_strerror(err));
370     static GtkWidget *dialog = NULL;
371 
372     if (!dialog) {
373         dialog = gtk_message_dialog_new(GTK_WINDOW(parent), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
374                                         GTK_BUTTONS_CLOSE, "%s", buf);
375         gtk_window_set_title(GTK_WINDOW(dialog), _("Error!"));
376     } else
377         gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), buf);
378 
379     gtk_dialog_run(GTK_DIALOG(dialog));
380     gtk_widget_hide(dialog);
381     g_free(buf);
382 }
383 
384 gboolean
gui_ok_cancel_modal(GtkWidget * parent,const gchar * text)385 gui_ok_cancel_modal(GtkWidget* parent, const gchar* text)
386 {
387     gint response;
388     static GtkWidget* dialog = NULL;
389 
390     if (!dialog) {
391         dialog = gtk_message_dialog_new(GTK_WINDOW(parent), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
392             NULL);
393         gtk_window_set_title(GTK_WINDOW(dialog), _("Question"));
394     }
395     gtk_message_dialog_set_markup(GTK_MESSAGE_DIALOG(dialog), text);
396     response = gtk_dialog_run(GTK_DIALOG(dialog));
397     gtk_widget_hide(dialog);
398 
399     return (response == GTK_RESPONSE_OK);
400 }
401 
gui_oom_error(void)402 void gui_oom_error(void)
403 {
404     static GtkWidget* dialog = NULL;
405     gui_error_dialog(&dialog, _("Out of memory error!"), FALSE);
406 }
407 
408 FILE*
gui_fopen(const gchar * name,const gchar * utf_name,const gchar * mode)409 gui_fopen(const gchar* name, const gchar* utf_name, const gchar* mode)
410 {
411     FILE* f;
412 
413     errno = 0;
414     f = fopen(name, mode);
415     if (!f) {
416         gchar *buf = g_strdup_printf("Error while opening file %s", utf_name);
417 
418         gui_errno_dialog(mainwindow, buf, errno);
419         g_free(buf);
420     }
421 
422     return f;
423 }
424 
425 gchar*
gui_filename_from_utf8(const gchar * old_name)426 gui_filename_from_utf8(const gchar* old_name)
427 {
428     GtkWidget* dialog;
429 
430     GError* error = NULL;
431     gchar* name = g_filename_from_utf8(old_name, -1, NULL, NULL, &error);
432 
433     if (!name) {
434         dialog = gtk_message_dialog_new(GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
435             _("An error occured when filename character set conversion:\n%s\n"
436               "The file operation probably failed."),
437             error->message);
438         gtk_dialog_run(GTK_DIALOG(dialog));
439         gtk_widget_destroy(dialog);
440         g_error_free(error);
441     }
442 
443     return name;
444 }
445 
446 gchar*
gui_filename_to_utf8(const gchar * old_name)447 gui_filename_to_utf8(const gchar* old_name)
448 {
449     GtkWidget* dialog;
450 
451     GError* error = NULL;
452     gchar* name = g_filename_to_utf8(old_name, -1, NULL, NULL, &error);
453 
454     if (!name) {
455         dialog = gtk_message_dialog_new(GTK_WINDOW(mainwindow), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
456             _("An error occured when filename character set conversion:\n%s\n"
457               "The file operation probably failed."),
458             error->message);
459         gtk_dialog_run(GTK_DIALOG(dialog));
460         gtk_widget_destroy(dialog);
461         g_error_free(error);
462     }
463 
464     return name;
465 }
466 
467 GtkWidget*
gui_combo_new(GtkListStore * ls)468 gui_combo_new(GtkListStore* ls)
469 {
470     GtkWidget* thing;
471     GtkCellRenderer* cell;
472 
473     thing = gtk_combo_box_new_with_model(GTK_TREE_MODEL(ls));
474     g_object_unref(ls);
475 
476     cell = gtk_cell_renderer_text_new();
477     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(thing), cell, TRUE);
478     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(thing), cell, "text", 0, NULL);
479 
480     return thing;
481 }
482 
gui_builder_from_file(const gchar * name,const struct menu_callback cb[])483 GtkBuilder* gui_builder_from_file(const gchar* name, const struct menu_callback cb[])
484 {
485     GtkBuilder* builder = gtk_builder_new();
486     GError* error = NULL;
487     guint i;
488 
489     if (!gtk_builder_add_from_file(builder, name, &error)) {
490         g_critical(_("%s.\nLoading widgets' description from %s file failed!\n"),
491             error->message, name);
492         g_error_free(error);
493         return NULL;
494     }
495 
496     if (cb)
497         for (i = 0; cb[i].widget_name; i++) {
498             GtkWidget* w = GTK_WIDGET(gtk_builder_get_object(builder, cb[i].widget_name));
499 
500             if (w)
501                 g_signal_connect_swapped(w, "activate", G_CALLBACK(cb[i].fn), cb[i].data);
502         }
503 
504     return builder;
505 }
506 
507 gboolean
gui_delete_noop(void)508 gui_delete_noop(void)
509 {
510     /* For dialogs, Response callback already hides this window */
511     return TRUE;
512 }
513 
gui_dialog_adjust(GtkWidget * dialog,gint default_id)514 void gui_dialog_adjust(GtkWidget* dialog, gint default_id)
515 {
516     gtk_container_set_border_width(GTK_CONTAINER(dialog), 4);
517     gtk_dialog_set_default_response(GTK_DIALOG(dialog), default_id);
518 }
519 
gui_set_escape_close(GtkWidget * window)520 void gui_set_escape_close(GtkWidget* window)
521 {
522     GtkAccelGroup* group = gtk_accel_group_new();
523     GClosure* closure = g_cclosure_new_swap(G_CALLBACK(gtk_widget_hide), window, NULL);
524 
525     gtk_accel_group_connect(group, GDK_Escape, 0, 0, closure);
526     gtk_window_add_accel_group(GTK_WINDOW(window), group);
527 }
528 
529 typedef struct _compare_data {
530     guint value;
531     gint number;
532 } compare_data;
533 
534 static gboolean
compare_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)535 compare_func(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer data)
536 {
537     compare_data* cmp_data = (compare_data*)data;
538     guint cur_val;
539 
540     gtk_tree_model_get(model, iter, 0, &cur_val, -1);
541 
542     if (cur_val == cmp_data->value) {
543         gint* indices = gtk_tree_path_get_indices(path);
544 
545         cmp_data->number = indices[0];
546         return TRUE; /* The desired element is found */
547     }
548 
549     return FALSE;
550 }
551 
552 gboolean
gui_set_active_combo_item(GtkWidget * combobox,GtkTreeModel * model,guint item)553 gui_set_active_combo_item(GtkWidget* combobox, GtkTreeModel* model, guint item)
554 {
555     compare_data cmp_data;
556 
557     cmp_data.value = item;
558     cmp_data.number = -1;
559     gtk_tree_model_foreach(model, compare_func, &cmp_data);
560 
561     if (cmp_data.number >= 0) {
562         gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), cmp_data.number);
563         return TRUE;
564     }
565 
566     return FALSE;
567 }
568 
569 typedef struct _str_cmp_data {
570     const gchar* str;
571     gboolean success;
572     GtkComboBoxText* combobox;
573 } str_cmp_data;
574 
575 static gboolean
str_cmp_func(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)576 str_cmp_func(GtkTreeModel* model, GtkTreePath* path, GtkTreeIter* iter, gpointer data)
577 {
578     gchar* item_str = NULL;
579     str_cmp_data* scd = (str_cmp_data*)data;
580 
581     gtk_tree_model_get(model, iter, 0, &item_str, -1);
582     if (!item_str)
583         return TRUE; /* Aborting due to error */
584 
585     if (!g_ascii_strcasecmp(item_str, scd->str)) {
586         scd->success = TRUE;
587         gtk_combo_box_set_active_iter(GTK_COMBO_BOX(scd->combobox), iter);
588         g_free(item_str);
589         return TRUE;
590     }
591 
592     g_free(item_str);
593     return FALSE;
594 }
595 
gui_combo_box_prepend_text_or_set_active(GtkComboBoxText * combobox,const gchar * text,gboolean force_active)596 void gui_combo_box_prepend_text_or_set_active(GtkComboBoxText* combobox, const gchar* text, gboolean force_active)
597 {
598     str_cmp_data scd;
599 
600     GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(combobox));
601 
602     scd.str = text;
603     scd.success = FALSE;
604     scd.combobox = combobox;
605     gtk_tree_model_foreach(model, str_cmp_func, &scd);
606 
607     if (!scd.success) {
608         gtk_combo_box_text_prepend_text(combobox, text);
609         if (force_active)
610             gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
611     }
612 }
613 
gui_get_text_entry(int length,void (* changedfunc)(),GtkWidget ** widget)614 gint gui_get_text_entry(int length,
615     void (*changedfunc)(),
616     GtkWidget** widget)
617 {
618     GtkWidget* thing;
619 
620     thing = gtk_entry_new();
621     gtk_entry_set_max_length(GTK_ENTRY(thing), length);
622 
623     *widget = thing;
624 
625     return g_signal_connect(thing, "changed",
626         G_CALLBACK(changedfunc), NULL);
627 }
628 
629 GtkWidget*
gui_get_widget(GtkBuilder * builder,const gchar * name,const gchar * file)630 gui_get_widget(GtkBuilder *builder, const gchar* name, const gchar* file)
631 {
632     GtkWidget* w = GTK_WIDGET(gtk_builder_get_object(builder, name));
633 
634     if (!w)
635         g_critical(_("GUI creation error: Widget '%s' is not found in %s file."), name, file);
636 
637     return w;
638 }
639 
640 static gboolean
call_menu(GtkWidget * widget,GdkEventButton * event,GtkMenu * menu)641 call_menu(GtkWidget* widget, GdkEventButton* event, GtkMenu* menu)
642 {
643     if (event->button == 3) {
644         gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
645         return TRUE;
646     }
647 
648     return FALSE;
649 }
650 
gui_popup_menu_attach(GtkWidget * menu,GtkWidget * widget,gpointer * user_data)651 void gui_popup_menu_attach(GtkWidget* menu, GtkWidget* widget, gpointer* user_data)
652 {
653     gtk_menu_attach_to_widget(GTK_MENU(menu), widget, NULL);
654     g_signal_connect(menu, "deactivate", G_CALLBACK(gtk_widget_hide), NULL);
655     g_signal_connect(widget, "button-press-event", G_CALLBACK(call_menu), menu);
656 }
657 
658 void
gui_get_pixel_size(GtkWidget * w,const char * text,gint * width,gint * height)659 gui_get_pixel_size(GtkWidget* w, const char* text, gint* width, gint* height)
660 {
661     PangoContext* context = gtk_widget_create_pango_context(w);
662     PangoLayout* layout = pango_layout_new(context);
663 
664     pango_layout_set_font_description(layout, w->style->font_desc);
665     pango_layout_set_text(layout, text, -1);
666     pango_layout_get_pixel_size(layout, width, height);
667 
668     g_object_unref(layout);
669     g_object_unref(context);
670 }
671 
672 GdkPixbuf*
gui_pixbuf_new_from_file(const gchar * path)673 gui_pixbuf_new_from_file(const gchar* path)
674 {
675     GError* error = NULL;
676     GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, &error);
677 
678     if (error) {
679         static GtkWidget *error_dialog = NULL;
680 
681         gui_error_dialog(&error_dialog, error->message, TRUE);
682         g_error_free(error);
683         pixbuf = NULL;
684     }
685 
686     return pixbuf;
687 }
688 
689 void
gui_add_icons(const icon_set * icons)690 gui_add_icons(const icon_set* icons)
691 {
692     gint i;
693 
694     for (i = 0; icons[i].gui_name; i++) {
695         gint j;
696 
697         for (j = 0; icons[i].sizes[j]; j++) {
698             gchar *icon_path = g_strdup_printf(DATADIR "/" PACKAGE "/%s-%i.png",
699                 icons[i].file_name, icons[i].sizes[j]);
700             GdkPixbuf* pixbuf = gui_pixbuf_new_from_file(icon_path);
701 
702             g_free(icon_path);
703             if (pixbuf) {
704                 gtk_icon_theme_add_builtin_icon(icons[i].gui_name, gdk_pixbuf_get_width(pixbuf), pixbuf);
705                 g_object_unref(G_OBJECT(pixbuf));
706             }
707         }
708     }
709 }
710