Compile modules based on HAVE_* features
[geeqie.git] / src / menu.cc
1 /*
2  * Copyright (C) 2004 John Ellis
3  * Copyright (C) 2008 - 2016 The Geeqie Team
4  *
5  * Author: John Ellis
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 "menu.h"
23
24 #include <gdk/gdk.h>
25
26 #include "collect-io.h"
27 #include "collect-table.h"
28 #include "collect.h"
29 #include "editors.h"
30 #include "intl.h"
31 #include "main-defines.h"
32 #include "pixbuf-util.h"
33 #include "ui-menu.h"
34
35 static GtkWidget *real_submenu_add_alter(GtkWidget *menu, GCallback func, gpointer data,
36                                          GtkAccelGroup *accel_group);
37
38 /*
39  *-----------------------------------------------------------------------------
40  * menu utils
41  *-----------------------------------------------------------------------------
42  */
43
44 static GtkWidget *add_menu_item(GtkWidget *menu, gchar *label, GtkAccelGroup *accel_group,
45                                 guint accel_key, guint accel_mods, GCallback func, gpointer data)
46 {
47         GtkWidget *item;
48
49         item = gtk_menu_item_new_with_label(label);
50         gtk_widget_add_accelerator(item, "activate", accel_group, accel_key, static_cast<GdkModifierType>(accel_mods), GTK_ACCEL_VISIBLE);
51         g_signal_connect(G_OBJECT(item), "activate", func, data);
52         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
53         gtk_widget_show(item);
54
55         return item;
56 }
57
58 gpointer submenu_item_get_data(GtkWidget *menu)
59 {
60         if (!gtk_widget_get_parent(menu) || !GTK_IS_MENU(gtk_widget_get_parent(menu))) return nullptr;
61
62         return g_object_get_data(G_OBJECT(gtk_widget_get_parent(menu)), "submenu_data");
63 }
64
65 /*
66  *-----------------------------------------------------------------------------
67  * edit menu
68  *-----------------------------------------------------------------------------
69  */
70 static void edit_item_destroy_cb(GtkWidget *, gpointer data)
71 {
72         g_free(data);
73 }
74
75 static void add_edit_items(GtkWidget *menu, GCallback func, GList *fd_list)
76 {
77         GList *editors_list = editor_list_get();
78         GList *work = editors_list;
79
80         while (work)
81                 {
82                 auto editor = static_cast<const EditorDescription *>(work->data);
83                 work = work->next;
84                 gboolean active = TRUE;
85
86                 if (fd_list && EDITOR_ERRORS(editor_command_parse(editor, fd_list, FALSE, nullptr)))
87                         active = FALSE;
88
89                 if (active)
90                         {
91                         GtkWidget *item;
92                         const gchar *stock_id = nullptr;
93                         gchar *key = g_strdup(editor->key);
94
95                         if (editor->icon && register_theme_icon_as_stock(key, editor->icon))
96                                 stock_id = key;
97
98                         item = menu_item_add_stock(menu, editor->name, stock_id, func, key);
99                         g_signal_connect(G_OBJECT(item), "destroy", G_CALLBACK(edit_item_destroy_cb), key);
100                         }
101                 }
102
103         g_list_free(editors_list);
104 }
105
106
107 GtkWidget *submenu_add_edit(GtkWidget *menu, GtkWidget **menu_item, GCallback func, gpointer data, GList *fd_list)
108 {
109         GtkWidget *item;
110         GtkWidget *submenu;
111         GtkAccelGroup *accel_group;
112
113         accel_group = gtk_accel_group_new();
114         item = menu_item_add(menu, _("_Plugins"), nullptr, nullptr);
115
116         submenu = gtk_menu_new();
117         g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
118         gtk_menu_set_accel_group(GTK_MENU(submenu), accel_group);
119         g_object_set_data(G_OBJECT(submenu), "accel_group", accel_group);
120
121         add_edit_items(submenu, func, fd_list);
122
123         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
124
125         if (menu_item) *menu_item = item;
126
127         return submenu;
128 }
129
130 /*
131  *-----------------------------------------------------------------------------
132  * sorting
133  *-----------------------------------------------------------------------------
134  */
135
136 gchar *sort_type_get_text(SortType method)
137 {
138         switch (method)
139                 {
140                 case SORT_SIZE:
141                         return _("Sort by size");
142                         break;
143                 case SORT_TIME:
144                         return _("Sort by date");
145                         break;
146                 case SORT_CTIME:
147                         return _("Sort by file creation date");
148                         break;
149                 case SORT_EXIFTIME:
150                         return _("Sort by Exif date original");
151                         break;
152                 case SORT_EXIFTIMEDIGITIZED:
153                         return _("Sort by Exif date digitized");
154                         break;
155                 case SORT_NONE:
156                         return _("Unsorted");
157                         break;
158                 case SORT_PATH:
159                         return _("Sort by path");
160                         break;
161                 case SORT_NUMBER:
162                         return _("Sort by number");
163                         break;
164                 case SORT_RATING:
165                         return _("Sort by rating");
166                         break;
167                 case SORT_CLASS:
168                         return _("Sort by class");
169                         break;
170                 case SORT_NAME:
171                 default:
172                         return _("Sort by name");
173                         break;
174                 }
175
176         return nullptr;
177 }
178
179 static GtkWidget *submenu_add_sort_item(GtkWidget *menu,
180                                         GCallback func, SortType type,
181                                         gboolean show_current, SortType show_type)
182 {
183         GtkWidget *item;
184
185         if (show_current)
186                 {
187                 item = menu_item_add_radio(menu,
188                                            sort_type_get_text(type), GINT_TO_POINTER((gint)type), (type == show_type),
189                                            func, GINT_TO_POINTER((gint)type));
190                 }
191         else
192                 {
193                 item = menu_item_add(menu, sort_type_get_text(type),
194                                      func, GINT_TO_POINTER((gint)type));
195                 }
196
197         return item;
198 }
199
200 GtkWidget *submenu_add_sort(GtkWidget *menu, GCallback func, gpointer data,
201                             gboolean include_none, gboolean include_path,
202                             gboolean show_current, SortType type)
203 {
204         GtkWidget *submenu;
205
206         submenu = gtk_menu_new();
207         g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
208
209         submenu_add_sort_item(submenu, func, SORT_NAME, show_current, type);
210         submenu_add_sort_item(submenu, func, SORT_NUMBER, show_current, type);
211         submenu_add_sort_item(submenu, func, SORT_TIME, show_current, type);
212         submenu_add_sort_item(submenu, func, SORT_CTIME, show_current, type);
213         submenu_add_sort_item(submenu, func, SORT_EXIFTIME, show_current, type);
214         submenu_add_sort_item(submenu, func, SORT_EXIFTIMEDIGITIZED, show_current, type);
215         submenu_add_sort_item(submenu, func, SORT_SIZE, show_current, type);
216         submenu_add_sort_item(submenu, func, SORT_RATING, show_current, type);
217         submenu_add_sort_item(submenu, func, SORT_CLASS, show_current, type);
218         if (include_path) submenu_add_sort_item(submenu, func, SORT_PATH, show_current, type);
219         if (include_none) submenu_add_sort_item(submenu, func, SORT_NONE, show_current, type);
220
221         if (menu)
222                 {
223                 GtkWidget *item;
224
225                 item = menu_item_add(menu, _("Sort"), nullptr, nullptr);
226                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
227                 return item;
228                 }
229
230         return submenu;
231 }
232
233 GtkWidget *submenu_add_dir_sort(GtkWidget *menu, GCallback func, gpointer data,
234                             gboolean include_none, gboolean include_path,
235                             gboolean show_current, SortType type)
236 {
237         GtkWidget *submenu;
238
239         submenu = gtk_menu_new();
240         g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
241
242         submenu_add_sort_item(submenu, func, SORT_NAME, show_current, type);
243         submenu_add_sort_item(submenu, func, SORT_NUMBER, show_current, type);
244         submenu_add_sort_item(submenu, func, SORT_TIME, show_current, type);
245         if (include_path) submenu_add_sort_item(submenu, func, SORT_PATH, show_current, type);
246         if (include_none) submenu_add_sort_item(submenu, func, SORT_NONE, show_current, type);
247
248         if (menu)
249                 {
250                 GtkWidget *item;
251
252                 item = menu_item_add(menu, _("Sort"), nullptr, nullptr);
253                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
254                 return item;
255                 }
256
257         return submenu;
258 }
259
260 gchar *zoom_type_get_text(ZoomMode method)
261 {
262         switch (method)
263                 {
264                 case ZOOM_RESET_ORIGINAL:
265                         return _("Zoom to original size");
266                         break;
267                 case ZOOM_RESET_FIT_WINDOW:
268                         return _("Fit image to window");
269                         break;
270                 case ZOOM_RESET_NONE:
271                         return _("Leave Zoom at previous setting");
272                         break;
273                 default:
274                         return _("Zoom to original size");
275                         break;
276                 }
277
278         return nullptr;
279 }
280
281 static GtkWidget *submenu_add_zoom_item(GtkWidget *menu,
282                                         GCallback func, ZoomMode mode,
283                                         gboolean show_current, ZoomMode show_mode)
284 {
285         GtkWidget *item;
286
287         if (show_current)
288                 {
289                 item = menu_item_add_radio(menu,
290                                            zoom_type_get_text(mode), GINT_TO_POINTER((gint)mode), (mode == show_mode),
291                                            func, GINT_TO_POINTER((gint)mode));
292                 }
293         else
294                 {
295                 item = menu_item_add(menu, zoom_type_get_text(mode),
296                                      func, GINT_TO_POINTER((gint)mode));
297                 }
298
299         return item;
300 }
301
302 GtkWidget *submenu_add_zoom(GtkWidget *menu, GCallback func, gpointer data,
303                             gboolean, gboolean, gboolean show_current, ZoomMode mode)
304 {
305         GtkWidget *submenu;
306
307         if (!menu)
308                 {
309                 submenu = gtk_menu_new();
310                 g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
311                 }
312         else
313                 {
314                 submenu = menu;
315                 }
316
317         submenu_add_zoom_item(submenu, func, ZOOM_RESET_ORIGINAL, show_current, mode);
318         submenu_add_zoom_item(submenu, func, ZOOM_RESET_FIT_WINDOW, show_current, mode);
319         submenu_add_zoom_item(submenu, func, ZOOM_RESET_NONE, show_current, mode);
320
321         if (menu)
322                 {
323                 GtkWidget *item;
324
325                 item = menu_item_add(menu, _("Zoom"), nullptr, nullptr);
326                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
327                 return item;
328                 }
329
330         return submenu;
331 }
332
333 /*
334  *-----------------------------------------------------------------------------
335  * altering
336  *-----------------------------------------------------------------------------
337  */
338
339 gchar *alter_type_get_text(AlterType type)
340 {
341         switch (type)
342                 {
343                 case ALTER_ROTATE_90:
344                         return _("Rotate clockwise 90°");
345                         break;
346                 case ALTER_ROTATE_90_CC:
347                         return _("Rotate counterclockwise 90°");
348                         break;
349                 case ALTER_ROTATE_180:
350                         return _("Rotate 180°");
351                         break;
352                 case ALTER_MIRROR:
353                         return _("Mirror");
354                         break;
355                 case ALTER_FLIP:
356                         return _("Flip");
357                         break;
358                 case ALTER_NONE:
359                         return _("Original state");
360                         break;
361                 default:
362                         break;
363                 }
364
365         return nullptr;
366 }
367
368 static void submenu_add_alter_item(GtkWidget *menu, GCallback func, AlterType type,
369                                    GtkAccelGroup *accel_group, guint accel_key, guint accel_mods)
370 {
371         if (accel_group)
372                 {
373                 add_menu_item(menu, alter_type_get_text(type), accel_group,
374                               accel_key, accel_mods, func, GINT_TO_POINTER((gint)type));
375
376                 }
377         else
378                 {
379                 menu_item_add(menu, alter_type_get_text(type), func, GINT_TO_POINTER((gint)type));
380                 }
381 }
382
383 static GtkWidget *real_submenu_add_alter(GtkWidget *menu, GCallback func, gpointer data,
384                                          GtkAccelGroup *accel_group)
385 {
386         GtkWidget *submenu;
387
388         submenu = gtk_menu_new();
389         g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
390
391         submenu_add_alter_item(submenu, func, ALTER_ROTATE_90, accel_group, ']', 0);
392         submenu_add_alter_item(submenu, func, ALTER_ROTATE_90_CC, accel_group, '[', 0);
393         submenu_add_alter_item(submenu, func, ALTER_ROTATE_180, accel_group, 'R', GDK_SHIFT_MASK);
394         submenu_add_alter_item(submenu, func, ALTER_MIRROR, accel_group, 'M', GDK_SHIFT_MASK);
395         submenu_add_alter_item(submenu, func, ALTER_FLIP, accel_group, 'F', GDK_SHIFT_MASK);
396         submenu_add_alter_item(submenu, func, ALTER_NONE, accel_group, 'O', GDK_SHIFT_MASK);
397
398         if (menu)
399                 {
400                 GtkWidget *item;
401
402                 item = menu_item_add(menu, _("_Orientation"), nullptr, nullptr);
403                 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
404                 return item;
405                 }
406
407         return submenu;
408 }
409
410 GtkWidget *submenu_add_alter(GtkWidget *menu, GCallback func, gpointer data)
411 {
412         GtkAccelGroup *accel;
413
414         accel = gtk_accel_group_new();
415         return real_submenu_add_alter(menu, func, data, accel); //last accel gr
416 }
417
418 /*
419  *-----------------------------------------------------------------------------
420  * collections
421  *-----------------------------------------------------------------------------
422  */
423
424 /**
425  * @brief Add submenu consisting of "New collection", and list of existing collections to a right-click menu.
426  * @param[in] menu
427  * @param[in] func
428  * @param[in] collection_list Type gchar
429  * @param[in] data
430  *
431  *  Used by all image windows
432  */
433 static void add_collection_list(GtkWidget *menu, GCallback func, GList *collection_list, gpointer)
434 {
435         GList *work;
436         gint index = 0; /* index to existing collection list menu item selected */
437
438         work = collection_list;
439         while (work)
440                 {
441                 auto collection_name = static_cast<const gchar *>(work->data);
442
443                 menu_item_add(menu, collection_name, func,
444                                                                                                         GINT_TO_POINTER(index));
445                 work = work->next;
446                 index++;
447                 }
448 }
449
450 GtkWidget *submenu_add_collections(GtkWidget *menu, GtkWidget **menu_item,
451                                                                                 GCallback func, gpointer data)
452 {
453         GtkWidget *item;
454         GtkWidget *submenu;
455         GList *collection_list = nullptr;
456
457         item = menu_item_add(menu, _("_Add to Collection"), nullptr, nullptr);
458
459         submenu = gtk_menu_new();
460         g_object_set_data(G_OBJECT(submenu), "submenu_data", data);
461
462         menu_item_add_stock_sensitive(submenu, _("New collection"),
463                                         GQ_ICON_COLLECTION, TRUE, G_CALLBACK(func), GINT_TO_POINTER(-1));
464         menu_item_add_divider(submenu);
465
466         collect_manager_list(&collection_list,nullptr,nullptr);
467         add_collection_list(submenu, func, collection_list, data);
468
469         gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
470         if (menu_item) *menu_item = item;
471
472         g_list_free(collection_list);
473
474         return submenu;
475 }
476
477 /**
478  * @brief Add file selection list to a collection
479  * @param[in] selection_list Selection list of ::_FileData
480  * @param[in] data Index to the collection list menu item selected, or -1 for new collection
481  *
482  *
483  */
484 void pop_menu_collections(GList *selection_list, gpointer data)
485 {
486         CollectWindow *cw;
487         gchar *collection_name;
488         GList *collection_list = nullptr;
489         gchar *name;
490         const gint index = GPOINTER_TO_INT(data);
491
492         if (index >= 0)
493                 {
494                 collect_manager_list(&collection_list, nullptr, nullptr);
495                 collection_name = static_cast<gchar *>(g_list_nth_data(collection_list, index));
496                 name = collection_path(collection_name);
497                 cw = collection_window_new(name);
498                 g_free(name);
499                 g_list_free_full(collection_list, g_free);
500                 }
501         else
502                 {
503                 cw = collection_window_new(nullptr);
504                 }
505
506         collection_table_add_filelist(cw->table, selection_list);
507 }
508
509 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */