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)
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 */
42 typedef struct _Tool Tool
;
44 typedef enum {DROP_NONE
, DROP_TO_PARENT
, DROP_TO_HOME
} DropDest
;
49 guchar
*tip
; /* Tooltip */
50 void (*clicked
)(GtkWidget
*w
, FilerWindow
*filer_window
);
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
,
89 FilerWindow
*filer_window
);
90 static void drag_leave(GtkWidget
*widget
,
91 GdkDragContext
*context
,
93 FilerWindow
*filer_window
);
94 static void handle_drops(FilerWindow
*filer_window
,
97 static void coll_selection_changed(Collection
*collection
, guint time
,
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
,
108 {N_("Up"), "up", N_("Change to parent directory"),
109 toolbar_up_clicked
, DROP_TO_PARENT
, TRUE
,
112 {N_("Home"), "home", N_("Change to home directory"),
113 toolbar_home_clicked
, DROP_TO_HOME
, TRUE
,
116 {N_("Scan"), "refresh", N_("Rescan directory contents"),
117 toolbar_refresh_clicked
, DROP_NONE
, TRUE
,
120 {N_("Size"), "zoom", N_("Change icon size"),
121 toolbar_size_clicked
, DROP_NONE
, TRUE
,
124 {N_("Details"), "details", N_("Show extra details"),
125 toolbar_details_clicked
, DROP_NONE
, TRUE
,
128 {N_("Hidden"), "hidden", N_("Show/hide hidden files"),
129 toolbar_hidden_clicked
, DROP_NONE
, TRUE
,
132 {N_("Help"), "help", N_("Show ROX-Filer help"),
133 toolbar_help_clicked
, DROP_NONE
, TRUE
,
138 /****************************************************************
139 * EXTERNAL INTERFACE *
140 ****************************************************************/
142 void toolbar_init(void)
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
];
161 path
= g_strconcat("pixmaps/",
162 tool
->name
, ".xpm", NULL
);
163 tool
->icon
= load_pixmap(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
];
177 GtkWidget
*icon_widget
, *vbox
, *text
;
179 g_return_val_if_fail(i
>= 0, NULL
);
181 if (i
>= sizeof(all_tools
) / sizeof(*all_tools
))
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
,
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
);
210 /* Create a new toolbar widget, suitable for adding to a filer window,
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
)
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
);
252 filer_update_dir(filer_window
, TRUE
);
256 static void toolbar_home_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
260 event
= gtk_get_current_event();
261 if (event
->type
== GDK_BUTTON_RELEASE
&& NEW_WIN_BUTTON(event
))
263 filer_opendir(home_dir
, filer_window
);
266 filer_change_to(filer_window
, home_dir
, NULL
);
269 static void toolbar_close_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
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
);
282 gtk_widget_destroy(filer_window
->window
);
285 static void toolbar_up_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
289 event
= gtk_get_current_event();
290 if (event
->type
== GDK_BUTTON_RELEASE
&& NEW_WIN_BUTTON(event
))
292 filer_open_parent(filer_window
);
295 change_to_parent(filer_window
);
298 static void toolbar_size_clicked(GtkWidget
*widget
, FilerWindow
*filer_window
)
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
);
314 display_set_layout(filer_window
,
315 option_get_int("display_size"),
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
)
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
];
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
,
352 gtk_signal_connect(GTK_OBJECT(filer_window
->collection
),
354 GTK_SIGNAL_FUNC(coll_selection_changed
),
355 (gpointer
) filer_window
);
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
);
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
);
399 static gint
menu_pressed(GtkWidget
*button
,
400 GdkEventButton
*event
,
401 FilerWindow
*filer_window
)
405 if (event
->button
!= 3 && event
->button
!= 2)
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
);
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
);
429 g_warning("Not implemented");
431 gtk_object_set_data(GTK_OBJECT(button
), "popup_menu",
433 gtk_signal_connect(GTK_OBJECT(button
), "button_press_event",
434 GTK_SIGNAL_FUNC(menu_pressed
), filer_window
);
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",
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
);
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);
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
,
488 FilerWindow
*filer_window
)
490 GdkDragAction action
= context
->suggested_action
;
493 dest
= (DropDest
) gtk_object_get_data(GTK_OBJECT(widget
),
496 if (dest
== DROP_TO_HOME
)
497 g_dataset_set_data(context
, "drop_dest_path", home_dir
);
500 guchar
*slash
, *path
;
502 slash
= strrchr(filer_window
->path
, '/');
503 if (slash
== NULL
|| slash
== filer_window
->path
)
504 path
= g_strdup("/");
506 path
= g_strndup(filer_window
->path
,
507 slash
- filer_window
->path
);
508 g_dataset_set_data_full(context
, "drop_dest_path",
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
);
521 static void drag_leave(GtkWidget
*widget
,
522 GdkDragContext
*context
,
524 FilerWindow
*filer_window
)
526 gtk_button_set_relief(GTK_BUTTON(widget
), GTK_RELIEF_NONE
);
530 static void handle_drops(FilerWindow
*filer_window
,
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",
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] == '.')
552 static void coll_selection_changed(Collection
*collection
, guint time
,
555 FilerWindow
*filer_window
= (FilerWindow
*) user_data
;
558 if (filer_window
->target_cb
)
561 if (collection
->number_selected
== 0)
565 if (filer_window
->scanning
)
568 GTK_LABEL(filer_window
->toolbar_text
), "");
572 if (!filer_window
->show_hidden
)
574 GHashTable
*hash
= filer_window
->directory
->known_items
;
577 g_hash_table_foreach(hash
, tally_items
, &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"),
589 else /* (French plurals work differently for zero) */
590 label
= g_strdup_printf(_("No items%s"),
596 gint i
= collection
->number_of_items
;
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
);
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
);
632 toolbar
= toolbar_new(filer_window
);
633 gtk_container_add(GTK_CONTAINER(filer_window
->toolbar_frame
),
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
;
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
)
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
)
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
);