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