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() */
28 #include "images_menus.h"
32 #include "next_image.h"
33 #include "mnemonics.h"
35 #include "thumbnails.h"
36 #include "callbacks.h"
38 #include "timestamp.h"
39 #include "dirty_gtk.h"
40 #include "gliv-image.h"
41 #include "tree_browser.h"
43 extern options_struct
*options
;
44 extern GlivImage
*current_image
;
46 /* The progress indicator. */
47 static GtkMenuItem
*rebuilding_entry
;
49 /* The menu entry to cancel the rebuilding. */
50 static GtkMenuItem
*cancel_menu_item
;
53 * Association between a directory and its GtkMenu.
54 * Used by Directories->"Current image directory...".
56 static GHashTable
*dir_menu_hash
= NULL
;
58 /* Menus timestamps */
59 static DECLARE_TIMESTAMP(directories_timestamp
);
60 static DECLARE_TIMESTAMP(images_timestamp
);
62 /*** Functions for both menus. ***/
64 /* Called at menu creation time. */
65 void set_rebuilding_entry(GtkMenuItem
* item
)
67 rebuilding_entry
= item
;
70 /* Refresh the percent indicator. */
71 static void set_menu_indicator(const gchar
* name
, gint percent
)
73 static gchar
*rebuilding
= NULL
;
76 set_menu_label(rebuilding_entry
, "", FALSE
);
80 if (rebuilding
== NULL
)
82 rebuilding
= _("Rebuilding:");
84 label
= g_strdup_printf("%s %s (%d%%)", rebuilding
, name
, percent
);
85 set_menu_label(rebuilding_entry
, label
, FALSE
);
91 /* Update the percent indicator. */
92 void set_progress(const gchar
* menu
, gint
* percent
, gint number
)
94 static gint total_length
;
96 /* Yes, that's ugly */
98 /* First time for the menu */
99 total_length
= tree_count_files();
104 /* Last time for the menu */
105 set_menu_indicator(NULL
, 0);
109 if ((*percent
+ 1) * total_length
<= 100 * number
) {
110 *percent
= (100 * number
) / total_length
;
111 set_menu_indicator(menu
, *percent
);
115 /* We don't want to perform the signal lookup for each file. */
116 static void connect_activate(GtkMenuItem
* instance
, const gchar
* filename
)
118 static guint signal_id
= 0;
123 signal_id
= g_signal_lookup("activate", G_TYPE_FROM_INSTANCE(instance
));
125 closure
= g_cclosure_new_swap(G_CALLBACK(menu_load
),
126 (gpointer
) filename
, NULL
);
128 g_signal_connect_closure_by_id(instance
, signal_id
, 0, closure
, FALSE
);
132 MENU_FILE
, /* From both menus. */
133 MENU_DIR
, /* From the Images menu. */
134 MENU_SUBMENU
/* From the Directories menu. */
137 /* Add also a mnemonic and a thumbnail. */
138 static GtkMenuItem
*add_menu_item(DirtyGtkMenu
* menu
,
139 tree_item
* item
, menu_type type
)
142 GtkMenuItem
*menu_item
;
143 GtkImage
*thumbnail
= NULL
;
145 if (options
->thumbnails
&& type
== MENU_FILE
&& fill_thumbnail(item
))
146 thumbnail
= GTK_IMAGE(gtk_image_new_from_pixbuf(item
->thumb
));
148 /* Simulate the do_threaded() effect of fill_thumbnail(). */
151 if (type
== MENU_DIR
)
156 if (options
->mnemonics
&& type
!= MENU_DIR
) {
157 name
= add_mnemonic(name
);
161 GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(name
));
163 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name
));
167 menu_item
= GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(name
));
169 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_label(name
));
173 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item
),
174 GTK_WIDGET(thumbnail
));
176 dirty_gtk_menu_append(menu
, GTK_WIDGET(menu_item
));
180 connect_activate(menu_item
, item
->path
);
184 gtk_widget_set_sensitive(GTK_WIDGET(menu_item
), FALSE
);
194 /*** Directories menu. ***/
196 static void add_file_from_tree(DirtyGtkMenu
* menu
, tree_item
* item
)
198 static gchar
*directories
= NULL
;
199 static gint number
= 0, percent
= 0;
201 if (canceled_using_tree())
205 /* First time for this menu. */
207 if (directories
== NULL
)
209 directories
= _("Directories");
216 add_menu_item(menu
, item
, MENU_FILE
);
218 set_progress(directories
, &percent
, number
);
222 static DirtyGtkMenu
*add_sub_menu(DirtyGtkMenu
* parent
, tree_item
* item
)
225 GtkMenuItem
*menu_item
;
227 menu_item
= add_menu_item(parent
, item
, MENU_SUBMENU
);
229 menu
= dirty_gtk_menu_new();
230 gtk_menu_item_set_submenu(menu_item
, GTK_WIDGET(menu
->gtk_menu
));
232 g_hash_table_insert(dir_menu_hash
, item
->path
,
233 dirty_gtk_menu_get_tearoff(menu
));
238 static void make_menu_from_tree_rec(GNode
* tree
, DirtyGtkMenu
* parent
)
243 if (canceled_using_tree())
246 if (G_NODE_IS_LEAF(tree
)) {
247 add_file_from_tree(parent
, tree
->data
);
252 menu
= add_sub_menu(parent
, item
);
256 g_node_children_foreach(tree
, G_TRAVERSE_ALL
,
257 (GNodeForeachFunc
) make_menu_from_tree_rec
, menu
);
259 dirty_gtk_menu_release(menu
);
263 static gboolean
open_current_dir_menu(void)
265 if (dir_menu_hash
&& current_image
&& current_image
->node
) {
266 gchar
*path
= g_strdup(current_image
->node
->data
);
267 GtkTearoffMenuItem
*tearoff
;
270 gchar
*new_path
= g_path_get_dirname(path
);
275 tearoff
= g_hash_table_lookup(dir_menu_hash
, path
);
276 } while (!tearoff
&& !g_str_equal(path
, "/") &&
277 !g_str_equal(path
, "."));
282 GtkWidget
*parent_menu
= gtk_widget_get_parent(GTK_WIDGET(tearoff
));
283 gtk_widget_realize(parent_menu
);
284 gtk_menu_item_activate(GTK_MENU_ITEM(tearoff
));
291 static void add_current_dir_entry(DirtyGtkMenu
* menu
)
296 label
= add_mnemonic(_("Current image directory..."));
298 item
= GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(label
));
299 g_signal_connect_swapped(item
, "activate",
300 G_CALLBACK(open_current_dir_menu
), NULL
);
302 gtk_widget_show(GTK_WIDGET(item
));
303 dirty_gtk_menu_append(menu
, GTK_WIDGET(item
));
306 g_hash_table_destroy(dir_menu_hash
);
308 dir_menu_hash
= g_hash_table_new_full(g_str_hash
, g_str_equal
, NULL
, NULL
);
310 g_hash_table_insert(dir_menu_hash
, ".", dirty_gtk_menu_get_tearoff(menu
));
311 g_hash_table_insert(dir_menu_hash
, "/", dirty_gtk_menu_get_tearoff(menu
));
314 /*** Images menu. ***/
316 static gboolean
add_file_item(GNode
* tree
, DirtyGtkMenu
* parent_menu
)
318 static gchar
*images
= NULL
;
319 static gint number
= 0, percent
= 0;
322 if (canceled_using_tree())
325 if (parent_menu
== NULL
) {
326 /* First time for this menu. */
330 images
= _("Images");
337 /* Check if it is the first image in its directory. */
338 sibling
= g_node_prev_sibling(tree
);
339 if (sibling
== NULL
&& tree
->parent
!= NULL
)
340 add_menu_item(parent_menu
, tree
->parent
->data
, MENU_DIR
);
342 add_menu_item(parent_menu
, tree
->data
, MENU_FILE
);
344 set_progress(images
, &percent
, number
);
350 /*** Menus builders. ***/
352 static DirtyGtkMenu
*begin_rebuild(GtkMenuItem
* root
, GCallback func
)
354 static DirtyGtkMenu
*menu
;
355 GtkWidget
*rebuilder
, *browser
;
358 gtk_menu_item_deselect(root
);
360 menu
= dirty_gtk_menu_new();
363 gtk_menu_item_new_with_mnemonic(add_mnemonic(_("Rebuild this menu")));
365 g_signal_connect_swapped(rebuilder
, "activate", func
, NULL
);
366 dirty_gtk_menu_append(menu
, rebuilder
);
369 gtk_menu_item_new_with_mnemonic(add_mnemonic
370 (_("Open thumbnails browser...")));
372 g_signal_connect(browser
, "activate", show_tree_browser
, NULL
);
373 dirty_gtk_menu_append(menu
, browser
);
375 gtk_menu_item_set_submenu(root
, GTK_WIDGET(menu
->gtk_menu
));
380 GNode
*get_tree(void)
382 if (currently_loading())
384 * There is no problem in rebuilding the images menus during
385 * a loading, it would just make the loading too long.
392 #define REBUILD_START(name, func, timestamp) \
394 if (root != NULL) { \
399 if (more_recent_than_tree(timestamp)) \
400 /* The menu is already up to date. */ \
404 if (tree == NULL) { \
409 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), TRUE); \
411 menu = begin_rebuild(menu_item, G_CALLBACK(func)); \
412 set_menu_indicator(name, 0); \
413 set_progress(NULL, NULL, -1); \
416 static gboolean
rebuild_end(GtkMenuItem
* root
, timestamp_t
* ts
,
417 GtkMenuItem
* menu_item
, DirtyGtkMenu
* menu
)
420 gtk_menu_item_deselect(root
);
422 dirty_gtk_menu_append(menu
, gtk_tearoff_menu_item_new());
424 set_progress(NULL
, NULL
, 0);
425 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item
), FALSE
);
427 if (canceled_using_tree())
434 gtk_widget_show_all(GTK_WIDGET(menu_item
));
435 dirty_gtk_menu_release(menu
);
439 gboolean
rebuild_directories(GtkMenuItem
* root
)
441 static GtkMenuItem
*menu_item
;
444 gchar
*prefix
, *old_name
;
447 REBUILD_START(_("Directories"), rebuild_directories
, directories_timestamp
);
448 add_current_dir_entry(menu
);
450 /* Build the menu. */
451 add_file_from_tree(NULL
, NULL
);
456 if (prefix
[0] == '\0' || prefix
[1] == '\0') {
457 for (child
= g_node_first_child(tree
); child
; child
= child
->next
) {
459 old_name
= item
->name
;
460 item
->name
= g_build_filename(prefix
, item
->name
, NULL
);
462 make_menu_from_tree_rec(child
, menu
);
465 item
->name
= old_name
;
468 make_menu_from_tree_rec(tree
, menu
);
470 return rebuild_end(root
, &directories_timestamp
, menu_item
, menu
);
473 gboolean
rebuild_images(GtkMenuItem
* root
)
475 static GtkMenuItem
*menu_item
;
479 REBUILD_START(_("Images"), rebuild_images
, images_timestamp
);
481 /* Build the menu. */
482 add_file_item(NULL
, NULL
);
483 g_node_traverse(tree
, G_PRE_ORDER
, G_TRAVERSE_LEAFS
, -1,
484 (GNodeTraverseFunc
) add_file_item
, menu
);
486 return rebuild_end(root
, &images_timestamp
, menu_item
, menu
);
489 /* Rebuild both menus. */
490 gboolean
rebuild_images_menus(void)
492 return rebuild_directories(NULL
) && rebuild_images(NULL
);
495 void set_stop_rebuilding_menu(GtkMenuItem
* item
)
497 cancel_menu_item
= item
;
498 gtk_widget_set_sensitive(GTK_WIDGET(item
), FALSE
);
501 void obsolete_menus(void)
503 reset_timestamp(&directories_timestamp
);
504 reset_timestamp(&images_timestamp
);
507 void cond_rebuild_menus(void)
509 if (directories_timestamp
)
510 rebuild_directories(NULL
);
512 if (images_timestamp
)
513 rebuild_images(NULL
);
515 cond_rebuild_tree_browser();