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)
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
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 */
30 #include "collection.h"
45 typedef struct _Tool Tool
;
47 typedef enum {DROP_NONE
, DROP_TO_PARENT
, DROP_TO_HOME
} DropDest
;
52 guchar
*tip
; /* Tooltip */
53 void (*clicked
)(GtkWidget
*w
, FilerWindow
*filer_window
);
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
,
92 FilerWindow
*filer_window
);
93 static void drag_leave(GtkWidget
*widget
,
94 GdkDragContext
*context
,
96 FilerWindow
*filer_window
);
97 static void handle_drops(FilerWindow
*filer_window
,
100 static void coll_selection_changed(Collection
*collection
, guint time
,
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
,
112 {N_("Up"), "up", N_("Change to parent directory"),
113 toolbar_up_clicked
, DROP_TO_PARENT
, TRUE
,
116 {N_("Home"), "home", N_("Change to home directory"),
117 toolbar_home_clicked
, DROP_TO_HOME
, TRUE
,
120 {N_("Scan"), "refresh", N_("Rescan directory contents"),
121 toolbar_refresh_clicked
, DROP_NONE
, TRUE
,
124 {N_("Size"), "zoom", N_("Change icon size"),
125 toolbar_size_clicked
, DROP_NONE
, TRUE
,
128 {N_("Details"), "details", N_("Show extra details"),
129 toolbar_details_clicked
, DROP_NONE
, TRUE
,
132 {N_("Hidden"), "hidden", N_("Show/hide hidden files"),
133 toolbar_hidden_clicked
, DROP_NONE
, TRUE
,
136 {N_("Help"), "help", N_("Show ROX-Filer help"),
137 toolbar_help_clicked
, DROP_NONE
, TRUE
,
142 /****************************************************************
143 * EXTERNAL INTERFACE *
144 ****************************************************************/
146 void toolbar_init(void)
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
];
165 path
= g_strconcat("images/",
166 tool
->name
, ".png", NULL
);
167 tool
->icon
= load_pixmap(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
];
183 GtkWidget
*icon_widget
, *vbox
, *text
;
185 g_return_val_if_fail(i
>= 0, NULL
);
187 if (i
>= sizeof(all_tools
) / sizeof(*all_tools
))
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
,
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
);
216 /* Create a new toolbar widget, suitable for adding to a filer window,
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
)
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
);
249 filer_opendir(make_path(app_dir
, "Help")->str
, NULL
);
252 static void toolbar_refresh_clicked(GtkWidget
*widget
,
253 FilerWindow
*filer_window
)
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
);
266 filer_update_dir(filer_window
, TRUE
);
270 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
274 event
= gtk_get_current_event();
275 if (event
->type
== GDK_BUTTON_RELEASE
&& NEW_WIN_BUTTON(event
))
277 filer_opendir(home_dir
, filer_window
);
280 filer_change_to(filer_window
, home_dir
, NULL
);
283 static void toolbar_close_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
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
);
296 gtk_widget_destroy(filer_window
->window
);
299 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
303 event
= gtk_get_current_event();
304 if (event
->type
== GDK_BUTTON_RELEASE
&& NEW_WIN_BUTTON(event
))
306 filer_open_parent(filer_window
);
309 change_to_parent(filer_window
);
312 static void toolbar_size_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
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
,
330 display_set_layout(filer_window
,
331 filer_window
->display_style
,
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
)
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
];
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
,
367 if (o_toolbar_info
.int_value
)
368 g_signal_connect_object(filer_window
->collection
,
370 G_CALLBACK(coll_selection_changed
), box
, 0);
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
);
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
);
414 static gint
menu_pressed(GtkWidget
*button
,
415 GdkEventButton
*event
,
416 FilerWindow
*filer_window
)
420 if (event
->button
!= 3 && event
->button
!= 2)
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
);
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
);
443 g_warning("Not implemented");
445 gtk_object_set_data(GTK_OBJECT(button
), "popup_menu",
447 gtk_signal_connect(GTK_OBJECT(button
), "button_press_event",
448 GTK_SIGNAL_FUNC(menu_pressed
), filer_window
);
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
,
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
);
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);
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
,
504 FilerWindow
*filer_window
)
506 GdkDragAction action
= context
->suggested_action
;
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",
518 slash
= strrchr(filer_window
->path
, '/');
519 if (slash
== NULL
|| slash
== filer_window
->path
)
520 path
= g_strdup("/");
522 path
= g_strndup(filer_window
->path
,
523 slash
- filer_window
->path
);
524 g_dataset_set_data_full(context
, "drop_dest_path",
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
);
537 static void drag_leave(GtkWidget
*widget
,
538 GdkDragContext
*context
,
540 FilerWindow
*filer_window
)
542 gtk_button_set_relief(GTK_BUTTON(widget
), GTK_RELIEF_NONE
);
546 static void handle_drops(FilerWindow
*filer_window
,
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] == '.')
567 static void coll_selection_changed(Collection
*collection
, guint time
,
570 FilerWindow
*filer_window
;
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
)
580 if (collection
->number_selected
== 0)
584 if (filer_window
->scanning
)
587 GTK_LABEL(filer_window
->toolbar_text
), "");
591 if (!filer_window
->show_hidden
)
593 GHashTable
*hash
= filer_window
->directory
->known_items
;
596 g_hash_table_foreach(hash
, tally_items
, &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"),
608 else /* (French plurals work differently for zero) */
609 label
= g_strdup_printf(_("No items%s"),
615 gint i
= collection
->number_of_items
;
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
);
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
);
651 toolbar
= toolbar_new(filer_window
);
652 gtk_container_add(GTK_CONTAINER(filer_window
->toolbar_frame
),
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)
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
)
680 if (changed
|| o_toolbar
.has_changed
|| o_toolbar_info
.has_changed
)
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
)
697 kids
= gtk_container_get_children(GTK_CONTAINER(option
->widget
));
699 for (next
= kids
; next
; next
= next
->next
)
701 GtkWidget
*kid
= (GtkWidget
*) next
->data
;
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
));
715 static guchar
*read_tools(Option
*option
)
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
;
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
);
736 g_string_append(list
, ", ");
737 g_string_append(list
, name
);
743 g_string_free(list
, FALSE
);
748 static GList
*build_tool_options(Option
*option
, xmlNode
*node
, guchar
*label
)
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
),
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
);