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"
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
;
63 set_menu_label(rebuilding_entry
, "", FALSE
);
67 if (rebuilding
== NULL
)
69 rebuilding
= _("Rebuilding:");
71 label
= g_strdup_printf("%s %s (%d%%)", rebuilding
, name
, percent
);
72 set_menu_label(rebuilding_entry
, label
, FALSE
);
78 /* Update the percent indicator. */
79 static void set_progress(gchar
* menu
, gint
* percent
, gint number
)
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;
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
);
108 MENU_FILE
, /* From both menus. */
109 MENU_DIR
, /* From the Images menu. */
110 MENU_SUBMENU
/* From the Directories menu. */
113 /* Add also a mnemonic and a thumbnail. */
114 static GtkMenuItem
*new_menu_item(tree_item
* item
, menu_type type
)
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
));
123 /* Simulate the do_threaded() effect of fill_thumbnail(). */
126 if (type
== MENU_DIR
)
131 if (options
->mnemonics
&& type
!= MENU_DIR
) {
132 name
= add_mnemonic(name
);
136 GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(name
));
138 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name
));
142 menu_item
= GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(name
));
144 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_label(name
));
148 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item
),
149 GTK_WIDGET(thumbnail
));
153 connect_activate(menu_item
, item
->path
);
157 gtk_widget_set_sensitive(GTK_WIDGET(menu_item
), FALSE
);
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
)
176 for (ptr
= list
; ptr
!= NULL
; ptr
= ptr
->next
)
177 gtk_menu_shell_insert(menu
, GTK_WIDGET(ptr
->data
), pos
);
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())
193 /* First time for this menu. */
195 if (directories
== NULL
)
197 directories
= _("Directories");
204 *list
= g_slist_prepend(*list
, new_menu_item(item
, MENU_FILE
));
206 set_progress(directories
, &percent
, number
);
210 static GtkMenuItem
*add_sub_menu(GSList
** list
, tree_item
* item
)
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
);
226 static void make_menu_from_tree_rec(GNode
* tree
, GSList
** list
)
228 GtkMenuItem
*menu_item
;
230 GSList
*new_list
= NULL
;
232 if (canceled_using_tree())
237 if (G_NODE_IS_LEAF(tree
)) {
238 add_file_from_tree(list
, item
);
242 menu_item
= add_sub_menu(list
, item
);
246 g_node_children_foreach(tree
, G_TRAVERSE_ALL
,
247 (GNodeForeachFunc
) make_menu_from_tree_rec
,
252 insert_list_into_menu(GTK_MENU_SHELL(gtk_menu_item_get_submenu(menu_item
)),
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;
264 if (canceled_using_tree())
268 /* First time for this menu. */
272 images
= _("Images");
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
);
294 /*** Menus builders. ***/
296 static GtkWidget
*common_rebuild(GtkMenuItem
* root
, gboolean start
)
298 static GtkMenuShell
*menu
;
301 gtk_menu_item_deselect(root
);
303 if (start
== FALSE
) {
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)
326 * There is no problem in rebuilding the images menus during
327 * a loading, it would just make the loading too long.
334 #define REBUILD_START(name) \
336 if (root != NULL) { \
341 if (has_timestamp(timestamp) && last_tree_ok(timestamp)) \
342 /* The menu is already up to date. */ \
349 gtk_widget_set_sensitive(GTK_WIDGET(cancel_menu_item), TRUE); \
351 menu = GTK_MENU_SHELL(common_rebuild(menu_item, TRUE)); \
352 set_menu_indicator(name, 0); \
355 #define REBUILD_END(function) \
357 GtkMenuItem *launcher; \
359 launcher = GTK_MENU_ITEM(common_rebuild(NULL, FALSE)); \
360 g_signal_connect_swapped(launcher, "activate", G_CALLBACK(function), \
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(×tamp); \
373 return timestamp != 0; \
376 gboolean
rebuild_directories(GtkMenuItem
* root
)
378 static GtkMenuItem
*menu_item
;
379 static DECLARE_TIMESTAMP(timestamp
);
382 gchar
*prefix
, *old_name
;
386 REBUILD_START(_("Directories"));
388 /* Build the menu. */
389 add_file_from_tree(NULL
, NULL
);
394 if (prefix
[0] == '\0' || prefix
[1] == '\0') {
395 for (child
= g_node_first_child(tree
); child
; child
= child
->next
) {
397 old_name
= item
->name
;
398 item
->name
= g_build_filename(prefix
, item
->name
, NULL
);
400 make_menu_from_tree_rec(child
, &list
);
403 item
->name
= old_name
;
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
);
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
);