r898: Applied Bernard Jungen's latest patch:
[rox-filer.git] / ROX-Filer / src / toolbar.c
blob5149ab296d663974ebc70106c5c4c7a493e20ffc
1 /*
2 * $Id$
4 * ROX-Filer, filer for the ROX desktop project
5 * Copyright (C) 2001, 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 "pixmaps.h"
38 #include "bind.h"
39 #include "type.h"
40 #include "dir.h"
42 typedef struct _Tool Tool;
44 typedef enum {DROP_NONE, DROP_TO_PARENT, DROP_TO_HOME} DropDest;
46 struct _Tool {
47 guchar *label;
48 guchar *name;
49 guchar *tip; /* Tooltip */
50 void (*clicked)(GtkWidget *w, FilerWindow *filer_window);
51 DropDest drop_action;
52 gboolean enabled;
53 MaskedPixmap *icon;
54 GtkWidget **menu; /* Right-click menu widget addr */
57 ToolbarType o_toolbar = TOOLBAR_NORMAL;
58 gint o_toolbar_info = TRUE;
60 static GtkTooltips *tooltips = NULL;
62 /* TRUE if the button presses (or released) should open a new window,
63 * rather than reusing the existing one.
65 #define NEW_WIN_BUTTON(button_event) \
66 (o_new_window_on_1 ? ((GdkEventButton *) button_event)->button == 1 \
67 : ((GdkEventButton *) button_event)->button != 1)
69 /* Static prototypes */
70 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window);
71 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window);
72 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window);
73 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window);
74 static void toolbar_refresh_clicked(GtkWidget *widget,
75 FilerWindow *filer_window);
76 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window);
77 static void toolbar_details_clicked(GtkWidget *widget,
78 FilerWindow *filer_window);
79 static void toolbar_hidden_clicked(GtkWidget *widget,
80 FilerWindow *filer_window);
81 static GtkWidget *add_button(GtkWidget *box, Tool *tool,
82 FilerWindow *filer_window);
83 static GtkWidget *create_toolbar(FilerWindow *filer_window);
84 static gboolean drag_motion(GtkWidget *widget,
85 GdkDragContext *context,
86 gint x,
87 gint y,
88 guint time,
89 FilerWindow *filer_window);
90 static void drag_leave(GtkWidget *widget,
91 GdkDragContext *context,
92 guint32 time,
93 FilerWindow *filer_window);
94 static void handle_drops(FilerWindow *filer_window,
95 GtkWidget *button,
96 DropDest dest);
97 static void coll_selection_changed(Collection *collection, guint time,
98 gpointer user_data);
99 static void recreate_toolbar(FilerWindow *filer_window);
100 static void toggle_shaded(GtkWidget *widget);
101 static void option_notify(void);
103 static Tool all_tools[] = {
104 {N_("Close"), "close", N_("Close filer window"),
105 toolbar_close_clicked, DROP_NONE, FALSE,
106 NULL, NULL},
108 {N_("Up"), "up", N_("Change to parent directory"),
109 toolbar_up_clicked, DROP_TO_PARENT, TRUE,
110 NULL, NULL},
112 {N_("Home"), "home", N_("Change to home directory"),
113 toolbar_home_clicked, DROP_TO_HOME, TRUE,
114 NULL, NULL},
116 {N_("Scan"), "refresh", N_("Rescan directory contents"),
117 toolbar_refresh_clicked, DROP_NONE, TRUE,
118 NULL, NULL},
120 {N_("Size"), "zoom", N_("Change icon size"),
121 toolbar_size_clicked, DROP_NONE, TRUE,
122 NULL, NULL},
124 {N_("Details"), "details", N_("Show extra details"),
125 toolbar_details_clicked, DROP_NONE, TRUE,
126 NULL, NULL},
128 {N_("Hidden"), "hidden", N_("Show/hide hidden files"),
129 toolbar_hidden_clicked, DROP_NONE, TRUE,
130 NULL, NULL},
132 {N_("Help"), "help", N_("Show ROX-Filer help"),
133 toolbar_help_clicked, DROP_NONE, TRUE,
134 NULL, NULL},
138 /****************************************************************
139 * EXTERNAL INTERFACE *
140 ****************************************************************/
142 void toolbar_init(void)
144 int i;
146 option_add_int("toolbar_type", o_toolbar, NULL);
147 option_add_int("toolbar_show_info", o_toolbar_info, NULL);
148 option_add_string("toolbar_disable", "close", NULL);
149 option_add_notify(option_notify);
151 tooltips = gtk_tooltips_new();
153 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
155 Tool *tool = &all_tools[i];
157 if (!tool->icon)
159 guchar *path;
161 path = g_strconcat("pixmaps/",
162 tool->name, ".xpm", NULL);
163 tool->icon = load_pixmap(path);
164 g_free(path);
169 /* Returns a button which can be used to turn a tool on and off.
170 * Button has 'tool_name' set to the tool's name.
171 * NULL if tool number i is too big.
173 GtkWidget *toolbar_tool_option(int i)
175 Tool *tool = &all_tools[i];
176 GtkWidget *button;
177 GtkWidget *icon_widget, *vbox, *text;
179 g_return_val_if_fail(i >= 0, NULL);
181 if (i >= sizeof(all_tools) / sizeof(*all_tools))
182 return NULL;
184 button = gtk_button_new();
185 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
186 gtk_tooltips_set_tip(tooltips, button, _(tool->tip), NULL);
188 icon_widget = gtk_pixmap_new(tool->icon->pixmap,
189 tool->icon->mask);
191 vbox = gtk_vbox_new(FALSE, 0);
192 gtk_box_pack_start(GTK_BOX(vbox), icon_widget, TRUE, TRUE, 0);
194 text = gtk_label_new(_(tool->label));
195 gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, TRUE, 0);
197 gtk_container_add(GTK_CONTAINER(button), vbox);
199 gtk_container_set_border_width(GTK_CONTAINER(button), 1);
200 gtk_misc_set_padding(GTK_MISC(icon_widget), 16, 1);
202 gtk_signal_connect_object(GTK_OBJECT(button), "clicked",
203 GTK_SIGNAL_FUNC(toggle_shaded), GTK_OBJECT(vbox));
205 gtk_object_set_data(GTK_OBJECT(button), "tool_name", tool->name);
207 return button;
210 /* Create a new toolbar widget, suitable for adding to a filer window,
211 * and return it.
213 GtkWidget *toolbar_new(FilerWindow *filer_window)
215 g_return_val_if_fail(filer_window != NULL, NULL);
216 g_return_val_if_fail(o_toolbar != TOOLBAR_NONE, NULL);
218 return create_toolbar(filer_window);
221 void toolbar_update_info(FilerWindow *filer_window)
223 if (o_toolbar != TOOLBAR_NONE && o_toolbar_info)
224 coll_selection_changed(filer_window->collection,
225 GDK_CURRENT_TIME, filer_window);
229 /****************************************************************
230 * INTERNAL FUNCTIONS *
231 ****************************************************************/
233 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window)
235 filer_opendir(make_path(app_dir, "Help")->str, NULL);
238 static void toolbar_refresh_clicked(GtkWidget *widget,
239 FilerWindow *filer_window)
241 GdkEvent *event;
243 event = gtk_get_current_event();
244 if (event->type == GDK_BUTTON_RELEASE &&
245 ((GdkEventButton *) event)->button != 1)
247 filer_opendir(filer_window->path, filer_window);
249 else
251 full_refresh();
252 filer_update_dir(filer_window, TRUE);
256 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
258 GdkEvent *event;
260 event = gtk_get_current_event();
261 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
263 filer_opendir(home_dir, filer_window);
265 else
266 filer_change_to(filer_window, home_dir, NULL);
269 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window)
271 GdkEvent *event;
273 g_return_if_fail(filer_window != NULL);
275 event = gtk_get_current_event();
276 if (event->type == GDK_BUTTON_RELEASE &&
277 ((GdkEventButton *) event)->button != 1)
279 filer_opendir(filer_window->path, filer_window);
281 else
282 gtk_widget_destroy(filer_window->window);
285 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
287 GdkEvent *event;
289 event = gtk_get_current_event();
290 if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
292 filer_open_parent(filer_window);
294 else
295 change_to_parent(filer_window);
298 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window)
300 GdkEventButton *bev;
302 bev = (GdkEventButton *) gtk_get_current_event();
304 display_change_size(filer_window,
305 bev->type == GDK_BUTTON_RELEASE && bev->button == 1);
308 static void toolbar_details_clicked(GtkWidget *widget,
309 FilerWindow *filer_window)
311 if (filer_window->details_type == DETAILS_NONE)
312 display_set_layout(filer_window, SMALL_ICONS, DETAILS_SUMMARY);
313 else
314 display_set_layout(filer_window,
315 option_get_int("display_size"),
316 DETAILS_NONE);
319 static void toolbar_hidden_clicked(GtkWidget *widget,
320 FilerWindow *filer_window)
322 display_set_hidden(filer_window, !filer_window->show_hidden);
325 static GtkWidget *create_toolbar(FilerWindow *filer_window)
327 GtkWidget *box;
328 GtkWidget *b;
329 int i;
331 box = gtk_hbox_new(FALSE, 0);
333 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
335 Tool *tool = &all_tools[i];
337 if (!tool->enabled)
338 continue;
340 b = add_button(box, tool, filer_window);
341 if (tool->drop_action != DROP_NONE)
342 handle_drops(filer_window, b, tool->drop_action);
345 filer_window->toolbar_text = gtk_label_new("");
346 gtk_misc_set_alignment(GTK_MISC(filer_window->toolbar_text), 0, 0.5);
347 gtk_widget_set_usize(filer_window->toolbar_text, 8, 1);
348 gtk_box_pack_start(GTK_BOX(box), filer_window->toolbar_text,
349 TRUE, TRUE, 4);
351 if (o_toolbar_info)
352 gtk_signal_connect(GTK_OBJECT(filer_window->collection),
353 "selection_changed",
354 GTK_SIGNAL_FUNC(coll_selection_changed),
355 (gpointer) filer_window);
357 return box;
360 /* This is used to simulate a click when button 3 is used (GtkButton
361 * normally ignores this).
363 static gint toolbar_other_button = 0;
364 static gint toolbar_adjust_pressed(GtkButton *button,
365 GdkEventButton *event,
366 FilerWindow *filer_window)
368 gint b = event->button;
370 if ((b == 2 || b == 3) && toolbar_other_button == 0)
372 toolbar_other_button = event->button;
373 gtk_grab_add(GTK_WIDGET(button));
374 gtk_button_pressed(button);
376 return TRUE;
379 return FALSE;
382 static gint toolbar_adjust_released(GtkButton *button,
383 GdkEventButton *event,
384 FilerWindow *filer_window)
386 if (event->button == toolbar_other_button)
388 toolbar_other_button = 0;
389 gtk_grab_remove(GTK_WIDGET(button));
390 gtk_button_released(button);
392 return TRUE;
395 return FALSE;
398 #if 0
399 static gint menu_pressed(GtkWidget *button,
400 GdkEventButton *event,
401 FilerWindow *filer_window)
403 GtkWidget *menu;
405 if (event->button != 3 && event->button != 2)
406 return FALSE;
408 menu = gtk_object_get_data(GTK_OBJECT(button), "popup_menu");
409 g_return_val_if_fail(menu != NULL, TRUE);
411 show_style_menu(filer_window, event, menu);
413 return TRUE;
415 #endif
417 static GtkWidget *add_button(GtkWidget *box, Tool *tool,
418 FilerWindow *filer_window)
420 GtkWidget *button, *icon_widget;
421 GtkSignalFunc cb = GTK_SIGNAL_FUNC(tool->clicked);
423 button = gtk_button_new();
424 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
425 GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
427 if (tool->menu)
429 g_warning("Not implemented");
430 #if 0
431 gtk_object_set_data(GTK_OBJECT(button), "popup_menu",
432 *tool->menu);
433 gtk_signal_connect(GTK_OBJECT(button), "button_press_event",
434 GTK_SIGNAL_FUNC(menu_pressed), filer_window);
435 #endif
437 else
439 gtk_signal_connect(GTK_OBJECT(button), "button_press_event",
440 GTK_SIGNAL_FUNC(toolbar_adjust_pressed), filer_window);
441 gtk_signal_connect(GTK_OBJECT(button), "button_release_event",
442 GTK_SIGNAL_FUNC(toolbar_adjust_released), filer_window);
445 gtk_signal_connect(GTK_OBJECT(button), "clicked",
446 cb, filer_window);
448 gtk_tooltips_set_tip(tooltips, button, _(tool->tip), NULL);
450 icon_widget = gtk_pixmap_new(tool->icon->pixmap, tool->icon->mask);
452 if (o_toolbar == TOOLBAR_LARGE)
454 GtkWidget *vbox, *text;
456 vbox = gtk_vbox_new(FALSE, 0);
457 gtk_box_pack_start(GTK_BOX(vbox), icon_widget, TRUE, TRUE, 0);
459 text = gtk_label_new(_(tool->label));
460 gtk_box_pack_start(GTK_BOX(vbox), text, FALSE, TRUE, 0);
462 gtk_container_add(GTK_CONTAINER(button), vbox);
464 else
465 gtk_container_add(GTK_CONTAINER(button), icon_widget);
467 gtk_container_set_border_width(GTK_CONTAINER(button), 1);
468 gtk_misc_set_padding(GTK_MISC(icon_widget),
469 o_toolbar == TOOLBAR_LARGE ? 16 : 8, 1);
470 gtk_box_pack_start(GTK_BOX(box), button, FALSE, TRUE, 0);
472 return button;
475 static void toggle_shaded(GtkWidget *widget)
477 gtk_widget_set_sensitive(widget, !GTK_WIDGET_SENSITIVE(widget));
480 /* Called during the drag when the mouse is in a widget registered
481 * as a drop target. Returns TRUE if we can accept the drop.
483 static gboolean drag_motion(GtkWidget *widget,
484 GdkDragContext *context,
485 gint x,
486 gint y,
487 guint time,
488 FilerWindow *filer_window)
490 GdkDragAction action = context->suggested_action;
491 DropDest dest;
493 dest = (DropDest) gtk_object_get_data(GTK_OBJECT(widget),
494 "toolbar_dest");
496 if (dest == DROP_TO_HOME)
497 g_dataset_set_data(context, "drop_dest_path", home_dir);
498 else
500 guchar *slash, *path;
502 slash = strrchr(filer_window->path, '/');
503 if (slash == NULL || slash == filer_window->path)
504 path = g_strdup("/");
505 else
506 path = g_strndup(filer_window->path,
507 slash - filer_window->path);
508 g_dataset_set_data_full(context, "drop_dest_path",
509 path, g_free);
512 g_dataset_set_data(context, "drop_dest_type", drop_dest_dir);
513 gdk_drag_status(context, action, time);
515 dnd_spring_load(context, filer_window);
516 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NORMAL);
518 return TRUE;
521 static void drag_leave(GtkWidget *widget,
522 GdkDragContext *context,
523 guint32 time,
524 FilerWindow *filer_window)
526 gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
527 dnd_spring_abort();
530 static void handle_drops(FilerWindow *filer_window,
531 GtkWidget *button,
532 DropDest dest)
534 make_drop_target(button, 0);
535 gtk_signal_connect(GTK_OBJECT(button), "drag_motion",
536 GTK_SIGNAL_FUNC(drag_motion), filer_window);
537 gtk_signal_connect(GTK_OBJECT(button), "drag_leave",
538 GTK_SIGNAL_FUNC(drag_leave), filer_window);
539 gtk_object_set_data(GTK_OBJECT(button), "toolbar_dest",
540 (gpointer) dest);
543 static void tally_items(gpointer key, gpointer value, gpointer data)
545 guchar *leafname = (guchar *) key;
546 int *tally = (int *) data;
548 if (leafname[0] == '.')
549 (*tally)++;
552 static void coll_selection_changed(Collection *collection, guint time,
553 gpointer user_data)
555 FilerWindow *filer_window = (FilerWindow *) user_data;
556 gchar *label;
558 if (filer_window->target_cb)
559 return;
561 if (collection->number_selected == 0)
563 gchar *s = NULL;
565 if (filer_window->scanning)
567 gtk_label_set_text(
568 GTK_LABEL(filer_window->toolbar_text), "");
569 return;
572 if (!filer_window->show_hidden)
574 GHashTable *hash = filer_window->directory->known_items;
575 int tally = 0;
577 g_hash_table_foreach(hash, tally_items, &tally);
579 if (tally)
580 s = g_strdup_printf(_(" (%u hidden)"), tally);
583 if (collection->number_of_items)
584 label = g_strdup_printf("%d %s%s",
585 collection->number_of_items,
586 collection->number_of_items != 1
587 ? _("items") : _("item"),
588 s ? s : "");
589 else /* (French plurals work differently for zero) */
590 label = g_strdup_printf(_("No items%s"),
591 s ? s : "");
592 g_free(s);
594 else
596 gint i = collection->number_of_items;
597 double size = 0;
599 while (i--)
601 DirItem *item = (DirItem *) collection->items[i].data;
603 if (collection->items[i].selected
604 && item->base_type != TYPE_DIRECTORY)
605 size += (double) item->size;
607 label = g_strdup_printf(_("%u selected (%s)"),
608 collection->number_selected,
609 format_double_size(size));
612 gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), label);
613 g_free(label);
616 static void recreate_toolbar(FilerWindow *filer_window)
618 GtkWidget *frame = filer_window->toolbar_frame;
620 if (GTK_BIN(frame)->child)
622 filer_window->toolbar_text = NULL;
623 gtk_widget_destroy(((GtkBin *) frame)->child);
626 if (o_toolbar == TOOLBAR_NONE)
627 gtk_widget_hide(frame);
628 else
630 GtkWidget *toolbar;
632 toolbar = toolbar_new(filer_window);
633 gtk_container_add(GTK_CONTAINER(filer_window->toolbar_frame),
634 toolbar);
635 gtk_widget_show_all(frame);
638 filer_target_mode(filer_window, NULL, NULL, NULL);
639 toolbar_update_info(filer_window);
642 static void option_notify(void)
644 ToolbarType old_type = o_toolbar;
645 gint old_info = o_toolbar_info;
646 guchar *list;
647 int i;
648 gboolean changed = FALSE;
650 list = option_get_static_string("toolbar_disable");
652 for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
654 Tool *tool = &all_tools[i];
655 gboolean old = tool->enabled;
657 tool->enabled = !in_list(tool->name, list);
659 if (old != tool->enabled)
660 changed = TRUE;
663 o_toolbar = option_get_int("toolbar_type");
664 o_toolbar_info = option_get_int("toolbar_show_info");
666 if (changed || old_type != o_toolbar || old_info != o_toolbar_info)
668 GList *next;
670 for (next = all_filer_windows; next; next = next->next)
672 FilerWindow *filer_window = (FilerWindow *) next->data;
674 if (old_info && old_type != TOOLBAR_NONE)
675 gtk_signal_disconnect_by_func(
676 GTK_OBJECT(filer_window->collection),
677 GTK_SIGNAL_FUNC(coll_selection_changed),
678 (gpointer) filer_window);
680 recreate_toolbar(filer_window);