r1355: Much improved layout for Options box (Andras Mohari).
[rox-filer.git] / ROX-Filer / src / toolbar.c
blob386b59194a097130f7034e49d6a5e09eb5a07341
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2002, the ROX-Filer team.
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the Free
9 * Software Foundation; either version 2 of the License, or (at your option)
10 * any later version.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
17 * You should have received a copy of the GNU General Public License along with
18 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
19 * Place, Suite 330, Boston, MA 02111-1307 USA
22 /* toolbar.c - for the button bars that go along the tops of windows */
24 #include "config.h"
26 #include <string.h>
28 #include "global.h"
30 #include "collection.h"
31 #include "toolbar.h"
32 #include "options.h"
33 #include "support.h"
34 #include "main.h"
35 #include "menu.h"
36 #include "dnd.h"
37 #include "filer.h"
38 #include "display.h"
39 #include "pixmaps.h"
40 #include "bind.h"
41 #include "type.h"
42 #include "dir.h"
43 #include "diritem.h"
45 typedef struct _Tool Tool;
47 typedef enum {DROP_NONE, DROP_TO_PARENT, DROP_TO_HOME} DropDest;
49 struct _Tool {
50 guchar *label;
51 guchar *name;
52 guchar *tip; /* Tooltip */
53 void (*clicked)(GtkWidget *w, FilerWindow *filer_window);
54 DropDest drop_action;
55 gboolean enabled;
56 MaskedPixmap *icon;
57 GtkWidget **menu; /* Right-click menu widget addr */
60 Option o_toolbar, o_toolbar_info, o_toolbar_disable;
62 static GtkTooltips *tooltips = NULL;
64 /* TRUE if the button presses (or released) should open a new window,
65 * rather than reusing the existing one.
67 #define NEW_WIN_BUTTON(button_event) \
68 (o_new_button_1.int_value \
69 ? ((GdkEventButton *) button_event)->button == 1 \
70 : ((GdkEventButton *) button_event)->button != 1)
72 /* Static prototypes */
73 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window);
74 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
75 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
76 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window);
77 static void toolbar_refresh_clicked(GtkWidget *widget,
78 FilerWindow *filer_window);
79 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window);
80 static void toolbar_details_clicked(GtkWidget *widget,
81 FilerWindow *filer_window);
82 static void toolbar_hidden_clicked(GtkWidget *widget,
83 FilerWindow *filer_window);
84 static GtkWidget *add_button(GtkWidget *box, Tool *tool,
85 FilerWindow *filer_window);
86 static GtkWidget *create_toolbar(FilerWindow *filer_window);
87 static gboolean drag_motion(GtkWidget *widget,
88 GdkDragContext *context,
89 gint x,
90 gint y,
91 guint time,
92 FilerWindow *filer_window);
93 static void drag_leave(GtkWidget *widget,
94 GdkDragContext *context,
95 guint32 time,
96 FilerWindow *filer_window);
97 static void handle_drops(FilerWindow *filer_window,
98 GtkWidget *button,
99 DropDest dest);
100 static void coll_selection_changed(Collection *collection, guint time,
101 gpointer user_data);
102 static void recreate_toolbar(FilerWindow *filer_window);
103 static void toggle_shaded(GtkWidget *widget);
104 static void option_notify(void);
105 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label);
107 static Tool all_tools[] = {
108 {N_("Close"), "close", N_("Close filer window"),
109 toolbar_close_clicked, DROP_NONE, FALSE,
110 NULL, NULL},
112 {N_("Up"), "up", N_("Change to parent directory"),
113 toolbar_up_clicked, DROP_TO_PARENT, TRUE,
114 NULL, NULL},
116 {N_("Home"), "home", N_("Change to home directory"),
117 toolbar_home_clicked, DROP_TO_HOME, TRUE,
118 NULL, NULL},
120 {N_("Scan"), "refresh", N_("Rescan directory contents"),
121 toolbar_refresh_clicked, DROP_NONE, TRUE,
122 NULL, NULL},
124 {N_("Size"), "zoom", N_("Change icon size"),
125 toolbar_size_clicked, DROP_NONE, TRUE,
126 NULL, NULL},
128 {N_("Details"), "details", N_("Show extra details"),
129 toolbar_details_clicked, DROP_NONE, TRUE,
130 NULL, NULL},
132 {N_("Hidden"), "hidden", N_("Show/hide hidden files"),
133 toolbar_hidden_clicked, DROP_NONE, TRUE,
134 NULL, NULL},
136 {N_("Help"), "help", N_("Show ROX-Filer help"),
137 toolbar_help_clicked, DROP_NONE, TRUE,
138 NULL, NULL},
142 /****************************************************************
143 * EXTERNAL INTERFACE *
144 ****************************************************************/
146 void toolbar_init(void)
148 int i;
150 option_add_int(&o_toolbar, "toolbar_type", TOOLBAR_NORMAL);
151 option_add_int(&o_toolbar_info, "toolbar_show_info", 1);
152 option_add_string(&o_toolbar_disable, "toolbar_disable", "close");
153 option_add_notify(option_notify);
155 tooltips = gtk_tooltips_new();
157 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
159 Tool *tool = &all_tools[i];
161 if (!tool->icon)
163 guchar *path;
165 path = g_strconcat("images/",
166 tool->name, ".png", NULL);
167 tool->icon = load_pixmap(path);
168 g_free(path);
172 option_register_widget("tool-options", build_tool_options);
175 /* Returns a button which can be used to turn a tool on and off.
176 * Button has 'tool_name' set to the tool's name.
177 * NULL if tool number i is too big.
179 GtkWidget *toolbar_tool_option(int i)
181 Tool *tool = &all_tools[i];
182 GtkWidget *button;
183 GtkWidget *icon_widget, *vbox, *text;
185 g_return_val_if_fail(i >= 0, NULL);
187 if (i >= sizeof(all_tools) / sizeof(*all_tools))
188 return NULL;
190 button = gtk_button_new();
191 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
192 gtk_tooltips_set_tip(tooltips, button, _(tool->tip), NULL);
194 icon_widget = gtk_image_new_from_pixmap(tool->icon->pixmap,
195 tool->icon->mask);
197 vbox = gtk_vbox_new(FALSE, 0);
198 gtk_box_pack_start(GTK_BOX(vbox), icon_widget, TRUE, TRUE, 0);
200 text = gtk_label_new(_(tool->label));
201 gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, TRUE, 0);
203 gtk_container_add(GTK_CONTAINER(button), vbox);
205 gtk_container_set_border_width(GTK_CONTAINER(button), 1);
206 gtk_misc_set_padding(GTK_MISC(icon_widget), 16, 1);
208 g_signal_connect_swapped(button, "clicked",
209 G_CALLBACK(toggle_shaded), vbox);
211 g_object_set_data(G_OBJECT(button), "tool_name", tool->name);
213 return button;
216 /* Create a new toolbar widget, suitable for adding to a filer window,
217 * and return it.
219 GtkWidget *toolbar_new(FilerWindow *filer_window)
221 g_return_val_if_fail(filer_window != NULL, NULL);
222 g_return_val_if_fail(o_toolbar.int_value != TOOLBAR_NONE, NULL);
224 return create_toolbar(filer_window);
227 void toolbar_update_info(FilerWindow *filer_window)
229 if (o_toolbar.int_value != TOOLBAR_NONE && o_toolbar_info.int_value)
230 coll_selection_changed(filer_window->collection,
231 GDK_CURRENT_TIME, NULL);
235 /****************************************************************
236 * INTERNAL FUNCTIONS *
237 ****************************************************************/
239 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window)
241 GdkEvent *event;
243 event = gtk_get_current_event();
244 if (event->type == GDK_BUTTON_RELEASE &&
245 ((GdkEventButton *) event)->button != 1)
246 filer_change_to(filer_window,
247 make_path(app_dir, "Help")->str, NULL);
248 else
249 filer_opendir(make_path(app_dir, "Help")->str, NULL);
252 static void toolbar_refresh_clicked(GtkWidget *widget,
253 FilerWindow *filer_window)
255 GdkEvent *event;
257 event = gtk_get_current_event();
258 if (event->type == GDK_BUTTON_RELEASE &&
259 ((GdkEventButton *) event)->button != 1)
261 filer_opendir(filer_window->path, filer_window);
263 else
265 full_refresh();
266 filer_update_dir(filer_window, TRUE);
270 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
272 GdkEvent *event;
274 event = gtk_get_current_event();
275 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
277 filer_opendir(home_dir, filer_window);
279 else
280 filer_change_to(filer_window, home_dir, NULL);
283 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window)
285 GdkEvent *event;
287 g_return_if_fail(filer_window != NULL);
289 event = gtk_get_current_event();
290 if (event->type == GDK_BUTTON_RELEASE &&
291 ((GdkEventButton *) event)->button != 1)
293 filer_opendir(filer_window->path, filer_window);
295 else
296 gtk_widget_destroy(filer_window->window);
299 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
301 GdkEvent *event;
303 event = gtk_get_current_event();
304 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
306 filer_open_parent(filer_window);
308 else
309 change_to_parent(filer_window);
312 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window)
314 GdkEventButton *bev;
316 bev = (GdkEventButton *) gtk_get_current_event();
318 display_change_size(filer_window,
319 bev->type == GDK_BUTTON_RELEASE && bev->button == 1);
322 static void toolbar_details_clicked(GtkWidget *widget,
323 FilerWindow *filer_window)
325 if (filer_window->details_type == DETAILS_NONE)
326 display_set_layout(filer_window,
327 filer_window->display_style,
328 DETAILS_SUMMARY);
329 else
330 display_set_layout(filer_window,
331 filer_window->display_style,
332 DETAILS_NONE);
335 static void toolbar_hidden_clicked(GtkWidget *widget,
336 FilerWindow *filer_window)
338 display_set_hidden(filer_window, !filer_window->show_hidden);
341 static GtkWidget *create_toolbar(FilerWindow *filer_window)
343 GtkWidget *box;
344 GtkWidget *b;
345 int i;
347 box = gtk_hbox_new(FALSE, 0);
349 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
351 Tool *tool = &all_tools[i];
353 if (!tool->enabled)
354 continue;
356 b = add_button(box, tool, filer_window);
357 if (tool->drop_action != DROP_NONE)
358 handle_drops(filer_window, b, tool->drop_action);
361 filer_window->toolbar_text = gtk_label_new("");
362 gtk_misc_set_alignment(GTK_MISC(filer_window->toolbar_text), 0, 0.5);
363 gtk_widget_set_size_request(filer_window->toolbar_text, 8, -1);
364 gtk_box_pack_start(GTK_BOX(box), filer_window->toolbar_text,
365 TRUE, TRUE, 4);
367 if (o_toolbar_info.int_value)
368 g_signal_connect_object(filer_window->collection,
369 "selection_changed",
370 G_CALLBACK(coll_selection_changed), box, 0);
372 return box;
375 /* This is used to simulate a click when button 3 is used (GtkButton
376 * normally ignores this).
378 static gint toolbar_other_button = 0;
379 static gint toolbar_adjust_pressed(GtkButton *button,
380 GdkEventButton *event,
381 FilerWindow *filer_window)
383 gint b = event->button;
385 if ((b == 2 || b == 3) && toolbar_other_button == 0)
387 toolbar_other_button = event->button;
388 gtk_grab_add(GTK_WIDGET(button));
389 gtk_button_pressed(button);
391 return TRUE;
394 return FALSE;
397 static gint toolbar_adjust_released(GtkButton *button,
398 GdkEventButton *event,
399 FilerWindow *filer_window)
401 if (event->button == toolbar_other_button)
403 toolbar_other_button = 0;
404 gtk_grab_remove(GTK_WIDGET(button));
405 gtk_button_released(button);
407 return TRUE;
410 return FALSE;
413 #if 0
414 static gint menu_pressed(GtkWidget *button,
415 GdkEventButton *event,
416 FilerWindow *filer_window)
418 GtkWidget *menu;
420 if (event->button != 3 && event->button != 2)
421 return FALSE;
423 menu = gtk_object_get_data(GTK_OBJECT(button), "popup_menu");
424 g_return_val_if_fail(menu != NULL, TRUE);
426 show_style_menu(filer_window, event, menu);
428 return TRUE;
430 #endif
432 static GtkWidget *add_button(GtkWidget *box, Tool *tool,
433 FilerWindow *filer_window)
435 GtkWidget *button, *icon_widget;
437 button = gtk_button_new();
438 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
439 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
441 if (tool->menu)
443 g_warning("Not implemented");
444 #if 0
445 gtk_object_set_data(GTK_OBJECT(button), "popup_menu",
446 *tool->menu);
447 gtk_signal_connect(GTK_OBJECT(button), "button_press_event",
448 GTK_SIGNAL_FUNC(menu_pressed), filer_window);
449 #endif
451 else
453 g_signal_connect(button, "button_press_event",
454 G_CALLBACK(toolbar_adjust_pressed), filer_window);
455 g_signal_connect(button, "button_release_event",
456 G_CALLBACK(toolbar_adjust_released), filer_window);
459 g_signal_connect(button, "clicked",
460 G_CALLBACK(tool->clicked), filer_window);
462 gtk_tooltips_set_tip(tooltips, button, _(tool->tip), NULL);
464 icon_widget = gtk_image_new_from_pixmap(tool->icon->pixmap,
465 tool->icon->mask);
467 if (o_toolbar.int_value == TOOLBAR_LARGE)
469 GtkWidget *vbox, *text;
471 vbox = gtk_vbox_new(FALSE, 0);
472 gtk_box_pack_start(GTK_BOX(vbox), icon_widget, TRUE, TRUE, 0);
474 text = gtk_label_new(_(tool->label));
475 gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, TRUE, 0);
477 gtk_container_add(GTK_CONTAINER(button), vbox);
479 else
480 gtk_container_add(GTK_CONTAINER(button), icon_widget);
482 gtk_container_set_border_width(GTK_CONTAINER(button), 1);
483 gtk_misc_set_padding(GTK_MISC(icon_widget),
484 o_toolbar.int_value == TOOLBAR_LARGE ? 16 : 8, 1);
485 gtk_box_pack_start(GTK_BOX(box), button, FALSE, TRUE, 0);
487 return button;
490 static void toggle_shaded(GtkWidget *widget)
492 gtk_widget_set_sensitive(widget, !GTK_WIDGET_SENSITIVE(widget));
493 option_check_widget(&o_toolbar_disable);
496 /* Called during the drag when the mouse is in a widget registered
497 * as a drop target. Returns TRUE if we can accept the drop.
499 static gboolean drag_motion(GtkWidget *widget,
500 GdkDragContext *context,
501 gint x,
502 gint y,
503 guint time,
504 FilerWindow *filer_window)
506 GdkDragAction action = context->suggested_action;
507 DropDest dest;
509 dest = (DropDest) g_object_get_data(G_OBJECT(widget), "toolbar_dest");
511 if (dest == DROP_TO_HOME)
512 g_dataset_set_data(context, "drop_dest_path",
513 (gchar *) home_dir);
514 else
516 gchar *slash, *path;
518 slash = strrchr(filer_window->path, '/');
519 if (slash == NULL || slash == filer_window->path)
520 path = g_strdup("/");
521 else
522 path = g_strndup(filer_window->path,
523 slash - filer_window->path);
524 g_dataset_set_data_full(context, "drop_dest_path",
525 path, g_free);
528 g_dataset_set_data(context, "drop_dest_type", drop_dest_dir);
529 gdk_drag_status(context, action, time);
531 dnd_spring_load(context, filer_window);
532 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NORMAL);
534 return TRUE;
537 static void drag_leave(GtkWidget *widget,
538 GdkDragContext *context,
539 guint32 time,
540 FilerWindow *filer_window)
542 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
543 dnd_spring_abort();
546 static void handle_drops(FilerWindow *filer_window,
547 GtkWidget *button,
548 DropDest dest)
550 make_drop_target(button, 0);
551 g_signal_connect(button, "drag_motion",
552 G_CALLBACK(drag_motion), filer_window);
553 g_signal_connect(button, "drag_leave",
554 G_CALLBACK(drag_leave), filer_window);
555 g_object_set_data(G_OBJECT(button), "toolbar_dest", (gpointer) dest);
558 static void tally_items(gpointer key, gpointer value, gpointer data)
560 guchar *leafname = (guchar *) key;
561 int *tally = (int *) data;
563 if (leafname[0] == '.')
564 (*tally)++;
567 static void coll_selection_changed(Collection *collection, guint time,
568 gpointer user_data)
570 FilerWindow *filer_window;
571 gchar *label;
573 filer_window = g_object_get_data(G_OBJECT(collection), "filer_window");
575 g_return_if_fail(filer_window != NULL);
577 if (filer_window->target_cb)
578 return;
580 if (collection->number_selected == 0)
582 gchar *s = NULL;
584 if (filer_window->scanning)
586 gtk_label_set_text(
587 GTK_LABEL(filer_window->toolbar_text), "");
588 return;
591 if (!filer_window->show_hidden)
593 GHashTable *hash = filer_window->directory->known_items;
594 int tally = 0;
596 g_hash_table_foreach(hash, tally_items, &tally);
598 if (tally)
599 s = g_strdup_printf(_(" (%u hidden)"), tally);
602 if (collection->number_of_items)
603 label = g_strdup_printf("%d %s%s",
604 collection->number_of_items,
605 collection->number_of_items != 1
606 ? _("items") : _("item"),
607 s ? s : "");
608 else /* (French plurals work differently for zero) */
609 label = g_strdup_printf(_("No items%s"),
610 s ? s : "");
611 g_free(s);
613 else
615 gint i = collection->number_of_items;
616 double size = 0;
618 while (i--)
620 DirItem *item = (DirItem *) collection->items[i].data;
622 if (collection->items[i].selected
623 && item->base_type != TYPE_DIRECTORY)
624 size += (double) item->size;
626 label = g_strdup_printf(_("%u selected (%s)"),
627 collection->number_selected,
628 format_double_size(size));
631 gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), label);
632 g_free(label);
635 static void recreate_toolbar(FilerWindow *filer_window)
637 GtkWidget *frame = filer_window->toolbar_frame;
639 if (GTK_BIN(frame)->child)
641 filer_window->toolbar_text = NULL;
642 gtk_widget_destroy(((GtkBin *) frame)->child);
645 if (o_toolbar.int_value == TOOLBAR_NONE)
646 gtk_widget_hide(frame);
647 else
649 GtkWidget *toolbar;
651 toolbar = toolbar_new(filer_window);
652 gtk_container_add(GTK_CONTAINER(filer_window->toolbar_frame),
653 toolbar);
654 gtk_widget_show_all(frame);
657 filer_target_mode(filer_window, NULL, NULL, NULL);
658 toolbar_update_info(filer_window);
661 static void option_notify(void)
663 guchar *list;
664 int i;
665 gboolean changed = FALSE;
667 list = o_toolbar_disable.value;
669 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
671 Tool *tool = &all_tools[i];
672 gboolean old = tool->enabled;
674 tool->enabled = !in_list(tool->name, list);
676 if (old != tool->enabled)
677 changed = TRUE;
680 if (changed || o_toolbar.has_changed || o_toolbar_info.has_changed)
682 GList *next;
684 for (next = all_filer_windows; next; next = next->next)
686 FilerWindow *filer_window = (FilerWindow *) next->data;
688 recreate_toolbar(filer_window);
693 static void update_tools(Option *option)
695 GList *next, *kids;
697 kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
699 for (next = kids; next; next = next->next)
701 GtkWidget *kid = (GtkWidget *) next->data;
702 guchar *name;
704 name = g_object_get_data(G_OBJECT(kid), "tool_name");
706 g_return_if_fail(name != NULL);
708 gtk_widget_set_sensitive(GTK_BIN(kid)->child,
709 !in_list(name, option->value));
712 g_list_free(kids);
715 static guchar *read_tools(Option *option)
717 GList *next, *kids;
718 GString *list;
719 guchar *retval;
721 list = g_string_new(NULL);
723 kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
725 for (next = kids; next; next = next->next)
727 GtkObject *kid = (GtkObject *) next->data;
728 guchar *name;
730 if (!GTK_WIDGET_SENSITIVE(GTK_BIN(kid)->child))
732 name = g_object_get_data(G_OBJECT(kid), "tool_name");
733 g_return_val_if_fail(name != NULL, list->str);
735 if (list->len)
736 g_string_append(list, ", ");
737 g_string_append(list, name);
741 g_list_free(kids);
742 retval = list->str;
743 g_string_free(list, FALSE);
745 return retval;
748 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label)
750 int i = 0;
751 GtkWidget *hbox, *tool, *sw, *box;
753 g_return_val_if_fail(option != NULL, NULL);
755 box = gtk_hbox_new(FALSE, 0);
756 hbox = gtk_hbox_new(FALSE, 0);
758 sw = gtk_scrolled_window_new(NULL, NULL);
759 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw),
760 GTK_SHADOW_NONE);
761 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
762 GTK_POLICY_ALWAYS, GTK_POLICY_NEVER);
764 while ((tool = toolbar_tool_option(i++)))
765 gtk_box_pack_start(GTK_BOX(hbox), tool, FALSE, TRUE, 0);
767 option->update_widget = update_tools;
768 option->read_widget = read_tools;
769 option->widget = hbox;
771 gtk_box_pack_start(GTK_BOX(box), sw, TRUE, TRUE, 0);
772 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), hbox);
774 return g_list_append(NULL, box);