Deduplicate cr3 image loader
[geeqie.git] / src / toolbar.cc
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2017 The Geeqie Team
4  *
5  * Author: Colin Clark
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 along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "toolbar.h"
23
24 #include <cstddef>
25
26 #include <gdk-pixbuf/gdk-pixbuf.h>
27 #include <glib-object.h>
28 #include <glib.h>
29
30 #include <config.h>
31
32 #include "compat.h"
33 #include "editors.h"
34 #include "intl.h"
35 #include "layout-util.h"
36 #include "layout.h"
37 #include "main-defines.h"
38 #include "ui-fileops.h"
39 #include "ui-menu.h"
40 #include "ui-misc.h"
41
42 /** Implements the user-definable toolbar function
43  * Called from the Preferences/toolbar tab
44  **/
45
46 struct ToolbarData
47 {
48         GtkWidget *widget;
49         GtkWidget *vbox;
50         GtkWidget *add_button;
51
52         LayoutWindow *lw;
53 };
54
55 struct ToolbarButtonData
56 {
57         GtkWidget *button;
58         GtkWidget *button_label;
59         GtkWidget *image;
60
61         const gchar *name; /* GtkActionEntry terminology */
62         const gchar *stock_id;
63 };
64
65 static ToolbarData *toolbarlist[2];
66
67 struct UseableToolbarItems
68 {
69         const gchar *name; /* GtkActionEntry terminology */
70         const gchar *label;
71         const gchar *stock_id;
72 };
73
74 /**
75  * @brief
76  * @param widget Not used
77  * @param data Pointer to vbox list item
78  * @param up Up/Down movement
79  * @param single_step Move up/down one step, or to top/bottom
80  *
81  */
82 static void toolbar_item_move(GtkWidget *, gpointer data, gboolean up, gboolean single_step)
83 {
84         auto list_item = static_cast<GtkWidget *>(data);
85         GtkWidget *box;
86         gint pos = 0;
87
88         if (!list_item) return;
89         box = gtk_widget_get_ancestor(list_item, GTK_TYPE_BOX);
90         if (!box) return;
91
92         gtk_container_child_get(GTK_CONTAINER(box), list_item, "position", &pos, NULL);
93
94         if (single_step)
95                 {
96                 pos = up ? (pos - 1) : (pos + 1);
97                 if (pos < 0) pos = 0;
98                 }
99         else
100                 {
101                 pos = up ? 0 : -1;
102                 }
103
104         gtk_box_reorder_child(GTK_BOX(box), list_item, pos);
105 }
106
107 static void toolbar_item_move_up_cb(GtkWidget *widget, gpointer data)
108 {
109         toolbar_item_move(widget, data, TRUE, TRUE);
110 }
111
112 static void toolbar_item_move_down_cb(GtkWidget *widget, gpointer data)
113 {
114         toolbar_item_move(widget, data, FALSE, TRUE);
115 }
116
117 static void toolbar_item_move_top_cb(GtkWidget *widget, gpointer data)
118 {
119         toolbar_item_move(widget, data, TRUE, FALSE);
120 }
121
122 static void toolbar_item_move_bottom_cb(GtkWidget *widget, gpointer data)
123 {
124         toolbar_item_move(widget, data, FALSE, FALSE);
125 }
126
127 static void toolbar_item_delete_cb(GtkWidget *, gpointer data)
128 {
129         gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(GTK_WIDGET(data))), GTK_WIDGET(data));
130 }
131
132 static void toolbar_menu_popup(GtkWidget *widget)
133 {
134         GtkWidget *menu;
135
136         menu = popup_menu_short_lived();
137
138         if (widget)
139                 {
140                 menu_item_add_icon(menu, _("Move to _top"), GQ_ICON_GO_TOP, G_CALLBACK(toolbar_item_move_top_cb), widget);
141                 menu_item_add_icon(menu, _("Move _up"), GQ_ICON_GO_UP, G_CALLBACK(toolbar_item_move_up_cb), widget);
142                 menu_item_add_icon(menu, _("Move _down"), GQ_ICON_GO_DOWN, G_CALLBACK(toolbar_item_move_down_cb), widget);
143                 menu_item_add_icon(menu, _("Move to _bottom"), GQ_ICON_GO_BOTTOM, G_CALLBACK(toolbar_item_move_bottom_cb), widget);
144                 menu_item_add_divider(menu);
145                 menu_item_add_icon(menu, _("Remove"), GQ_ICON_DELETE, G_CALLBACK(toolbar_item_delete_cb), widget);
146                 menu_item_add_divider(menu);
147                 }
148
149         gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
150 }
151
152 static gboolean toolbar_press_cb(GtkGesture *, int, double, double, gpointer data)
153 {
154         auto button_data = static_cast<ToolbarButtonData *>(data);
155
156         toolbar_menu_popup(button_data->button);
157
158         return TRUE;
159 }
160
161 static void get_toolbar_item(const gchar *name, gchar **label, gchar **stock_id)
162 {
163         ActionItem *action_item;
164         GList *list;
165         GList *work;
166         *label = nullptr;
167         *stock_id = nullptr;
168
169         list = get_action_items();
170
171         work = list;
172         while (work)
173                 {
174                 action_item = static_cast<ActionItem *>(work->data);
175                 if (g_strcmp0(action_item->name, name) == 0)
176                         {
177                         *label = g_strdup(action_item->label);
178                         *stock_id = g_strdup(action_item->icon_name);
179                         break;
180                         }
181
182                 work = work->next;
183                 }
184
185         action_items_free(list);
186 }
187
188 static void toolbar_item_free(ToolbarButtonData *tbbd)
189 {
190         if (!tbbd) return;
191
192         g_free(const_cast<gchar *>(tbbd->name));
193         g_free(const_cast<gchar *>(tbbd->stock_id));
194         g_free(const_cast<ToolbarButtonData *>(tbbd));
195 }
196
197 static void toolbar_button_free(GtkWidget *widget)
198 {
199         g_free(g_object_get_data(G_OBJECT(widget), "toolbar_add_name"));
200         g_free(g_object_get_data(G_OBJECT(widget), "toolbar_add_label"));
201         g_free(g_object_get_data(G_OBJECT(widget), "toolbar_add_stock_id"));
202 }
203
204 static void toolbarlist_add_button(const gchar *name, const gchar *label,
205                                                                         const gchar *stock_id, GtkBox *box)
206 {
207         ToolbarButtonData *toolbar_entry;
208         GtkWidget *hbox;
209         GtkGesture *gesture;
210
211         toolbar_entry = g_new(ToolbarButtonData,1);
212         toolbar_entry->button = gtk_button_new();
213         gtk_button_set_relief(GTK_BUTTON(toolbar_entry->button), GTK_RELIEF_NONE);
214         gq_gtk_box_pack_start(GTK_BOX(box), toolbar_entry->button, FALSE, FALSE, 0);
215         gtk_widget_show(toolbar_entry->button);
216
217         g_object_set_data_full(G_OBJECT(toolbar_entry->button), "toolbarbuttondata",
218         toolbar_entry, reinterpret_cast<GDestroyNotify>(toolbar_item_free));
219
220         hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, PREF_PAD_BUTTON_GAP);
221         gq_gtk_container_add(GTK_WIDGET(toolbar_entry->button), hbox);
222         gtk_widget_show(hbox);
223
224         toolbar_entry->button_label = gtk_label_new(label);
225         toolbar_entry->name = g_strdup(name);
226         toolbar_entry->stock_id = g_strdup(stock_id);
227
228 #if HAVE_GTK4
229         gesture = gtk_gesture_click_new();
230         gtk_widget_add_controller(toolbar_entry->button, GTK_EVENT_CONTROLLER(gesture));
231 #else
232         gesture = gtk_gesture_multi_press_new(toolbar_entry->button);
233 #endif
234         gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(gesture), MOUSE_BUTTON_RIGHT);
235         g_signal_connect(gesture, "released", G_CALLBACK(toolbar_press_cb), toolbar_entry);
236
237         if (toolbar_entry->stock_id)
238                 {
239                 GdkPixbuf *pixbuf;
240                 gchar *iconl;
241                 iconl = path_from_utf8(toolbar_entry->stock_id);
242                 pixbuf = gdk_pixbuf_new_from_file(iconl, nullptr);
243                 g_free(iconl);
244                 if (pixbuf)
245                         {
246                         GdkPixbuf *scaled;
247                         gint w;
248                         gint h;
249
250                         w = h = 16;
251                         gtk_icon_size_lookup(GTK_ICON_SIZE_BUTTON, &w, &h);
252
253                         scaled = gdk_pixbuf_scale_simple(pixbuf, w, h,
254                                                          GDK_INTERP_BILINEAR);
255                         toolbar_entry->image = gtk_image_new_from_pixbuf(scaled);
256
257                         g_object_unref(scaled);
258                         g_object_unref(pixbuf);
259                         }
260                 else
261                         {
262                         toolbar_entry->image = gtk_image_new_from_stock(toolbar_entry->stock_id,
263                                                                                                                 GTK_ICON_SIZE_BUTTON);
264                         }
265                 }
266         else
267                 {
268                 toolbar_entry->image = gtk_image_new_from_icon_name(GQ_ICON_GO_JUMP,
269                                                                                                                 GTK_ICON_SIZE_BUTTON);
270                 }
271         gq_gtk_box_pack_start(GTK_BOX(hbox), toolbar_entry->image, FALSE, FALSE, 0);
272         gtk_widget_show(toolbar_entry->image);
273         gq_gtk_box_pack_start(GTK_BOX(hbox), toolbar_entry->button_label, FALSE, FALSE, 0);
274         gtk_widget_show(toolbar_entry->button_label);
275 }
276
277 static void toolbarlist_add_cb(GtkWidget *widget, gpointer data)
278 {
279         auto name = static_cast<const gchar *>(g_object_get_data(G_OBJECT(widget), "toolbar_add_name"));
280         auto label = static_cast<const gchar *>(g_object_get_data(G_OBJECT(widget), "toolbar_add_label"));
281         auto stock_id = static_cast<const gchar *>(g_object_get_data(G_OBJECT(widget), "toolbar_add_stock_id"));
282         auto tbbd = static_cast<ToolbarData *>(data);
283
284         toolbarlist_add_button(name, label, stock_id, GTK_BOX(tbbd->vbox));
285 }
286
287 static void get_desktop_data(const gchar *name, gchar **label, gchar **stock_id)
288 {
289         GList *editors_list;
290         GList *work;
291         *label = nullptr;
292         *stock_id = nullptr;
293
294         editors_list = editor_list_get();
295         work = editors_list;
296         while (work)
297                 {
298                 auto editor = static_cast<const EditorDescription *>(work->data);
299
300                 if (g_strcmp0(name, editor->key) == 0)
301                         {
302                         *label = g_strdup(editor->name);
303                         *stock_id = g_strconcat(editor->icon, ".desktop", NULL);
304                         break;
305                         }
306                 work = work->next;
307                 }
308         g_list_free(editors_list);
309 }
310
311 static void toolbar_menu_add_popup(GtkWidget *, gpointer data)
312 {
313         ActionItem *action_item;
314         auto toolbarlist = static_cast<ToolbarData *>(data);
315         GList *list;
316         GList *work;
317         GtkWidget *item;
318         GtkWidget *menu;
319
320         menu = popup_menu_short_lived();
321
322         item = menu_item_add_stock(menu, "Separator", "Separator", G_CALLBACK(toolbarlist_add_cb), toolbarlist);
323         g_object_set_data(G_OBJECT(item), "toolbar_add_name", g_strdup("Separator"));
324         g_object_set_data(G_OBJECT(item), "toolbar_add_label", g_strdup("Separator"));
325         g_object_set_data(G_OBJECT(item), "toolbar_add_stock_id", g_strdup("no-icon"));
326         g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(toolbar_button_free), item);
327
328         list = get_action_items();
329
330         work = list;
331         while (work)
332                 {
333                 action_item = static_cast<ActionItem *>(work->data);
334
335                 item = menu_item_add_stock(menu, action_item->label, action_item->icon_name, G_CALLBACK(toolbarlist_add_cb), toolbarlist);
336                 g_object_set_data(G_OBJECT(item), "toolbar_add_name", g_strdup(action_item->name));
337                 g_object_set_data(G_OBJECT(item), "toolbar_add_label", g_strdup(action_item->label));
338                 g_object_set_data(G_OBJECT(item), "toolbar_add_stock_id", g_strdup(action_item->icon_name));
339                 g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(toolbar_button_free), item);
340
341                 work = work->next;
342                 }
343
344         action_items_free(list);
345
346         gtk_menu_popup_at_pointer(GTK_MENU(menu), nullptr);
347 }
348
349 static gboolean toolbar_menu_add_cb(GtkWidget *widget, gpointer data)
350 {
351         auto toolbarlist = static_cast<ToolbarData *>(data);
352
353         toolbar_menu_add_popup(widget, toolbarlist);
354         return TRUE;
355 }
356
357 /**
358  * @brief For each layoutwindow, clear toolbar and reload with current selection
359  * @param bar Main or Status toolbar
360  *
361  */
362 void toolbar_apply(ToolbarType bar)
363 {
364         LayoutWindow *lw;
365         GList *work_windows;
366         GList *work_toolbar;
367
368         work_windows = layout_window_list;
369         while (work_windows)
370                 {
371                 lw = static_cast<LayoutWindow *>(work_windows->data);
372
373                 layout_toolbar_clear(lw, bar);
374
375                 work_toolbar = gtk_container_get_children(GTK_CONTAINER(toolbarlist[bar]->vbox));
376                 while (work_toolbar)
377                         {
378                         auto button = static_cast<GtkButton *>(work_toolbar->data);
379                         ToolbarButtonData *tbbd;
380
381                         tbbd = static_cast<ToolbarButtonData *>(g_object_get_data(G_OBJECT(button),"toolbarbuttondata"));
382                         layout_toolbar_add(lw, bar, tbbd->name);
383
384                         work_toolbar = work_toolbar->next;
385                         }
386                 g_list_free(work_toolbar);
387
388                 work_windows = work_windows->next;
389                 }
390
391 }
392
393 /**
394  * @brief Load the current toolbar items into the vbox
395  * @param lw
396  * @param box The vbox displayed in the preferences Toolbar tab
397  * @param bar Main or Status toolbar
398  *
399  * Get the current contents of the toolbar, both menu items
400  * and desktop items, and load them into the vbox
401  */
402 static void toolbarlist_populate(LayoutWindow *lw, GtkBox *box, ToolbarType bar)
403 {
404         GList *work = g_list_first(lw->toolbar_actions[bar]);
405
406         while (work)
407                 {
408                 auto name = static_cast<gchar *>(work->data);
409                 gchar *label;
410                 gchar *icon;
411                 work = work->next;
412
413                 if (file_extension_match(name, ".desktop"))
414                         {
415                         get_desktop_data(name, &label, &icon);
416                         }
417                 else
418                         {
419                         get_toolbar_item(name, &label, &icon);
420                         }
421
422                 if (g_strcmp0(name, "Separator") != 0)
423                         {
424                         toolbarlist_add_button(name, label, icon, box);
425                         }
426                 else
427                         {
428                         toolbarlist_add_button(name, name, "no-icon", box);
429                         }
430                 }
431 }
432
433 GtkWidget *toolbar_select_new(LayoutWindow *lw, ToolbarType bar)
434 {
435         GtkWidget *scrolled;
436         GtkWidget *tbar;
437         GtkWidget *add_box;
438
439         if (!lw) return nullptr;
440
441         if (!toolbarlist[bar])
442                 {
443                 toolbarlist[bar] = g_new0(ToolbarData, 1);
444                 }
445         toolbarlist[bar]->lw = lw;
446
447         toolbarlist[bar]->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, PREF_PAD_GAP);
448         gtk_widget_show(toolbarlist[bar]->widget);
449
450         scrolled = gq_gtk_scrolled_window_new(nullptr, nullptr);
451         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
452                                                         GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
453         gq_gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled), GTK_SHADOW_NONE);
454         gq_gtk_box_pack_start(GTK_BOX(toolbarlist[bar]->widget), scrolled, TRUE, TRUE, 0);
455         gtk_widget_show(scrolled);
456
457         toolbarlist[bar]->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
458         gtk_widget_show(toolbarlist[bar]->vbox);
459         gq_gtk_container_add(GTK_WIDGET(scrolled), toolbarlist[bar]->vbox);
460         gtk_viewport_set_shadow_type(GTK_VIEWPORT(gtk_bin_get_child(GTK_BIN(scrolled))),
461                                                                                                                                 GTK_SHADOW_NONE);
462
463         add_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
464         gtk_widget_show(add_box);
465         gq_gtk_box_pack_end(GTK_BOX(toolbarlist[bar]->widget), add_box, FALSE, FALSE, 0);
466         tbar = pref_toolbar_new(add_box);
467         toolbarlist[bar]->add_button = pref_toolbar_button(tbar, GQ_ICON_ADD, _("Add"), FALSE,
468                                                                                         _("Add Toolbar Item"),
469                                                                                         G_CALLBACK(toolbar_menu_add_cb), toolbarlist[bar]);
470         gtk_widget_show(toolbarlist[bar]->add_button);
471
472         toolbarlist_populate(lw,GTK_BOX(toolbarlist[bar]->vbox), bar);
473
474         return toolbarlist[bar]->widget;
475 }
476
477 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */