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 "str_utils.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
);
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
)
98 const gchar
*ptr_orig
;
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. */
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;
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
);
140 MENU_FILE
, /* From both menus. */
141 MENU_DIR
, /* From the Images menu. */
142 MENU_SUBMENU
/* From the Directories menu. */
145 /* Add also a mnemonic and a thumbnail. */
146 static GtkMenuItem
*add_menu_item(GtkMenuShell
* menu
,
147 tree_item
* item
, menu_type type
)
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
)
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
);
170 GTK_MENU_ITEM(gtk_image_menu_item_new_with_mnemonic(name
));
172 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_mnemonic(name
));
176 menu_item
= GTK_MENU_ITEM(gtk_image_menu_item_new_with_label(name
));
178 menu_item
= GTK_MENU_ITEM(gtk_menu_item_new_with_label(name
));
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);
189 connect_activate(menu_item
, item
->path
);
193 gtk_widget_set_sensitive(GTK_WIDGET(menu_item
), FALSE
);
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())
215 /* First time for this menu. */
217 if (directories
== NULL
)
219 directories
= _("Directories");
226 add_menu_item(menu
, item
, MENU_FILE
);
228 set_progress(directories
, &percent
, number
);
232 static GtkMenuShell
*add_sub_menu(GtkMenuShell
* parent
, tree_item
* item
)
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());
247 static void make_menu_from_tree_rec(GNode
* tree
, GtkMenuShell
* parent
)
252 if (canceled_using_tree())
255 if (G_NODE_IS_LEAF(tree
))
256 add_file_from_tree(parent
, 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
,
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;
274 if (canceled_using_tree())
277 if (parent_menu
== NULL
) {
278 /* First time for this menu. */
282 images
= _("Images");
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
);
302 /*** Menus builders. ***/
304 static GtkWidget
*common_rebuild(GtkMenuItem
* root
, gboolean start
)
306 static GtkMenuShell
*menu
;
309 gtk_menu_item_deselect(root
);
311 if (start
== FALSE
) {
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)
334 * There is no problem in rebuilding the images menus during
335 * a loading, it would just make the loading too long.
342 #define REBUILD_START(name) \
344 if (root != NULL) { \
349 if (has_timestamp(timestamp) && last_tree_ok(timestamp)) \
350 /* The menu is already up to date. */ \
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); \
362 #define REBUILD_END(function) \
364 GtkMenuItem *launcher; \
366 launcher = GTK_MENU_ITEM(common_rebuild(NULL, FALSE)); \
367 g_signal_connect_swapped(launcher, "activate", G_CALLBACK(function), \
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(×tamp); \
378 return timestamp != 0; \
381 gboolean
rebuild_directories(GtkMenuItem
* root
)
383 static GtkMenuItem
*menu_item
;
384 static DECLARE_TIMESTAMP(timestamp
);
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
);
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
);