Remove the trace of a TODO file.
[gliv.git] / src / images_menus.c
blobdcf00d0213066f6cff7e8e992cfbba19159fa6f8
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 "str_utils.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);
92 * Underscores in filenames are replaced with mnemonics, so we duplicate them.
93 * The caller has to determine whether the returned string has to be freed.
95 static gchar *duplicate_underscores(const gchar * orig)
97 gint nb_underscores;
98 const gchar *ptr_orig;
99 gchar *new, *ptr_new;
101 /* How many underscores? */
102 nb_underscores = count_underscores(orig);
103 if (nb_underscores == 0)
104 return (gchar *) orig;
106 ptr_new = new = g_new(gchar, strlen(orig) + nb_underscores + 1);
108 for (ptr_orig = orig; *ptr_orig != '\0'; ptr_orig++) {
109 *ptr_new = *ptr_orig;
110 if (*ptr_orig == '_') {
111 /* Duplicate this one. */
112 ptr_new++;
113 *ptr_new = '_';
115 ptr_new++;
118 *ptr_new = '\0';
120 return new;
123 /* We don't want to perform the signal lookup for each file. */
124 static void connect_activate(GtkMenuItem * instance, const gchar * filename)
126 static guint signal_id = 0;
127 GClosure *closure;
129 if (signal_id == 0)
130 /* First time. */
131 signal_id = g_signal_lookup("activate", G_TYPE_FROM_INSTANCE(instance));
133 closure = g_cclosure_new_swap(G_CALLBACK(menu_load),
134 (gpointer) filename, NULL);
136 g_signal_connect_closure_by_id(instance, signal_id, 0, closure, FALSE);
139 typedef enum {
140 MENU_FILE, /* From both menus. */
141 MENU_DIR, /* From the Images menu. */
142 MENU_SUBMENU /* From the Directories menu. */
143 } menu_type;
145 /* Add also a mnemonic and a thumbnail. */
146 static GtkMenuItem *add_menu_item(GtkMenuShell * menu,
147 tree_item * item, menu_type type)
149 const gchar *name;
150 GtkMenuItem *menu_item;
151 GtkImage *thumbnail = NULL;
153 if (options->thumbnails && type == MENU_FILE && fill_thumbnail(item))
154 thumbnail = GTK_IMAGE(gtk_image_new_from_pixbuf(item->thumb));
156 if (type == MENU_DIR)
157 name = item->path;
158 else
159 name = item->name;
161 if (options->mnemonics && type != MENU_DIR) {
162 gchar *dup_underscores = duplicate_underscores(name);
164 name = add_mnemonic(dup_underscores);
165 if (dup_underscores != name)
166 g_free(dup_underscores);
168 if (thumbnail)
169 menu_item =
170 GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(name));
171 else
172 menu_item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name));
174 } else {
175 if (thumbnail)
176 menu_item = GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(name));
177 else
178 menu_item = GTK_MENU_ITEM(gtk_menu_item_new_with_label(name));
181 if (thumbnail)
182 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
183 GTK_WIDGET(thumbnail));
185 gtk_menu_shell_insert(menu, GTK_WIDGET(menu_item), 1);
187 switch (type) {
188 case MENU_FILE:
189 connect_activate(menu_item, item->path);
190 break;
192 case MENU_DIR:
193 gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
194 break;
196 case MENU_SUBMENU:
197 break;
200 process_events();
201 return menu_item;
204 /*** Directories menu. ***/
206 static void add_file_from_tree(GtkMenuShell * menu, tree_item * item)
208 static gchar *directories = NULL;
209 static gint number = 0, percent = 0;
211 if (canceled_using_tree())
212 return;
214 if (menu == NULL) {
215 /* First time for this menu. */
217 if (directories == NULL)
218 /* First time. */
219 directories = _("Directories");
221 number = 0;
222 percent = 0;
223 return;
226 add_menu_item(menu, item, MENU_FILE);
228 set_progress(directories, &percent, number);
229 number++;
232 static GtkMenuShell *add_sub_menu(GtkMenuShell * parent, tree_item * item)
234 GtkMenuShell *menu;
235 GtkMenuItem *menu_item;
237 menu_item = add_menu_item(parent, item, MENU_SUBMENU);
239 menu = GTK_MENU_SHELL(gtk_menu_new());
240 gtk_menu_item_set_submenu(menu_item, GTK_WIDGET(menu));
242 gtk_menu_shell_prepend(menu, gtk_tearoff_menu_item_new());
244 return menu;
247 static void make_menu_from_tree_rec(GNode * tree, GtkMenuShell * parent)
249 tree_item *item;
250 GtkMenuShell *menu;
252 if (canceled_using_tree())
253 return;
255 if (G_NODE_IS_LEAF(tree))
256 add_file_from_tree(parent, tree->data);
257 else {
258 item = tree->data;
259 menu = add_sub_menu(parent, item);
260 g_node_children_foreach(tree, G_TRAVERSE_ALL,
261 (GNodeForeachFunc) make_menu_from_tree_rec,
262 menu);
266 /*** Images menu. ***/
268 static gboolean add_file_item(GNode * tree, GtkMenuShell * parent_menu)
270 static gchar *images = NULL;
271 static gint number = 0, percent = 0;
272 GNode *sibling;
274 if (canceled_using_tree())
275 return TRUE;
277 if (parent_menu == NULL) {
278 /* First time for this menu. */
280 if (images == NULL)
281 /* First time. */
282 images = _("Images");
284 number = 0;
285 percent = 0;
286 return FALSE;
289 add_menu_item(parent_menu, tree->data, MENU_FILE);
291 /* Check if it is the last image in its directory. */
292 sibling = g_node_next_sibling(tree);
293 if (!(G_NODE_IS_ROOT(tree) || (sibling && G_NODE_IS_LEAF(sibling))))
294 add_menu_item(parent_menu, tree->parent->data, MENU_DIR);
296 set_progress(images, &percent, number);
297 number++;
299 return FALSE;
302 /*** Menus builders. ***/
304 static GtkWidget *common_rebuild(GtkMenuItem * root, gboolean start)
306 static GtkMenuShell *menu;
308 if (root != NULL)
309 gtk_menu_item_deselect(root);
311 if (start == FALSE) {
312 const gchar *label;
313 GtkMenuItem *item;
315 label = add_mnemonic(_("Rebuild this menu"));
316 item = GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(label));
318 gtk_menu_shell_insert(menu, GTK_WIDGET(item), 1);
320 return GTK_WIDGET(item);
323 menu = GTK_MENU_SHELL(gtk_menu_new());
324 gtk_menu_shell_prepend(menu, gtk_tearoff_menu_item_new());
325 gtk_menu_item_set_submenu(root, GTK_WIDGET(menu));
327 return GTK_WIDGET(menu);
330 static GNode *get_tree(void)
332 if (is_loading())
334 * There is no problem in rebuilding the images menus during
335 * a loading, it would just make the loading too long.
337 return NULL;
339 return make_tree();
342 #define REBUILD_START(name) \
343 do { \
344 if (root != NULL) { \
345 menu_item = root; \
346 return TRUE; \
349 if (has_timestamp(timestamp) && last_tree_ok(timestamp)) \
350 /* The menu is already up to date. */ \
351 return TRUE; \
353 tree = get_tree(); \
354 if (tree == NULL) \
355 return FALSE; \
357 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), TRUE); \
358 menu = GTK_MENU_SHELL(common_rebuild(menu_item, TRUE)); \
359 set_menu_indicator(name, 0); \
360 } while (0)
362 #define REBUILD_END(function) \
363 do { \
364 GtkMenuItem *launcher; \
366 launcher = GTK_MENU_ITEM(common_rebuild(NULL, FALSE)); \
367 g_signal_connect_swapped(launcher, "activate", G_CALLBACK(function), \
368 NULL); \
370 gtk_widget_show_all(GTK_WIDGET(menu_item)); \
371 set_menu_indicator(NULL, 0); \
372 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), FALSE); \
373 if (canceled_using_tree()) \
374 reset_timestamp(&timestamp); \
375 else \
376 touch(&timestamp); \
377 end_using_tree(); \
378 return timestamp != 0; \
379 } while (0)
381 gboolean rebuild_directories(GtkMenuItem * root)
383 static GtkMenuItem *menu_item;
384 static DECLARE_TIMESTAMP(timestamp);
385 GtkMenuShell *menu;
386 GNode *tree;
388 REBUILD_START(_("Directories"));
390 /* Build the menu. */
391 add_file_from_tree(NULL, NULL);
392 make_menu_from_tree_rec(tree, menu);
394 REBUILD_END(rebuild_directories);
397 gboolean rebuild_images(GtkMenuItem * root)
399 static GtkMenuItem *menu_item;
400 static DECLARE_TIMESTAMP(timestamp);
401 GtkMenuShell *menu;
402 GNode *tree;
404 REBUILD_START(_("Images"));
406 /* Build the menu. */
407 add_file_item(NULL, NULL);
408 g_node_traverse(tree, G_PRE_ORDER, G_TRAVERSE_LEAFS, -1,
409 (GNodeTraverseFunc) add_file_item, menu);
411 REBUILD_END(rebuild_images);
414 /* Rebuild both menus. */
415 gboolean rebuild_images_menus(void)
417 return rebuild_directories(NULL) && rebuild_images(NULL);
420 void set_stop_rebuilding_menu(GtkMenuItem * item)
422 cancel_menu_item = item;
423 gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);