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"
34 #include "files_list.h"
36 #include "thumbnails.h"
37 #include "callbacks.h"
39 #include "timestamp.h"
40 #include "dirty_gtk.h"
41 #include "gliv-image.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 * The number of files in the current tree.
54 * Used also to know if we are rebuilding a menu.
56 static gint tree_count
;
59 * Association between a directory and its GtkMenu.
60 * Used by Directories->"Current image directory...".
62 static GHashTable
*dir_menu_hash
= NULL
;
64 /*** Functions for both menus. ***/
66 /* Called at menu creation time. */
67 void set_rebuilding_entry(GtkMenuItem
* item
)
69 rebuilding_entry
= item
;
72 /* Refresh the percent indicator. */
73 static void set_menu_indicator(gchar
* name
, gint percent
)
75 static gchar
*rebuilding
= NULL
;
78 set_menu_label(rebuilding_entry
, "", FALSE
);
82 if (rebuilding
== NULL
)
84 rebuilding
= _("Rebuilding:");
86 label
= g_strdup_printf("%s %s (%d%%)", rebuilding
, name
, percent
);
87 set_menu_label(rebuilding_entry
, label
, FALSE
);
93 /* Update the percent indicator. */
94 static void set_progress(gchar
* menu
, gint
* percent
, gint number
)
96 if ((*percent
+ 1) * tree_count
<= 100 * number
) {
97 *percent
= (100 * number
) / tree_count
;
98 set_menu_indicator(menu
, *percent
);
102 /* We don't want to perform the signal lookup for each file. */
103 static void connect_activate(GtkMenuItem
* instance
, const gchar
* filename
)
105 static guint signal_id
= 0;
110 signal_id
= g_signal_lookup("activate", G_TYPE_FROM_INSTANCE(instance
));
112 closure
= g_cclosure_new_swap(G_CALLBACK(menu_load
),
113 (gpointer
) filename
, NULL
);
115 g_signal_connect_closure_by_id(instance
, signal_id
, 0, closure
, FALSE
);
119 MENU_FILE
, /* From both menus. */
120 MENU_DIR
, /* From the Images menu. */
121 MENU_SUBMENU
/* From the Directories menu. */
124 /* Add also a mnemonic and a thumbnail. */
125 static GtkMenuItem
*add_menu_item(DirtyGtkMenu
* menu
,
126 tree_item
* item
, menu_type type
)
129 GtkMenuItem
*menu_item
;
130 GtkImage
*thumbnail
= NULL
;
132 if (options
->thumbnails
&& type
== MENU_FILE
&& fill_thumbnail(item
))
133 thumbnail
= GTK_IMAGE(gtk_image_new_from_pixbuf(item
->thumb
));
135 /* Simulate the do_threaded() effect of fill_thumbnail(). */
138 if (type
== MENU_DIR
)
143 if (options
->mnemonics
&& type
!= MENU_DIR
) {
144 name
= add_mnemonic(name
);
148 GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(name
));
150 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name
));
154 menu_item
= GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(name
));
156 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_label(name
));
160 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item
),
161 GTK_WIDGET(thumbnail
));
163 dirty_gtk_menu_append(menu
, GTK_WIDGET(menu_item
));
167 connect_activate(menu_item
, item
->path
);
171 gtk_widget_set_sensitive(GTK_WIDGET(menu_item
), FALSE
);
181 /*** Directories menu. ***/
183 static void add_file_from_tree(DirtyGtkMenu
* menu
, tree_item
* item
)
185 static gchar
*directories
= NULL
;
186 static gint number
= 0, percent
= 0;
189 if (canceled_using_tree())
193 /* First time for this menu. */
195 if (directories
== NULL
)
197 directories
= _("Directories");
204 dirname
= g_path_get_dirname(item
->path
);
205 g_hash_table_insert(dir_menu_hash
, dirname
,
206 dirty_gtk_menu_get_tearoff(menu
));
207 add_menu_item(menu
, item
, MENU_FILE
);
209 set_progress(directories
, &percent
, number
);
213 static DirtyGtkMenu
*add_sub_menu(DirtyGtkMenu
* parent
, tree_item
* item
)
216 GtkMenuItem
*menu_item
;
218 menu_item
= add_menu_item(parent
, item
, MENU_SUBMENU
);
220 menu
= dirty_gtk_menu_new();
221 gtk_menu_item_set_submenu(menu_item
, GTK_WIDGET(menu
->gtk_menu_shell
));
226 static void make_menu_from_tree_rec(GNode
* tree
, DirtyGtkMenu
* parent
)
231 if (canceled_using_tree())
234 if (G_NODE_IS_LEAF(tree
)) {
235 add_file_from_tree(parent
, tree
->data
);
240 menu
= add_sub_menu(parent
, item
);
244 g_node_children_foreach(tree
, G_TRAVERSE_ALL
,
245 (GNodeForeachFunc
) make_menu_from_tree_rec
, menu
);
247 dirty_gtk_menu_release(menu
);
251 static gboolean
open_current_dir_menu(void)
253 if (dir_menu_hash
&& current_image
) {
254 gchar
*dir
= g_path_get_dirname(current_image
->node
->data
);
255 GtkTearoffMenuItem
*tearoff
= g_hash_table_lookup(dir_menu_hash
, dir
);
260 gtk_menu_item_activate(GTK_MENU_ITEM(tearoff
));
266 static void add_current_dir_entry(DirtyGtkMenu
* menu
)
271 label
= add_mnemonic(_("Current image directory..."));
273 item
= GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(label
));
274 g_signal_connect_swapped(item
, "activate",
275 G_CALLBACK(open_current_dir_menu
), NULL
);
277 gtk_widget_show(GTK_WIDGET(item
));
278 dirty_gtk_menu_append(menu
, GTK_WIDGET(item
));
281 g_hash_table_destroy(dir_menu_hash
);
284 g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
287 /*** Images menu. ***/
289 static gboolean
add_file_item(GNode
* tree
, DirtyGtkMenu
* parent_menu
)
291 static gchar
*images
= NULL
;
292 static gint number
= 0, percent
= 0;
295 if (canceled_using_tree())
298 if (parent_menu
== NULL
) {
299 /* First time for this menu. */
303 images
= _("Images");
310 /* Check if it is the first image in its directory. */
311 sibling
= g_node_prev_sibling(tree
);
312 if (sibling
== NULL
&& tree
->parent
!= NULL
)
313 add_menu_item(parent_menu
, tree
->parent
->data
, MENU_DIR
);
315 add_menu_item(parent_menu
, tree
->data
, MENU_FILE
);
317 set_progress(images
, &percent
, number
);
323 /*** Menus builders. ***/
325 static DirtyGtkMenu
*begin_rebuild(GtkMenuItem
* root
, GCallback func
)
327 static DirtyGtkMenu
*menu
;
328 GtkWidget
*rebuilder
;
331 gtk_menu_item_deselect(root
);
333 menu
= dirty_gtk_menu_new();
336 gtk_menu_item_new_with_mnemonic(add_mnemonic(_("Rebuild this menu")));
338 g_signal_connect_swapped(rebuilder
, "activate", func
, NULL
);
339 dirty_gtk_menu_append(menu
, rebuilder
);
341 gtk_menu_item_set_submenu(root
, GTK_WIDGET(menu
->gtk_menu_shell
));
346 static GNode
*get_tree(void)
350 * There is no problem in rebuilding the images menus during
351 * a loading, it would just make the loading too long.
358 #define REBUILD_START(name, func) \
360 if (root != NULL) { \
365 if (more_recent_than_tree(timestamp)) \
366 /* The menu is already up to date. */ \
373 tree_count = tree_count_files(tree); \
374 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), TRUE); \
376 menu = begin_rebuild(menu_item, G_CALLBACK(func)); \
377 set_menu_indicator(name, 0); \
380 #define REBUILD_END() \
383 gtk_menu_item_deselect(root); \
385 set_menu_indicator(NULL, 0); \
386 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), FALSE); \
387 if (canceled_using_tree()) \
388 reset_timestamp(×tamp); \
393 gtk_widget_show_all(GTK_WIDGET(menu_item)); \
394 dirty_gtk_menu_release(menu); \
396 return timestamp != 0; \
399 gboolean
rebuild_directories(GtkMenuItem
* root
)
401 static GtkMenuItem
*menu_item
;
402 static DECLARE_TIMESTAMP(timestamp
);
405 gchar
*prefix
, *old_name
;
408 REBUILD_START(_("Directories"), rebuild_directories
);
409 add_current_dir_entry(menu
);
411 /* Build the menu. */
412 add_file_from_tree(NULL
, NULL
);
417 if (prefix
[0] == '\0' || prefix
[1] == '\0') {
418 for (child
= g_node_first_child(tree
); child
; child
= child
->next
) {
420 old_name
= item
->name
;
421 item
->name
= g_build_filename(prefix
, item
->name
, NULL
);
423 make_menu_from_tree_rec(child
, menu
);
426 item
->name
= old_name
;
429 make_menu_from_tree_rec(tree
, menu
);
434 gboolean
rebuild_images(GtkMenuItem
* root
)
436 static GtkMenuItem
*menu_item
;
437 static DECLARE_TIMESTAMP(timestamp
);
441 REBUILD_START(_("Images"), rebuild_images
);
443 /* Build the menu. */
444 add_file_item(NULL
, NULL
);
445 g_node_traverse(tree
, G_PRE_ORDER
, G_TRAVERSE_LEAFS
, -1,
446 (GNodeTraverseFunc
) add_file_item
, menu
);
451 /* Rebuild both menus. */
452 gboolean
rebuild_images_menus(void)
454 return rebuild_directories(NULL
) && rebuild_images(NULL
);
457 void set_stop_rebuilding_menu(GtkMenuItem
* item
)
459 cancel_menu_item
= item
;
460 gtk_widget_set_sensitive(GTK_WIDGET(item
), FALSE
);
463 gboolean
is_rebuilding_a_menu(void)
465 return tree_count
!= 0;