Fix missing translation
[geeqie.git] / src / ui-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 "ui-menu.h"
23
24 #include <config.h>
25
26 #include "layout.h"
27
28 /*
29  *-----------------------------------------------------------------------------
30  * menu items
31  *-----------------------------------------------------------------------------
32  */
33
34 /**
35  * @brief Add accelerator key to a window popup menu
36  * @param menu
37  * @param accel_group
38  * @param window_keys
39  *
40  * This is used only so that the user can see the applicable
41  * shortcut key displayed in the menu. The actual handling of
42  * the keystroke is done elsewhere in the code.
43  */
44 static void menu_item_add_accelerator(GtkWidget *menu, GtkAccelGroup *accel_group, hard_coded_window_keys *window_keys)
45 {
46         gchar *label;
47         gchar *label_text;
48         gchar **label_stripped;
49         gint i = 0;
50
51         label = g_strdup(gtk_menu_item_get_label(GTK_MENU_ITEM(menu)));
52
53         pango_parse_markup(label, -1, '_', nullptr, &label_text, nullptr, nullptr);
54
55         label_stripped = g_strsplit(label_text, "...", 2);
56
57         while (window_keys[i].text != nullptr)
58                 {
59                 if (g_strcmp0(window_keys[i].text, label_stripped[0]) == 0)
60                         {
61                         gtk_widget_add_accelerator(menu, "activate", accel_group, window_keys[i].key_value, window_keys[i].mask, GTK_ACCEL_VISIBLE);
62
63                         break;
64                         }
65                 i++;
66                 }
67
68         g_free(label);
69         g_free(label_text);
70         g_strfreev(label_stripped);
71 }
72
73 /**
74  * @brief Callback for the actions GList sort function
75  * @param a
76  * @param b
77  * @returns
78  *
79  * Sort the action entries so that the non-shifted and non-control
80  * entries are at the start of the list. The user then sees the basic
81  * non-modified key shortcuts displayed in the menus.
82  */
83 static gint actions_sort_cb(gconstpointer a, gconstpointer b)
84 {
85         const gchar *accel_path_a;
86         GtkAccelKey key_a;
87         const gchar *accel_path_b;
88         GtkAccelKey key_b;
89
90         accel_path_a = gtk_action_get_accel_path(GTK_ACTION(a));
91         accel_path_b = gtk_action_get_accel_path(GTK_ACTION(b));
92
93         if (accel_path_a && gtk_accel_map_lookup_entry(accel_path_a, &key_a) && accel_path_b && gtk_accel_map_lookup_entry(accel_path_b, &key_b))
94                 {
95                 if (key_a.accel_mods < key_b.accel_mods) return -1;
96                 if (key_a.accel_mods > key_b.accel_mods) return 1;
97                 }
98
99         return 0;
100 }
101
102 /**
103  * @brief Add accelerator key to main window popup menu
104  * @param menu
105  * @param accel_group
106  *
107  * This is used only so that the user can see the applicable
108  * shortcut key displayed in the menu. The actual handling of
109  * the keystroke is done elsewhere in the code.
110  */
111 static void menu_item_add_main_window_accelerator(GtkWidget *menu, GtkAccelGroup *accel_group)
112 {
113         gchar *menu_label;
114         gchar *menu_label_text;
115         gchar *action_label;
116         gchar *action_label_text;
117         LayoutWindow *lw;
118         GList *groups;
119         GList *actions;
120         GtkAction *action;
121         const gchar *accel_path;
122         GtkAccelKey key;
123
124         menu_label = g_strdup(gtk_menu_item_get_label(GTK_MENU_ITEM(menu)));
125
126         pango_parse_markup(menu_label, -1, '_', nullptr, &menu_label_text, nullptr, nullptr);
127
128         lw = static_cast<LayoutWindow *>(layout_window_list->data); /* get the actions from the first window, it should not matter, they should be the same in all windows */
129
130         g_assert(lw && lw->ui_manager);
131         groups = gtk_ui_manager_get_action_groups(lw->ui_manager);
132
133         while (groups)
134                 {
135                 actions = gtk_action_group_list_actions(GTK_ACTION_GROUP(groups->data));
136                 actions = g_list_sort(actions, actions_sort_cb);
137
138                 while (actions)
139                         {
140                         action = GTK_ACTION(actions->data);
141                         accel_path = gtk_action_get_accel_path(action);
142                         if (accel_path && gtk_accel_map_lookup_entry(accel_path, &key))
143                                 {
144                                 g_object_get(action, "label", &action_label, NULL);
145
146                                 pango_parse_markup(action_label, -1, '_', nullptr, &action_label_text, nullptr, nullptr);
147
148                                 if (g_strcmp0(action_label_text, menu_label_text) == 0)
149                                         {
150                                         if (key.accel_key != 0)
151                                                 {
152                                                 gtk_widget_add_accelerator(menu, "activate", accel_group, key.accel_key, key.accel_mods, GTK_ACCEL_VISIBLE);
153
154                                                 break;
155                                                 }
156                                         }
157                                 g_free(action_label_text);
158                                 g_free(action_label);
159                                 }
160                         actions = actions->next;
161                         }
162                 groups = groups->next;
163                 }
164
165         g_free(menu_label);
166         g_free(menu_label_text);
167 }
168
169 static void menu_item_finish(GtkWidget *menu, GtkWidget *item, GCallback func, gpointer data)
170 {
171         if (func) g_signal_connect(G_OBJECT(item), "activate", func, data);
172         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
173         gtk_widget_show(item);
174 }
175
176 GtkWidget *menu_item_add(GtkWidget *menu, const gchar *label,
177                          GCallback func, gpointer data)
178 {
179         GtkWidget *item;
180         GtkAccelGroup *accel_group;
181         hard_coded_window_keys *window_keys;
182
183         item = gtk_menu_item_new_with_mnemonic(label);
184         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
185         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
186
187         if (accel_group && window_keys)
188                 {
189                 menu_item_add_accelerator(item, accel_group, window_keys);
190                 }
191         else if (accel_group)
192                 {
193                 menu_item_add_main_window_accelerator(item, accel_group);
194                 }
195
196         menu_item_finish(menu, item, func, data);
197
198         return item;
199 }
200
201 GtkWidget *menu_item_add_stock(GtkWidget *menu, const gchar *label, const gchar *stock_id,
202                                GCallback func, gpointer data)
203 {
204         GtkWidget *item;
205         GtkWidget *image;
206         GtkAccelGroup *accel_group;
207         hard_coded_window_keys *window_keys;
208
209         item = gtk_image_menu_item_new_with_mnemonic(label);
210         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
211         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
212
213         image = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
214         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
215
216         if (accel_group && window_keys)
217                 {
218                 menu_item_add_accelerator(item, accel_group, window_keys);
219                 }
220         else if (accel_group)
221                 {
222                 menu_item_add_main_window_accelerator(item, accel_group);
223                 }
224
225         gtk_widget_show(image);
226         menu_item_finish(menu, item, func, data);
227
228         return item;
229 }
230
231 GtkWidget *menu_item_add_icon(GtkWidget *menu, const gchar *label, const gchar *icon_name,
232                                GCallback func, gpointer data)
233 {
234         GtkWidget *item;
235         GtkWidget *image;
236         GtkAccelGroup *accel_group;
237         hard_coded_window_keys *window_keys;
238
239         item = gtk_image_menu_item_new_with_mnemonic(label);
240         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
241         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
242
243         image = gtk_image_new_from_icon_name(icon_name, GTK_ICON_SIZE_MENU);
244         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
245
246         if (accel_group && window_keys)
247                 {
248                 menu_item_add_accelerator(item, accel_group, window_keys);
249                 }
250         else if (accel_group)
251                 {
252                 menu_item_add_main_window_accelerator(item, accel_group);
253                 }
254
255         gtk_widget_show(image);
256         menu_item_finish(menu, item, func, data);
257
258         return item;
259 }
260
261 GtkWidget *menu_item_add_sensitive(GtkWidget *menu, const gchar *label, gboolean sensitive,
262                                    GCallback func, gpointer data)
263 {
264         GtkWidget *item;
265         GtkAccelGroup *accel_group;
266         hard_coded_window_keys *window_keys;
267
268         item = menu_item_add(menu, label, func, data);
269         gtk_widget_set_sensitive(item, sensitive);
270         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
271         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
272         if (accel_group && window_keys)
273                 {
274                 menu_item_add_accelerator(item, accel_group, window_keys);
275                 }
276         else if (accel_group)
277                 {
278                 menu_item_add_main_window_accelerator(item, accel_group);
279                 }
280
281         return item;
282 }
283
284 GtkWidget *menu_item_add_stock_sensitive(GtkWidget *menu, const gchar *label, const gchar *stock_id, gboolean sensitive,
285                                          GCallback func, gpointer data)
286 {
287         GtkWidget *item;
288         GtkAccelGroup *accel_group;
289         hard_coded_window_keys *window_keys;
290
291         item = menu_item_add_stock(menu, label, stock_id, func, data);
292         gtk_widget_set_sensitive(item, sensitive);
293         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
294         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
295         if (accel_group && window_keys)
296                 {
297                 menu_item_add_accelerator(item, accel_group, window_keys);
298                 }
299         else if (accel_group)
300                 {
301                 menu_item_add_main_window_accelerator(item, accel_group);
302                 }
303
304         return item;
305 }
306
307 GtkWidget *menu_item_add_icon_sensitive(GtkWidget *menu, const gchar *label, const gchar *icon_name, gboolean sensitive,
308                                          GCallback func, gpointer data)
309 {
310         GtkWidget *item;
311         GtkAccelGroup *accel_group;
312         hard_coded_window_keys *window_keys;
313
314         item = menu_item_add_icon(menu, label, icon_name, func, data);
315         gtk_widget_set_sensitive(item, sensitive);
316         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
317         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
318         if (accel_group && window_keys)
319                 {
320                 menu_item_add_accelerator(item, accel_group, window_keys);
321                 }
322         else if (accel_group)
323                 {
324                 menu_item_add_main_window_accelerator(item, accel_group);
325                 }
326
327         return item;
328 }
329
330 GtkWidget *menu_item_add_check(GtkWidget *menu, const gchar *label, gboolean active,
331                                GCallback func, gpointer data)
332 {
333         GtkWidget *item;
334         GtkAccelGroup *accel_group;
335         hard_coded_window_keys *window_keys;
336
337         item = gtk_check_menu_item_new_with_mnemonic(label);
338         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
339         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
340
341         if (accel_group && window_keys)
342                 {
343                 menu_item_add_accelerator(item, accel_group, window_keys);
344                 }
345         else if (accel_group)
346                 {
347                 menu_item_add_main_window_accelerator(item, accel_group);
348                 }
349
350         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active);
351         menu_item_finish(menu, item, func, data);
352
353         return item;
354 }
355
356 GtkWidget *menu_item_add_radio(GtkWidget *menu, const gchar *label, gpointer item_data, gboolean active,
357                                GCallback func, gpointer data)
358 {
359         GtkAccelGroup *accel_group;
360         hard_coded_window_keys *window_keys;
361
362         GtkWidget *item = menu_item_add_check(menu, label, active, func, data);
363         g_object_set_data(G_OBJECT(item), "menu_item_radio_data", item_data);
364         g_object_set(G_OBJECT(item), "draw-as-radio", TRUE, NULL);
365
366         window_keys = static_cast<hard_coded_window_keys *>(g_object_get_data(G_OBJECT(menu), "window_keys"));
367         accel_group = static_cast<GtkAccelGroup *>(g_object_get_data(G_OBJECT(menu), "accel_group"));
368         if (accel_group && window_keys)
369                 {
370                 menu_item_add_accelerator(item, accel_group, window_keys);
371                 }
372         else if (accel_group)
373                 {
374                 menu_item_add_main_window_accelerator(item, accel_group);
375                 }
376
377         return item;
378 }
379
380 void menu_item_add_divider(GtkWidget *menu)
381 {
382         GtkWidget *item = gtk_separator_menu_item_new();
383         gtk_widget_set_sensitive(item, FALSE);
384         gtk_menu_shell_append(GTK_MENU_SHELL(menu),item);
385         gtk_widget_show(item);
386 }
387
388 GtkWidget *menu_item_add_simple(GtkWidget *menu, const gchar *label,
389                                 GCallback func, gpointer data)
390 {
391         GtkWidget *item = gtk_menu_item_new_with_label(label);
392         menu_item_finish(menu, item, func, data);
393
394         return item;
395 }
396
397 /*
398  *-----------------------------------------------------------------------------
399  * popup menus
400  *-----------------------------------------------------------------------------
401  */
402
403 static void popup_menu_short_lived_cb(GtkWidget *, gpointer data)
404 {
405         /* destroy the menu */
406         g_object_unref(G_OBJECT(data));
407 }
408
409 GtkWidget *popup_menu_short_lived()
410 {
411         GtkWidget *menu;
412
413         menu = gtk_menu_new();
414
415         /* take ownership of menu */
416 #ifdef GTK_OBJECT_FLOATING
417         /* GTK+ < 2.10 */
418         g_object_ref(G_OBJECT(menu));
419         gtk_object_sink(GTK_OBJECT(menu));
420 #else
421         /* GTK+ >= 2.10 */
422         g_object_ref_sink(G_OBJECT(menu));
423 #endif
424
425         g_signal_connect(G_OBJECT(menu), "selection_done",
426                          G_CALLBACK(popup_menu_short_lived_cb), menu);
427         return menu;
428 }
429
430 gboolean popup_menu_position_clamp(GtkMenu *menu, gint *x, gint *y, gint height)
431 {
432         gboolean adjusted = FALSE;
433         gint w;
434         gint h;
435         gint xw;
436         gint xh;
437         GtkRequisition requisition;
438
439         gtk_widget_get_requisition(GTK_WIDGET(menu), &requisition);
440         w = requisition.width;
441         h = requisition.height;
442         xw = gdk_screen_width();
443         xh = gdk_screen_height();
444
445         if (*x + w > xw)
446                 {
447                 *x = xw - w;
448                 adjusted = TRUE;
449                 }
450         if (*y + h > xh)
451                 {
452                 if (height)
453                         {
454                         *y = MAX(0, *y - h - height);
455                         }
456                 else
457                         {
458                         *y = xh - h;
459                         }
460                 adjusted = TRUE;
461                 };
462
463         if (*x < 0)
464                 {
465                 *x = 0;
466                 adjusted = TRUE;
467                 }
468         if (*y < 0)
469                 {
470                 *y = 0;
471                 adjusted = TRUE;
472                 }
473
474         return adjusted;
475 }
476 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */