r2228: Made 'Automatic' an icon size, rather than a separate option.
[rox-filer.git] / ROX-Filer / src / toolbar.c
blobae0cdf94a3c9cd256fa4f399aa2db4b9b3ba53b9
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 "toolbar.h"
31 #include "options.h"
32 #include "support.h"
33 #include "main.h"
34 #include "menu.h"
35 #include "dnd.h"
36 #include "filer.h"
37 #include "display.h"
38 #include "pixmaps.h"
39 #include "bind.h"
40 #include "type.h"
41 #include "dir.h"
42 #include "diritem.h"
43 #include "view_iface.h"
44 #include "bookmarks.h"
46 typedef struct _Tool Tool;
48 typedef enum {DROP_NONE, DROP_TO_PARENT, DROP_TO_HOME} DropDest;
50 struct _Tool {
51 const gchar *label;
52 const gchar *name;
53 const gchar *tip; /* Tooltip */
54 void (*clicked)(GtkWidget *w, FilerWindow *filer_window);
55 DropDest drop_action;
56 gboolean enabled;
57 gboolean menu; /* Activate on button-press */
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_bookmarks_clicked(GtkWidget *widget,
77 FilerWindow *filer_window);
78 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window);
79 static void toolbar_refresh_clicked(GtkWidget *widget,
80 FilerWindow *filer_window);
81 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window);
82 static void toolbar_details_clicked(GtkWidget *widget,
83 FilerWindow *filer_window);
84 static void toolbar_hidden_clicked(GtkWidget *widget,
85 FilerWindow *filer_window);
86 static GtkWidget *add_button(GtkWidget *bar, Tool *tool,
87 FilerWindow *filer_window);
88 static GtkWidget *create_toolbar(FilerWindow *filer_window);
89 static gboolean drag_motion(GtkWidget *widget,
90 GdkDragContext *context,
91 gint x,
92 gint y,
93 guint time,
94 FilerWindow *filer_window);
95 static void drag_leave(GtkWidget *widget,
96 GdkDragContext *context,
97 guint32 time,
98 FilerWindow *filer_window);
99 static void handle_drops(FilerWindow *filer_window,
100 GtkWidget *button,
101 DropDest dest);
102 static void toggle_shaded(GtkWidget *widget);
103 static void option_notify(void);
104 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label);
105 static void tally_items(gpointer key, gpointer value, gpointer data);
107 static Tool all_tools[] = {
108 {N_("Close"), GTK_STOCK_CLOSE, N_("Close filer window"),
109 toolbar_close_clicked, DROP_NONE, FALSE,
110 FALSE},
112 {N_("Up"), GTK_STOCK_GO_UP, N_("Change to parent directory"),
113 toolbar_up_clicked, DROP_TO_PARENT, TRUE,
114 FALSE},
116 {N_("Home"), GTK_STOCK_HOME, N_("Change to home directory"),
117 toolbar_home_clicked, DROP_TO_HOME, TRUE,
118 FALSE},
120 {N_("Bookmarks"), ROX_STOCK_BOOKMARKS, N_("Bookmarks menu"),
121 toolbar_bookmarks_clicked, DROP_NONE, FALSE,
122 TRUE},
124 {N_("Scan"), GTK_STOCK_REFRESH, N_("Rescan directory contents"),
125 toolbar_refresh_clicked, DROP_NONE, TRUE,
126 FALSE},
128 {N_("Size"), GTK_STOCK_ZOOM_IN, N_("Change icon size"),
129 toolbar_size_clicked, DROP_NONE, TRUE,
130 FALSE},
132 {N_("Details"), ROX_STOCK_SHOW_DETAILS, N_("Show extra details"),
133 toolbar_details_clicked, DROP_NONE, TRUE,
134 FALSE},
136 {N_("Hidden"), ROX_STOCK_SHOW_HIDDEN, N_("Show/hide hidden files"),
137 toolbar_hidden_clicked, DROP_NONE, TRUE,
138 FALSE},
140 {N_("Help"), GTK_STOCK_HELP, N_("Show ROX-Filer help"),
141 toolbar_help_clicked, DROP_NONE, TRUE,
142 FALSE},
146 /****************************************************************
147 * EXTERNAL INTERFACE *
148 ****************************************************************/
150 void toolbar_init(void)
152 option_add_int(&o_toolbar, "toolbar_type", TOOLBAR_NORMAL);
153 option_add_int(&o_toolbar_info, "toolbar_show_info", 1);
154 option_add_string(&o_toolbar_disable, "toolbar_disable",
155 GTK_STOCK_CLOSE);
156 option_add_notify(option_notify);
158 tooltips = gtk_tooltips_new();
160 option_register_widget("tool-options", build_tool_options);
163 /* Returns a button which can be used to turn a tool on and off.
164 * Button has 'tool_name' set to the tool's name.
165 * NULL if tool number i is too big.
167 GtkWidget *toolbar_tool_option(int i)
169 Tool *tool = &all_tools[i];
170 GtkWidget *button;
171 GtkWidget *icon_widget, *vbox, *text;
173 g_return_val_if_fail(i >= 0, NULL);
175 if (i >= sizeof(all_tools) / sizeof(*all_tools))
176 return NULL;
178 button = gtk_button_new();
179 gtk_tooltips_set_tip(tooltips, button, _(tool->tip), NULL);
181 icon_widget = gtk_image_new_from_stock(tool->name,
182 GTK_ICON_SIZE_LARGE_TOOLBAR);
184 vbox = gtk_vbox_new(FALSE, 0);
185 gtk_box_pack_start(GTK_BOX(vbox), icon_widget, TRUE, TRUE, 0);
187 text = gtk_label_new(_(tool->label));
188 gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, TRUE, 0);
190 gtk_container_add(GTK_CONTAINER(button), vbox);
192 gtk_container_set_border_width(GTK_CONTAINER(button), 1);
194 g_signal_connect_swapped(button, "clicked",
195 G_CALLBACK(toggle_shaded), vbox);
197 g_object_set_data(G_OBJECT(button), "tool_name",
198 (gchar *) tool->name);
200 return button;
203 void toolbar_update_info(FilerWindow *filer_window)
205 gchar *label;
206 ViewIface *view;
207 int n_selected;
209 if (o_toolbar.int_value == TOOLBAR_NONE || !o_toolbar_info.int_value)
210 return; /* Not showing info */
212 if (filer_window->target_cb)
213 return;
215 view = filer_window->view;
217 n_selected = view_count_selected(view);
219 if (n_selected == 0)
221 gchar *s = NULL;
222 int n_items;
224 if (filer_window->scanning)
226 gtk_label_set_text(
227 GTK_LABEL(filer_window->toolbar_text), "");
228 return;
231 if (!filer_window->show_hidden)
233 GHashTable *hash = filer_window->directory->known_items;
234 int tally = 0;
236 g_hash_table_foreach(hash, tally_items, &tally);
238 if (tally)
239 s = g_strdup_printf(_(" (%u hidden)"), tally);
242 n_items = view_count_items(view);
244 if (n_items)
245 label = g_strdup_printf("%d %s%s",
246 n_items,
247 n_items != 1 ? _("items") : _("item"),
248 s ? s : "");
249 else /* (French plurals work differently for zero) */
250 label = g_strdup_printf(_("No items%s"),
251 s ? s : "");
252 g_free(s);
254 else
256 double size = 0;
257 ViewIter iter;
258 DirItem *item;
260 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
262 while ((item = iter.next(&iter)))
263 if (item->base_type != TYPE_DIRECTORY)
264 size += (double) item->size;
266 label = g_strdup_printf(_("%u selected (%s)"),
267 n_selected, format_double_size(size));
270 gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), label);
271 g_free(label);
274 /* Create, destroy or recreate toolbar for this window so that it
275 * matches the option setting.
277 void toolbar_update_toolbar(FilerWindow *filer_window)
279 g_return_if_fail(filer_window != NULL);
281 if (filer_window->toolbar)
283 gtk_widget_destroy(filer_window->toolbar);
284 filer_window->toolbar = NULL;
285 filer_window->toolbar_text = NULL;
288 if (o_toolbar.int_value != TOOLBAR_NONE)
290 filer_window->toolbar = create_toolbar(filer_window);
291 gtk_box_pack_start(filer_window->toplevel_vbox,
292 filer_window->toolbar, FALSE, TRUE, 0);
293 gtk_box_reorder_child(filer_window->toplevel_vbox,
294 filer_window->toolbar, 0);
295 gtk_widget_show_all(filer_window->toolbar);
298 filer_target_mode(filer_window, NULL, NULL, NULL);
299 toolbar_update_info(filer_window);
302 /****************************************************************
303 * INTERNAL FUNCTIONS *
304 ****************************************************************/
306 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window)
308 GdkEvent *event;
310 event = gtk_get_current_event();
311 if (event->type == GDK_BUTTON_RELEASE &&
312 ((GdkEventButton *) event)->button != 1)
313 menu_rox_help(NULL, HELP_MANUAL, NULL);
314 else
315 filer_opendir(make_path(app_dir, "Help"), NULL, NULL);
318 static void toolbar_refresh_clicked(GtkWidget *widget,
319 FilerWindow *filer_window)
321 GdkEvent *event;
323 event = gtk_get_current_event();
324 if (event->type == GDK_BUTTON_RELEASE &&
325 ((GdkEventButton *) event)->button != 1)
327 filer_opendir(filer_window->sym_path, filer_window, NULL);
329 else
331 full_refresh();
332 filer_update_dir(filer_window, TRUE);
336 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
338 GdkEvent *event;
340 event = gtk_get_current_event();
341 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
343 filer_opendir(home_dir, filer_window, NULL);
345 else
346 filer_change_to(filer_window, home_dir, NULL);
349 static void toolbar_bookmarks_clicked(GtkWidget *widget,
350 FilerWindow *filer_window)
352 GdkEvent *event;
354 g_return_if_fail(filer_window != NULL);
356 event = gtk_get_current_event();
357 if (event->type == GDK_BUTTON_PRESS &&
358 ((GdkEventButton *) event)->button == 1)
360 bookmarks_show_menu(filer_window);
362 else if (event->type == GDK_BUTTON_RELEASE &&
363 ((GdkEventButton *) event)->button != 1)
365 bookmarks_edit();
369 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window)
371 GdkEvent *event;
373 g_return_if_fail(filer_window != NULL);
375 event = gtk_get_current_event();
376 if (event->type == GDK_BUTTON_RELEASE &&
377 ((GdkEventButton *) event)->button != 1)
379 filer_opendir(filer_window->sym_path, filer_window, NULL);
381 else if (!filer_window_delete(filer_window->window, NULL, filer_window))
382 gtk_widget_destroy(filer_window->window);
385 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
387 GdkEvent *event;
389 event = gtk_get_current_event();
390 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
392 filer_open_parent(filer_window);
394 else
395 change_to_parent(filer_window);
398 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window)
400 GdkEventButton *bev;
402 bev = (GdkEventButton *) gtk_get_current_event();
404 display_change_size(filer_window,
405 bev->type == GDK_BUTTON_RELEASE && bev->button == 1);
408 static void toolbar_details_clicked(GtkWidget *widget,
409 FilerWindow *filer_window)
411 if (filer_window->view_type == VIEW_TYPE_DETAILS)
412 filer_set_view_type(filer_window, VIEW_TYPE_COLLECTION);
413 else
414 filer_set_view_type(filer_window, VIEW_TYPE_DETAILS);
417 static void toolbar_hidden_clicked(GtkWidget *widget,
418 FilerWindow *filer_window)
420 display_set_hidden(filer_window, !filer_window->show_hidden);
423 static GtkWidget *create_toolbar(FilerWindow *filer_window)
425 GtkWidget *bar;
426 GtkWidget *b;
427 int i;
429 bar = gtk_toolbar_new();
430 gtk_widget_set_size_request(bar, 100, -1);
432 if (o_toolbar.int_value == TOOLBAR_LARGE)
433 gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
434 else if (o_toolbar.int_value == TOOLBAR_HORIZONTAL)
435 gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH_HORIZ);
436 else
437 gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS);
439 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
441 Tool *tool = &all_tools[i];
443 if (!tool->enabled)
444 continue;
446 b = add_button(bar, tool, filer_window);
447 if (tool->drop_action != DROP_NONE)
448 handle_drops(filer_window, b, tool->drop_action);
451 filer_window->toolbar_text = gtk_label_new("");
452 gtk_misc_set_alignment(GTK_MISC(filer_window->toolbar_text), 0, 0.5);
453 gtk_toolbar_append_widget(GTK_TOOLBAR(bar),
454 filer_window->toolbar_text, NULL, NULL);
456 return bar;
459 /* This is used to simulate a click when button 3 is used (GtkButton
460 * normally ignores this).
462 static gint toolbar_other_button = 0;
463 static gint toolbar_button_pressed(GtkButton *button,
464 GdkEventButton *event,
465 FilerWindow *filer_window)
467 gint b = event->button;
468 Tool *tool;
470 tool = g_object_get_data(G_OBJECT(button), "rox-tool");
471 g_return_val_if_fail(tool != NULL, TRUE);
473 if (tool->menu && b == 1)
475 tool->clicked((GtkWidget *) button, filer_window);
476 return TRUE;
479 if ((b == 2 || b == 3) && toolbar_other_button == 0)
481 toolbar_other_button = event->button;
482 gtk_grab_add(GTK_WIDGET(button));
483 gtk_button_pressed(button);
485 return TRUE;
488 return FALSE;
491 static gint toolbar_button_released(GtkButton *button,
492 GdkEventButton *event,
493 FilerWindow *filer_window)
495 if (event->button == toolbar_other_button)
497 toolbar_other_button = 0;
498 gtk_grab_remove(GTK_WIDGET(button));
499 gtk_button_released(button);
501 return TRUE;
504 return FALSE;
507 static GtkWidget *add_button(GtkWidget *bar, Tool *tool,
508 FilerWindow *filer_window)
510 GtkWidget *button, *icon_widget;
512 icon_widget = gtk_image_new_from_stock(tool->name,
513 GTK_ICON_SIZE_LARGE_TOOLBAR);
515 button = gtk_toolbar_insert_element(GTK_TOOLBAR(bar),
516 GTK_TOOLBAR_CHILD_BUTTON,
517 NULL,
518 _(tool->label),
519 _(tool->tip), NULL,
520 icon_widget,
521 NULL, NULL, /* CB, userdata */
522 GTK_TOOLBAR(bar)->num_children);
524 if (o_toolbar.int_value == TOOLBAR_HORIZONTAL)
526 GtkWidget *hbox, *label;
527 GList *kids;
528 hbox = GTK_BIN(button)->child;
529 kids = gtk_container_get_children(GTK_CONTAINER(hbox));
530 label = g_list_nth_data(kids, 1);
531 g_list_free(kids);
533 if (label)
535 gtk_box_set_child_packing(GTK_BOX(hbox), label,
536 TRUE, TRUE, 0, GTK_PACK_END);
537 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
541 g_object_set_data(G_OBJECT(button), "rox-tool", tool);
543 g_signal_connect(button, "clicked",
544 G_CALLBACK(tool->clicked), filer_window);
545 g_signal_connect(button, "button_press_event",
546 G_CALLBACK(toolbar_button_pressed), filer_window);
547 g_signal_connect(button, "button_release_event",
548 G_CALLBACK(toolbar_button_released), filer_window);
550 return button;
553 static void toggle_shaded(GtkWidget *widget)
555 gtk_widget_set_sensitive(widget, !GTK_WIDGET_SENSITIVE(widget));
556 option_check_widget(&o_toolbar_disable);
559 /* Called during the drag when the mouse is in a widget registered
560 * as a drop target. Returns TRUE if we can accept the drop.
562 static gboolean drag_motion(GtkWidget *widget,
563 GdkDragContext *context,
564 gint x,
565 gint y,
566 guint time,
567 FilerWindow *filer_window)
569 GdkDragAction action = context->suggested_action;
570 DropDest dest;
572 dest = (DropDest) g_object_get_data(G_OBJECT(widget), "toolbar_dest");
574 if (dest == DROP_TO_HOME)
575 g_dataset_set_data(context, "drop_dest_path",
576 (gchar *) home_dir);
577 else
578 g_dataset_set_data_full(context, "drop_dest_path",
579 g_path_get_dirname(filer_window->sym_path),
580 g_free);
582 g_dataset_set_data(context, "drop_dest_type", (gpointer) drop_dest_dir);
583 gdk_drag_status(context, action, time);
585 dnd_spring_load(context, filer_window);
586 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NORMAL);
588 return TRUE;
591 static void drag_leave(GtkWidget *widget,
592 GdkDragContext *context,
593 guint32 time,
594 FilerWindow *filer_window)
596 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
597 dnd_spring_abort();
600 static void handle_drops(FilerWindow *filer_window,
601 GtkWidget *button,
602 DropDest dest)
604 make_drop_target(button, 0);
605 g_signal_connect(button, "drag_motion",
606 G_CALLBACK(drag_motion), filer_window);
607 g_signal_connect(button, "drag_leave",
608 G_CALLBACK(drag_leave), filer_window);
609 g_object_set_data(G_OBJECT(button), "toolbar_dest", (gpointer) dest);
612 static void tally_items(gpointer key, gpointer value, gpointer data)
614 guchar *leafname = (guchar *) key;
615 int *tally = (int *) data;
617 if (leafname[0] == '.')
618 (*tally)++;
621 static void option_notify(void)
623 int i;
624 gboolean changed = FALSE;
625 guchar *list = o_toolbar_disable.value;
627 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
629 Tool *tool = &all_tools[i];
630 gboolean old = tool->enabled;
632 tool->enabled = !in_list(tool->name, list);
634 if (old != tool->enabled)
635 changed = TRUE;
638 if (changed || o_toolbar.has_changed || o_toolbar_info.has_changed)
640 GList *next;
642 for (next = all_filer_windows; next; next = next->next)
644 FilerWindow *filer_window = (FilerWindow *) next->data;
646 toolbar_update_toolbar(filer_window);
651 static void update_tools(Option *option)
653 GList *next, *kids;
655 kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
657 for (next = kids; next; next = next->next)
659 GtkWidget *kid = (GtkWidget *) next->data;
660 guchar *name;
662 name = g_object_get_data(G_OBJECT(kid), "tool_name");
664 g_return_if_fail(name != NULL);
666 gtk_widget_set_sensitive(GTK_BIN(kid)->child,
667 !in_list(name, option->value));
670 g_list_free(kids);
673 static guchar *read_tools(Option *option)
675 GList *next, *kids;
676 GString *list;
677 guchar *retval;
679 list = g_string_new(NULL);
681 kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
683 for (next = kids; next; next = next->next)
685 GtkObject *kid = (GtkObject *) next->data;
686 guchar *name;
688 if (!GTK_WIDGET_SENSITIVE(GTK_BIN(kid)->child))
690 name = g_object_get_data(G_OBJECT(kid), "tool_name");
691 g_return_val_if_fail(name != NULL, list->str);
693 if (list->len)
694 g_string_append(list, ", ");
695 g_string_append(list, name);
699 g_list_free(kids);
700 retval = list->str;
701 g_string_free(list, FALSE);
703 return retval;
706 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label)
708 guint num_tools = G_N_ELEMENTS(all_tools);
709 guint rows = 3;
710 guint cols = (num_tools + rows - 1) / rows;
711 int i;
712 GtkWidget *hbox, *tool, *table;
714 g_return_val_if_fail(option != NULL, NULL);
716 hbox = gtk_hbox_new(FALSE, 0);
717 table = gtk_table_new(rows, cols, TRUE);
719 for (i = 0; (tool = toolbar_tool_option(i)) != NULL; i++)
721 guint left = i % cols;
722 guint top = i / cols;
724 gtk_table_attach_defaults(GTK_TABLE(table), tool,
725 left, left + 1, top, top + 1);
728 gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
730 option->update_widget = update_tools;
731 option->read_widget = read_tools;
732 option->widget = table;
734 return g_list_append(NULL, hbox);