Remove misleading comment in ~/.glivrc.
[gliv.git] / src / images_menus.c
blobaf9a9a20cc5a434eb9bc2c5ad2e95cc0e6ef4dbb
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 * See the COPYING file for license information.
18 * Guillaume Chazarain <guichaz@yahoo.fr>
21 /************************************
22 * The Images and Directories menus *
23 ************************************/
25 #include <string.h> /* strlen() */
27 #include "gliv.h"
28 #include "images_menus.h"
29 #include "options.h"
30 #include "menus.h"
31 #include "messages.h"
32 #include "next_image.h"
33 #include "mnemonics.h"
34 #include "files_list.h"
35 #include "loading.h"
36 #include "thumbnails.h"
37 #include "callbacks.h"
38 #include "tree.h"
39 #include "timestamp.h"
41 extern options_struct *options;
43 /* The progress indicator. */
44 static GtkMenuItem *rebuilding_entry;
46 /* The menu entry to cancel the rebuilding. */
47 static GtkMenuItem *cancel_menu_item;
49 /*** Functions for both menus. ***/
51 /* Called at menu creation time. */
52 void set_rebuilding_entry(GtkMenuItem * item)
54 rebuilding_entry = item;
57 /* Refresh the percent indicator. */
58 static void set_menu_indicator(gchar * name, gint percent)
60 static gchar *rebuilding = NULL;
62 if (name == NULL)
63 set_menu_label(rebuilding_entry, "", FALSE);
64 else {
65 gchar *label;
67 if (rebuilding == NULL)
68 /* First time. */
69 rebuilding = _("Rebuilding:");
71 label = g_strdup_printf("%s %s (%d%%)", rebuilding, name, percent);
72 set_menu_label(rebuilding_entry, label, FALSE);
74 g_free(label);
78 /* Update the percent indicator. */
79 static void set_progress(gchar * menu, gint * percent, gint number)
81 gint length;
83 length = get_list_length();
85 if ((*percent + 1) * length <= 100 * number) {
86 *percent = (100 * number) / length;
87 set_menu_indicator(menu, *percent);
91 /* We don't want to perform the signal lookup for each file. */
92 static void connect_activate(GtkMenuItem * instance, const gchar * filename)
94 static guint signal_id = 0;
95 GClosure *closure;
97 if (signal_id == 0)
98 /* First time. */
99 signal_id = g_signal_lookup("activate", G_TYPE_FROM_INSTANCE(instance));
101 closure = g_cclosure_new_swap(G_CALLBACK(menu_load),
102 (gpointer) filename, NULL);
104 g_signal_connect_closure_by_id(instance, signal_id, 0, closure, FALSE);
107 typedef enum {
108 MENU_FILE, /* From both menus. */
109 MENU_DIR, /* From the Images menu. */
110 MENU_SUBMENU /* From the Directories menu. */
111 } menu_type;
113 /* Add also a mnemonic and a thumbnail. */
114 static GtkMenuItem *new_menu_item(tree_item * item, menu_type type)
116 const gchar *name;
117 GtkMenuItem *menu_item;
118 GtkImage *thumbnail = NULL;
120 if (options->thumbnails && type == MENU_FILE && fill_thumbnail(item))
121 thumbnail = GTK_IMAGE(gtk_image_new_from_pixbuf(item->thumb));
122 else
123 /* Simulate the do_threaded() effect of fill_thumbnail(). */
124 process_events();
126 if (type == MENU_DIR)
127 name = item->path;
128 else
129 name = item->name;
131 if (options->mnemonics && type != MENU_DIR) {
132 name = add_mnemonic(name);
134 if (thumbnail)
135 menu_item =
136 GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(name));
137 else
138 menu_item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name));
140 } else {
141 if (thumbnail)
142 menu_item = GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(name));
143 else
144 menu_item = GTK_MENU_ITEM(gtk_menu_item_new_with_label(name));
147 if (thumbnail)
148 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
149 GTK_WIDGET(thumbnail));
151 switch (type) {
152 case MENU_FILE:
153 connect_activate(menu_item, item->path);
154 break;
156 case MENU_DIR:
157 gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
158 break;
160 case MENU_SUBMENU:
161 break;
164 return menu_item;
168 * We first insert in the head of a list, and then we insert every elements
169 * from that list in the menu head. This way we build the menu in O(n) and
170 * the mnemonics are created in normal order.
172 static void insert_list_into_menu(GtkMenuShell * menu, GSList * list, gint pos)
174 GSList *ptr;
176 for (ptr = list; ptr != NULL; ptr = ptr->next)
177 gtk_menu_shell_insert(menu, GTK_WIDGET(ptr->data), pos);
179 g_slist_free(list);
182 /*** Directories menu. ***/
184 static void add_file_from_tree(GSList ** list, tree_item * item)
186 static gchar *directories = NULL;
187 static gint number = 0, percent = 0;
189 if (canceled_using_tree())
190 return;
192 if (list == NULL) {
193 /* First time for this menu. */
195 if (directories == NULL)
196 /* First time. */
197 directories = _("Directories");
199 number = 0;
200 percent = 0;
201 return;
204 *list = g_slist_prepend(*list, new_menu_item(item, MENU_FILE));
206 set_progress(directories, &percent, number);
207 number++;
210 static GtkMenuItem *add_sub_menu(GSList ** list, tree_item * item)
212 GtkMenuShell *menu;
213 GtkMenuItem *menu_item;
215 menu_item = new_menu_item(item, MENU_SUBMENU);
217 menu = GTK_MENU_SHELL(gtk_menu_new());
218 gtk_menu_item_set_submenu(menu_item, GTK_WIDGET(menu));
220 gtk_menu_shell_prepend(menu, gtk_tearoff_menu_item_new());
221 *list = g_slist_prepend(*list, menu_item);
223 return menu_item;
226 static void make_menu_from_tree_rec(GNode * tree, GSList ** list)
228 GtkMenuItem *menu_item;
229 tree_item *item;
230 GSList *new_list = NULL;
232 if (canceled_using_tree())
233 return;
235 item = tree->data;
237 if (G_NODE_IS_LEAF(tree)) {
238 add_file_from_tree(list, item);
239 return;
242 menu_item = add_sub_menu(list, item);
244 push_mnemonics();
246 g_node_children_foreach(tree, G_TRAVERSE_ALL,
247 (GNodeForeachFunc) make_menu_from_tree_rec,
248 &new_list);
250 pop_mnemonics();
252 insert_list_into_menu(GTK_MENU_SHELL(gtk_menu_item_get_submenu(menu_item)),
253 new_list, 1);
256 /*** Images menu. ***/
258 static gboolean add_file_item(GNode * tree, GSList ** list)
260 static gchar *images = NULL;
261 static gint number = 0, percent = 0;
262 GNode *sibling;
264 if (canceled_using_tree())
265 return TRUE;
267 if (tree == NULL) {
268 /* First time for this menu. */
270 if (images == NULL)
271 /* First time. */
272 images = _("Images");
274 number = 0;
275 percent = 0;
276 return FALSE;
280 /* Check if it is the first image in its directory. */
281 sibling = g_node_prev_sibling(tree);
282 if (!(G_NODE_IS_ROOT(tree) || (sibling && G_NODE_IS_LEAF(sibling))))
283 *list = g_slist_prepend(*list,
284 new_menu_item(tree->parent->data, MENU_DIR));
286 *list = g_slist_prepend(*list, new_menu_item(tree->data, MENU_FILE));
288 set_progress(images, &percent, number);
289 number++;
291 return FALSE;
294 /*** Menus builders. ***/
296 static GtkWidget *common_rebuild(GtkMenuItem * root, gboolean start)
298 static GtkMenuShell *menu;
300 if (root != NULL)
301 gtk_menu_item_deselect(root);
303 if (start == FALSE) {
304 const gchar *label;
305 GtkMenuItem *item;
307 label = add_mnemonic(_("Rebuild this menu"));
308 item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(label));
310 gtk_menu_shell_insert(menu, GTK_WIDGET(item), 1);
312 return GTK_WIDGET(item);
315 menu = GTK_MENU_SHELL(gtk_menu_new());
316 gtk_menu_shell_prepend(menu, gtk_tearoff_menu_item_new());
317 gtk_menu_item_set_submenu(root, GTK_WIDGET(menu));
319 return GTK_WIDGET(menu);
322 static GNode *get_tree(void)
324 if (is_loading())
326 * There is no problem in rebuilding the images menus during
327 * a loading, it would just make the loading too long.
329 return NULL;
331 return make_tree();
334 #define REBUILD_START(name) \
335 do { \
336 if (root != NULL) { \
337 menu_item = root; \
338 return TRUE; \
341 if (has_timestamp(timestamp) && last_tree_ok(timestamp)) \
342 /* The menu is already up to date. */ \
343 return TRUE; \
345 tree = get_tree(); \
346 if (tree == NULL) \
347 return FALSE; \
349 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), TRUE); \
350 reset_mnemonics(); \
351 menu = GTK_MENU_SHELL(common_rebuild(menu_item, TRUE)); \
352 set_menu_indicator(name, 0); \
353 } while (0)
355 #define REBUILD_END(function) \
356 do { \
357 GtkMenuItem *launcher; \
359 launcher = GTK_MENU_ITEM(common_rebuild(NULL, FALSE)); \
360 g_signal_connect_swapped(launcher, "activate", G_CALLBACK(function), \
361 NULL); \
363 insert_list_into_menu(menu, list, 2); \
364 gtk_widget_show_all(GTK_WIDGET(menu_item)); \
365 set_menu_indicator(NULL, 0); \
366 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), FALSE); \
367 if (canceled_using_tree()) \
368 reset_timestamp(&timestamp); \
369 else \
370 touch(&timestamp); \
371 end_using_tree(); \
372 reset_mnemonics(); \
373 return timestamp != 0; \
374 } while (0)
376 gboolean rebuild_directories(GtkMenuItem * root)
378 static GtkMenuItem *menu_item;
379 static DECLARE_TIMESTAMP(timestamp);
380 GtkMenuShell *menu;
381 GNode *tree, *child;
382 gchar *prefix, *old_name;
383 tree_item *item;
384 GSList *list = NULL;
386 REBUILD_START(_("Directories"));
388 /* Build the menu. */
389 add_file_from_tree(NULL, NULL);
391 item = tree->data;
392 prefix = item->path;
394 if (prefix[0] == '\0' || prefix[1] == '\0') {
395 for (child = g_node_first_child(tree); child; child = child->next) {
396 item = child->data;
397 old_name = item->name;
398 item->name = g_build_filename(prefix, item->name, NULL);
400 make_menu_from_tree_rec(child, &list);
402 g_free(item->name);
403 item->name = old_name;
405 } else
406 make_menu_from_tree_rec(tree, &list);
408 REBUILD_END(rebuild_directories);
411 gboolean rebuild_images(GtkMenuItem * root)
413 static GtkMenuItem *menu_item;
414 static DECLARE_TIMESTAMP(timestamp);
415 GSList *list = NULL;
416 GtkMenuShell *menu;
417 GNode *tree;
419 REBUILD_START(_("Images"));
421 /* Build the menu. */
422 add_file_item(NULL, NULL);
423 g_node_traverse(tree, G_PRE_ORDER, G_TRAVERSE_LEAFS, -1,
424 (GNodeTraverseFunc) add_file_item, &list);
426 REBUILD_END(rebuild_images);
429 /* Rebuild both menus. */
430 gboolean rebuild_images_menus(void)
432 return rebuild_directories(NULL) && rebuild_images(NULL);
435 void set_stop_rebuilding_menu(GtkMenuItem * item)
437 cancel_menu_item = item;
438 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);