r1824: Internal changes: moved menu and toolbar over to View interface.
[rox-filer.git] / ROX-Filer / src / toolbar.c
blob6976c592af423679c8a4c6648a22a7437492f2d3
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"
45 typedef struct _Tool Tool;
47 typedef enum {DROP_NONE, DROP_TO_PARENT, DROP_TO_HOME} DropDest;
49 struct _Tool {
50 const gchar *label;
51 const gchar *name;
52 const gchar *tip; /* Tooltip */
53 void (*clicked)(GtkWidget *w, FilerWindow *filer_window);
54 DropDest drop_action;
55 gboolean enabled;
56 GtkWidget **menu; /* Right-click menu widget addr */
59 Option o_toolbar, o_toolbar_info, o_toolbar_disable;
61 static GtkTooltips *tooltips = NULL;
63 /* TRUE if the button presses (or released) should open a new window,
64 * rather than reusing the existing one.
66 #define NEW_WIN_BUTTON(button_event) \
67 (o_new_button_1.int_value \
68 ? ((GdkEventButton *) button_event)->button == 1 \
69 : ((GdkEventButton *) button_event)->button != 1)
71 /* Static prototypes */
72 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window);
73 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
74 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
75 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window);
76 static void toolbar_refresh_clicked(GtkWidget *widget,
77 FilerWindow *filer_window);
78 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window);
79 static void toolbar_details_clicked(GtkWidget *widget,
80 FilerWindow *filer_window);
81 static void toolbar_hidden_clicked(GtkWidget *widget,
82 FilerWindow *filer_window);
83 static GtkWidget *add_button(GtkWidget *bar, Tool *tool,
84 FilerWindow *filer_window);
85 static GtkWidget *create_toolbar(FilerWindow *filer_window);
86 static gboolean drag_motion(GtkWidget *widget,
87 GdkDragContext *context,
88 gint x,
89 gint y,
90 guint time,
91 FilerWindow *filer_window);
92 static void drag_leave(GtkWidget *widget,
93 GdkDragContext *context,
94 guint32 time,
95 FilerWindow *filer_window);
96 static void handle_drops(FilerWindow *filer_window,
97 GtkWidget *button,
98 DropDest dest);
99 static void toggle_shaded(GtkWidget *widget);
100 static void option_notify(void);
101 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label);
102 static void tally_items(gpointer key, gpointer value, gpointer data);
104 static Tool all_tools[] = {
105 {N_("Close"), GTK_STOCK_CLOSE, N_("Close filer window"),
106 toolbar_close_clicked, DROP_NONE, FALSE,
107 NULL},
109 {N_("Up"), GTK_STOCK_GO_UP, N_("Change to parent directory"),
110 toolbar_up_clicked, DROP_TO_PARENT, TRUE,
111 NULL},
113 {N_("Home"), GTK_STOCK_HOME, N_("Change to home directory"),
114 toolbar_home_clicked, DROP_TO_HOME, TRUE,
115 NULL},
117 {N_("Scan"), GTK_STOCK_REFRESH, N_("Rescan directory contents"),
118 toolbar_refresh_clicked, DROP_NONE, TRUE,
119 NULL},
121 {N_("Size"), GTK_STOCK_ZOOM_IN, N_("Change icon size"),
122 toolbar_size_clicked, DROP_NONE, TRUE,
123 NULL},
125 {N_("Details"), GTK_STOCK_JUSTIFY_LEFT, N_("Show extra details"),
126 toolbar_details_clicked, DROP_NONE, TRUE,
127 NULL},
129 {N_("Hidden"), GTK_STOCK_STRIKETHROUGH, N_("Show/hide hidden files"),
130 toolbar_hidden_clicked, DROP_NONE, TRUE,
131 NULL},
133 {N_("Help"), GTK_STOCK_HELP, N_("Show ROX-Filer help"),
134 toolbar_help_clicked, DROP_NONE, TRUE,
135 NULL},
139 /****************************************************************
140 * EXTERNAL INTERFACE *
141 ****************************************************************/
143 void toolbar_init(void)
145 option_add_int(&o_toolbar, "toolbar_type", TOOLBAR_NORMAL);
146 option_add_int(&o_toolbar_info, "toolbar_show_info", 1);
147 option_add_string(&o_toolbar_disable, "toolbar_disable",
148 GTK_STOCK_CLOSE);
149 option_add_notify(option_notify);
151 tooltips = gtk_tooltips_new();
153 option_register_widget("tool-options", build_tool_options);
156 /* Returns a button which can be used to turn a tool on and off.
157 * Button has 'tool_name' set to the tool's name.
158 * NULL if tool number i is too big.
160 GtkWidget *toolbar_tool_option(int i)
162 Tool *tool = &all_tools[i];
163 GtkWidget *button;
164 GtkWidget *icon_widget, *vbox, *text;
166 g_return_val_if_fail(i >= 0, NULL);
168 if (i >= sizeof(all_tools) / sizeof(*all_tools))
169 return NULL;
171 button = gtk_button_new();
172 gtk_tooltips_set_tip(tooltips, button, _(tool->tip), NULL);
174 icon_widget = gtk_image_new_from_stock(tool->name,
175 GTK_ICON_SIZE_LARGE_TOOLBAR);
177 vbox = gtk_vbox_new(FALSE, 0);
178 gtk_box_pack_start(GTK_BOX(vbox), icon_widget, TRUE, TRUE, 0);
180 text = gtk_label_new(_(tool->label));
181 gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, TRUE, 0);
183 gtk_container_add(GTK_CONTAINER(button), vbox);
185 gtk_container_set_border_width(GTK_CONTAINER(button), 1);
187 g_signal_connect_swapped(button, "clicked",
188 G_CALLBACK(toggle_shaded), vbox);
190 g_object_set_data(G_OBJECT(button), "tool_name",
191 (gchar *) tool->name);
193 return button;
196 void toolbar_update_info(FilerWindow *filer_window)
198 gchar *label;
199 ViewIface *view;
200 int n_selected;
202 if (o_toolbar.int_value == TOOLBAR_NONE || !o_toolbar_info.int_value)
203 return; /* Not showing info */
205 if (filer_window->target_cb)
206 return;
208 view = filer_window->view;
210 n_selected = view_count_selected(view);
212 if (n_selected == 0)
214 gchar *s = NULL;
215 int n_items;
217 if (filer_window->scanning)
219 gtk_label_set_text(
220 GTK_LABEL(filer_window->toolbar_text), "");
221 return;
224 if (!filer_window->show_hidden)
226 GHashTable *hash = filer_window->directory->known_items;
227 int tally = 0;
229 g_hash_table_foreach(hash, tally_items, &tally);
231 if (tally)
232 s = g_strdup_printf(_(" (%u hidden)"), tally);
235 n_items = view_count_items(view);
237 if (n_items)
238 label = g_strdup_printf("%d %s%s",
239 n_items,
240 n_items != 1 ? _("items") : _("item"),
241 s ? s : "");
242 else /* (French plurals work differently for zero) */
243 label = g_strdup_printf(_("No items%s"),
244 s ? s : "");
245 g_free(s);
247 else
249 double size = 0;
250 ViewIter iter;
251 DirItem *item;
253 view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
255 while ((item = iter.next(&iter)))
256 if (item->base_type != TYPE_DIRECTORY)
257 size += (double) item->size;
259 label = g_strdup_printf(_("%u selected (%s)"),
260 n_selected, format_double_size(size));
263 gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), label);
264 g_free(label);
267 /* Create, destroy or recreate toolbar for this window so that it
268 * matches the option setting.
270 void toolbar_update_toolbar(FilerWindow *filer_window)
272 g_return_if_fail(filer_window != NULL);
274 if (filer_window->toolbar)
276 gtk_widget_destroy(filer_window->toolbar);
277 filer_window->toolbar = NULL;
278 filer_window->toolbar_text = NULL;
281 if (o_toolbar.int_value != TOOLBAR_NONE)
283 filer_window->toolbar = create_toolbar(filer_window);
284 gtk_box_pack_start(filer_window->toplevel_vbox,
285 filer_window->toolbar, FALSE, TRUE, 0);
286 gtk_box_reorder_child(filer_window->toplevel_vbox,
287 filer_window->toolbar, 0);
288 gtk_widget_show_all(filer_window->toolbar);
291 filer_target_mode(filer_window, NULL, NULL, NULL);
292 toolbar_update_info(filer_window);
295 /****************************************************************
296 * INTERNAL FUNCTIONS *
297 ****************************************************************/
299 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window)
301 GdkEvent *event;
303 event = gtk_get_current_event();
304 if (event->type == GDK_BUTTON_RELEASE &&
305 ((GdkEventButton *) event)->button != 1)
306 filer_change_to(filer_window,
307 make_path(app_dir, "Help")->str, NULL);
308 else
309 filer_opendir(make_path(app_dir, "Help")->str, NULL);
312 static void toolbar_refresh_clicked(GtkWidget *widget,
313 FilerWindow *filer_window)
315 GdkEvent *event;
317 event = gtk_get_current_event();
318 if (event->type == GDK_BUTTON_RELEASE &&
319 ((GdkEventButton *) event)->button != 1)
321 filer_opendir(filer_window->sym_path, filer_window);
323 else
325 full_refresh();
326 filer_update_dir(filer_window, TRUE);
330 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
332 GdkEvent *event;
334 event = gtk_get_current_event();
335 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
337 filer_opendir(home_dir, filer_window);
339 else
340 filer_change_to(filer_window, home_dir, NULL);
343 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window)
345 GdkEvent *event;
347 g_return_if_fail(filer_window != NULL);
349 event = gtk_get_current_event();
350 if (event->type == GDK_BUTTON_RELEASE &&
351 ((GdkEventButton *) event)->button != 1)
353 filer_opendir(filer_window->sym_path, filer_window);
355 else
356 gtk_widget_destroy(filer_window->window);
359 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
361 GdkEvent *event;
363 event = gtk_get_current_event();
364 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
366 filer_open_parent(filer_window);
368 else
369 change_to_parent(filer_window);
372 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window)
374 GdkEventButton *bev;
376 bev = (GdkEventButton *) gtk_get_current_event();
378 display_change_size(filer_window,
379 bev->type == GDK_BUTTON_RELEASE && bev->button == 1);
382 static void toolbar_details_clicked(GtkWidget *widget,
383 FilerWindow *filer_window)
385 if (filer_window->details_type == DETAILS_NONE)
386 display_set_layout(filer_window,
387 filer_window->display_style,
388 DETAILS_SUMMARY);
389 else
390 display_set_layout(filer_window,
391 filer_window->display_style,
392 DETAILS_NONE);
395 static void toolbar_hidden_clicked(GtkWidget *widget,
396 FilerWindow *filer_window)
398 display_set_hidden(filer_window, !filer_window->show_hidden);
401 static GtkWidget *create_toolbar(FilerWindow *filer_window)
403 GtkWidget *bar;
404 GtkWidget *b;
405 int i;
407 bar = gtk_toolbar_new();
408 gtk_widget_set_size_request(bar, 100, -1);
410 if (o_toolbar.int_value == TOOLBAR_LARGE)
411 gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
412 else if (o_toolbar.int_value == TOOLBAR_HORIZONTAL)
413 gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH_HORIZ);
414 else
415 gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS);
417 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
419 Tool *tool = &all_tools[i];
421 if (!tool->enabled)
422 continue;
424 b = add_button(bar, tool, filer_window);
425 if (tool->drop_action != DROP_NONE)
426 handle_drops(filer_window, b, tool->drop_action);
429 filer_window->toolbar_text = gtk_label_new("");
430 gtk_misc_set_alignment(GTK_MISC(filer_window->toolbar_text), 0, 0.5);
431 gtk_toolbar_append_widget(GTK_TOOLBAR(bar),
432 filer_window->toolbar_text, NULL, NULL);
434 return bar;
437 /* This is used to simulate a click when button 3 is used (GtkButton
438 * normally ignores this).
440 static gint toolbar_other_button = 0;
441 static gint toolbar_adjust_pressed(GtkButton *button,
442 GdkEventButton *event,
443 FilerWindow *filer_window)
445 gint b = event->button;
447 if ((b == 2 || b == 3) && toolbar_other_button == 0)
449 toolbar_other_button = event->button;
450 gtk_grab_add(GTK_WIDGET(button));
451 gtk_button_pressed(button);
453 return TRUE;
456 return FALSE;
459 static gint toolbar_adjust_released(GtkButton *button,
460 GdkEventButton *event,
461 FilerWindow *filer_window)
463 if (event->button == toolbar_other_button)
465 toolbar_other_button = 0;
466 gtk_grab_remove(GTK_WIDGET(button));
467 gtk_button_released(button);
469 return TRUE;
472 return FALSE;
475 static GtkWidget *add_button(GtkWidget *bar, Tool *tool,
476 FilerWindow *filer_window)
478 GtkWidget *button, *icon_widget;
480 icon_widget = gtk_image_new_from_stock(tool->name,
481 GTK_ICON_SIZE_LARGE_TOOLBAR);
483 button = gtk_toolbar_insert_element(GTK_TOOLBAR(bar),
484 GTK_TOOLBAR_CHILD_BUTTON,
485 NULL,
486 _(tool->label),
487 _(tool->tip), NULL,
488 icon_widget,
489 NULL, NULL, /* CB, userdata */
490 GTK_TOOLBAR(bar)->num_children);
492 if (o_toolbar.int_value == TOOLBAR_HORIZONTAL)
494 GtkWidget *hbox, *label;
495 GList *kids;
496 hbox = GTK_BIN(button)->child;
497 kids = gtk_container_get_children(GTK_CONTAINER(hbox));
498 label = g_list_nth_data(kids, 1);
499 g_list_free(kids);
501 if (label)
503 gtk_box_set_child_packing(GTK_BOX(hbox), label,
504 TRUE, TRUE, 0, GTK_PACK_END);
505 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
509 g_signal_connect(button, "clicked",
510 G_CALLBACK(tool->clicked), filer_window);
512 g_signal_connect(button, "button_press_event",
513 G_CALLBACK(toolbar_adjust_pressed), filer_window);
514 g_signal_connect(button, "button_release_event",
515 G_CALLBACK(toolbar_adjust_released), filer_window);
517 return button;
520 static void toggle_shaded(GtkWidget *widget)
522 gtk_widget_set_sensitive(widget, !GTK_WIDGET_SENSITIVE(widget));
523 option_check_widget(&o_toolbar_disable);
526 /* Called during the drag when the mouse is in a widget registered
527 * as a drop target. Returns TRUE if we can accept the drop.
529 static gboolean drag_motion(GtkWidget *widget,
530 GdkDragContext *context,
531 gint x,
532 gint y,
533 guint time,
534 FilerWindow *filer_window)
536 GdkDragAction action = context->suggested_action;
537 DropDest dest;
539 dest = (DropDest) g_object_get_data(G_OBJECT(widget), "toolbar_dest");
541 if (dest == DROP_TO_HOME)
542 g_dataset_set_data(context, "drop_dest_path",
543 (gchar *) home_dir);
544 else
545 g_dataset_set_data_full(context, "drop_dest_path",
546 g_dirname(filer_window->sym_path), g_free);
548 g_dataset_set_data(context, "drop_dest_type", (gpointer) drop_dest_dir);
549 gdk_drag_status(context, action, time);
551 dnd_spring_load(context, filer_window);
552 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NORMAL);
554 return TRUE;
557 static void drag_leave(GtkWidget *widget,
558 GdkDragContext *context,
559 guint32 time,
560 FilerWindow *filer_window)
562 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
563 dnd_spring_abort();
566 static void handle_drops(FilerWindow *filer_window,
567 GtkWidget *button,
568 DropDest dest)
570 make_drop_target(button, 0);
571 g_signal_connect(button, "drag_motion",
572 G_CALLBACK(drag_motion), filer_window);
573 g_signal_connect(button, "drag_leave",
574 G_CALLBACK(drag_leave), filer_window);
575 g_object_set_data(G_OBJECT(button), "toolbar_dest", (gpointer) dest);
578 static void tally_items(gpointer key, gpointer value, gpointer data)
580 guchar *leafname = (guchar *) key;
581 int *tally = (int *) data;
583 if (leafname[0] == '.')
584 (*tally)++;
587 static void option_notify(void)
589 int i;
590 gboolean changed = FALSE;
591 guchar *list = o_toolbar_disable.value;
593 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
595 Tool *tool = &all_tools[i];
596 gboolean old = tool->enabled;
598 tool->enabled = !in_list(tool->name, list);
600 if (old != tool->enabled)
601 changed = TRUE;
604 if (changed || o_toolbar.has_changed || o_toolbar_info.has_changed)
606 GList *next;
608 for (next = all_filer_windows; next; next = next->next)
610 FilerWindow *filer_window = (FilerWindow *) next->data;
612 toolbar_update_toolbar(filer_window);
617 static void update_tools(Option *option)
619 GList *next, *kids;
621 kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
623 for (next = kids; next; next = next->next)
625 GtkWidget *kid = (GtkWidget *) next->data;
626 guchar *name;
628 name = g_object_get_data(G_OBJECT(kid), "tool_name");
630 g_return_if_fail(name != NULL);
632 gtk_widget_set_sensitive(GTK_BIN(kid)->child,
633 !in_list(name, option->value));
636 g_list_free(kids);
639 static guchar *read_tools(Option *option)
641 GList *next, *kids;
642 GString *list;
643 guchar *retval;
645 list = g_string_new(NULL);
647 kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
649 for (next = kids; next; next = next->next)
651 GtkObject *kid = (GtkObject *) next->data;
652 guchar *name;
654 if (!GTK_WIDGET_SENSITIVE(GTK_BIN(kid)->child))
656 name = g_object_get_data(G_OBJECT(kid), "tool_name");
657 g_return_val_if_fail(name != NULL, list->str);
659 if (list->len)
660 g_string_append(list, ", ");
661 g_string_append(list, name);
665 g_list_free(kids);
666 retval = list->str;
667 g_string_free(list, FALSE);
669 return retval;
672 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label)
674 guint num_tools = G_N_ELEMENTS(all_tools);
675 guint rows = 2;
676 guint cols = (num_tools + rows - 1) / rows;
677 int i;
678 GtkWidget *hbox, *tool, *table;
680 g_return_val_if_fail(option != NULL, NULL);
682 hbox = gtk_hbox_new(FALSE, 0);
683 table = gtk_table_new(rows, cols, TRUE);
685 for (i = 0; (tool = toolbar_tool_option(i)) != NULL; i++)
687 guint left = i % cols;
688 guint top = i / cols;
690 gtk_table_attach_defaults(GTK_TABLE(table), tool,
691 left, left + 1, top, top + 1);
694 gtk_box_pack_start(GTK_BOX(hbox), table, FALSE, FALSE, 0);
696 option->update_widget = update_tools;
697 option->read_widget = read_tools;
698 option->widget = table;
700 return g_list_append(NULL, hbox);