1 /*
2  * ROX-Filer, filer for the ROX desktop project
3  * Copyright (C) 2006, Thomas Leonard and others (see changelog for details).
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the Free
7  * Software Foundation; either version 2 of the License, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17  * Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 /* toolbar.c - for the button bars that go along the tops of windows */
21 
22 #include "config.h"
23 
24 #include <string.h>
25 
26 #include "global.h"
27 
28 #include "toolbar.h"
29 #include "options.h"
30 #include "support.h"
31 #include "main.h"
32 #include "menu.h"
33 #include "dnd.h"
34 #include "filer.h"
35 #include "display.h"
36 #include "pixmaps.h"
37 #include "bind.h"
38 #include "type.h"
39 #include "dir.h"
40 #include "diritem.h"
41 #include "view_iface.h"
42 #include "bookmarks.h"
43 #include "gui_support.h"
44 
45 typedef struct _Tool Tool;
46 
47 typedef enum {DROP_NONE, DROP_TO_PARENT, DROP_TO_HOME, DROP_BOOKMARK} DropDest;
48 
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 	gboolean	menu;		/* Activate on button-press */
57 };
58 
59 Option o_toolbar, o_toolbar_info, o_toolbar_disable;
60 Option o_toolbar_min_width;
61 
62 static FilerWindow *filer_window_being_counted;
63 
64 /* TRUE if the button presses (or released) should open a new window,
65  * rather than reusing the existing one.
66  */
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)
71 
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_autosize_clicked(GtkWidget *widget, FilerWindow *filer_window);
83 static void toolbar_details_clicked(GtkWidget *widget,
84 				    FilerWindow *filer_window);
85 static void toolbar_hidden_clicked(GtkWidget *widget,
86 				   FilerWindow *filer_window);
87 static void toolbar_select_clicked(GtkWidget *widget,
88 				   FilerWindow *filer_window);
89 static void toolbar_sort_clicked(GtkWidget *widget,
90 				   FilerWindow *filer_window);
91 static GtkWidget *add_button(GtkWidget *bar, Tool *tool,
92 				FilerWindow *filer_window);
93 static GtkWidget *create_toolbar(FilerWindow *filer_window);
94 static gboolean drag_motion(GtkWidget		*widget,
95                             GdkDragContext	*context,
96                             gint		x,
97                             gint		y,
98                             guint		time,
99 			    FilerWindow		*filer_window);
100 static void drag_leave(GtkWidget	*widget,
101                        GdkDragContext	*context,
102 		       guint32		time,
103 		       FilerWindow	*filer_window);
104 static void handle_drops(FilerWindow *filer_window,
105 			 GtkWidget *button,
106 			 DropDest dest);
107 static void toggle_selected(GtkToggleButton *widget, gpointer data);
108 static void option_notify(void);
109 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label);
110 static void tally_items(gpointer key, gpointer value, gpointer data);
111 
112 static Tool all_tools[] = {
113 	{N_("Close"), GTK_STOCK_CLOSE, N_("Close filer window"),
114 	 toolbar_close_clicked, DROP_NONE, FALSE,
115 	 FALSE},
116 
117 	{N_("Up"), GTK_STOCK_GO_UP, N_("Change to parent directory"),
118 	 toolbar_up_clicked, DROP_TO_PARENT, TRUE,
119 	 FALSE},
120 
121 	{N_("Home"), GTK_STOCK_HOME, N_("Change to home directory"),
122 	 toolbar_home_clicked, DROP_TO_HOME, TRUE,
123 	 FALSE},
124 
125 	{N_("Bookmarks"), ROX_STOCK_BOOKMARKS, N_("Bookmarks menu"),
126 	 toolbar_bookmarks_clicked, DROP_BOOKMARK, FALSE,
127 	 TRUE},
128 
129 	{N_("Scan"), GTK_STOCK_REFRESH, N_("Rescan directory contents"),
130 	 toolbar_refresh_clicked, DROP_NONE, TRUE,
131 	 FALSE},
132 
133 	{N_("Size"), GTK_STOCK_ZOOM_IN, N_("Change icon size"),
134 	 toolbar_size_clicked, DROP_NONE, TRUE,
135 	 FALSE},
136 
137 	{N_("Size"), GTK_STOCK_ZOOM_FIT, N_("Automatic size mode"),
138 	 toolbar_autosize_clicked, DROP_NONE, TRUE,
139 	 FALSE},
140 
141 	{N_("Details"), ROX_STOCK_SHOW_DETAILS, N_("Show extra details"),
142 	 toolbar_details_clicked, DROP_NONE, TRUE,
143 	 FALSE},
144 
145 	{N_("Sort"), GTK_STOCK_SORT_ASCENDING, N_("Change sort criteria"),
146 	 toolbar_sort_clicked, DROP_NONE, FALSE,
147 	 FALSE},
148 
149 	{N_("Hidden"), ROX_STOCK_SHOW_HIDDEN, N_("Left: Show/hide hidden files\n"
150 						 "Right: Show/hide thumbnails"),
151 	 toolbar_hidden_clicked, DROP_NONE, TRUE,
152 	 FALSE},
153 
154 	{N_("Select"), ROX_STOCK_SELECT, N_("Select all/invert selection"),
155 	 toolbar_select_clicked, DROP_NONE, FALSE,
156 	 FALSE},
157 
158 	{N_("Help"), GTK_STOCK_HELP, N_("Show ROX-Filer help"),
159 	 toolbar_help_clicked, DROP_NONE, TRUE,
160 	 FALSE},
161 };
162 
163 
164 /****************************************************************
165  *			EXTERNAL INTERFACE			*
166  ****************************************************************/
167 
toolbar_init(void)168 void toolbar_init(void)
169 {
170 	option_add_int(&o_toolbar, "toolbar_type", TOOLBAR_NORMAL);
171 	option_add_int(&o_toolbar_info, "toolbar_show_info", 1);
172 	option_add_string(&o_toolbar_disable, "toolbar_disable",
173 					GTK_STOCK_CLOSE);
174 	option_add_int(&o_toolbar_min_width, "toolbar_min_width", 1);
175 	option_add_notify(option_notify);
176 
177 	option_register_widget("tool-options", build_tool_options);
178 }
179 
toolbar_update_info(FilerWindow * filer_window)180 void toolbar_update_info(FilerWindow *filer_window)
181 {
182 	gchar		*label;
183 	ViewIface	*view;
184 	int		n_selected;
185 
186 	g_return_if_fail(filer_window != NULL);
187 
188 	if (o_toolbar.int_value == TOOLBAR_NONE || !o_toolbar_info.int_value)
189 		return;		/* Not showing info */
190 
191 	if (filer_window->target_cb)
192 		return;
193 
194 	view = filer_window->view;
195 
196 	n_selected = view_count_selected(view);
197 
198 	if (n_selected == 0)
199 	{
200 		gchar *s = NULL;
201 		int   n_items;
202 
203 		if (filer_window->scanning)
204 		{
205 			gtk_label_set_text(
206 				GTK_LABEL(filer_window->toolbar_text), "");
207 			return;
208 		}
209 
210 		if (!(filer_window->show_hidden ||
211 		      filer_window->temp_show_hidden) ||
212 		    filer_window->filter!=FILER_SHOW_ALL)
213 		{
214 			GHashTable *hash = filer_window->directory->known_items;
215 			int	   tally = 0;
216 
217 			filer_window_being_counted=filer_window;
218 			g_hash_table_foreach(hash, tally_items, &tally);
219 
220 			if (tally)
221 				s = g_strdup_printf(_(" (%u hidden)"), tally);
222 		}
223 
224 		n_items = view_count_items(view);
225 
226 		if (n_items)
227 			label = g_strdup_printf("%d %s%s",
228 					n_items,
229 					n_items != 1 ? _("items") : _("item"),
230 					s ? s : "");
231 		else /* (French plurals work differently for zero) */
232 			label = g_strdup_printf(_("No items%s"),
233 					s ? s : "");
234 		g_free(s);
235 	}
236 	else
237 	{
238 		double	size = 0;
239 		ViewIter iter;
240 		DirItem *item;
241 
242 		view_get_iter(filer_window->view, &iter, VIEW_ITER_SELECTED);
243 
244 		while ((item = iter.next(&iter)))
245 		{
246 			if (item->base_type != TYPE_DIRECTORY &&
247 			    item->base_type != TYPE_UNKNOWN)
248 				size += (double) item->size;
249 		}
250 
251 		label = g_strdup_printf(_("%u selected (%s)"),
252 				n_selected, format_double_size(size));
253 	}
254 
255 	gtk_label_set_text(GTK_LABEL(filer_window->toolbar_text), label);
256 	g_free(label);
257 }
258 
259 /* Create, destroy or recreate toolbar for this window so that it
260  * matches the option setting.
261  */
toolbar_update_toolbar(FilerWindow * filer_window)262 void toolbar_update_toolbar(FilerWindow *filer_window)
263 {
264 	g_return_if_fail(filer_window != NULL);
265 
266 	if (filer_window->toolbar)
267 	{
268 		gtk_widget_destroy(filer_window->toolbar);
269 		filer_window->toolbar = NULL;
270 		filer_window->toolbar_text = NULL;
271 	}
272 
273 	if (o_toolbar.int_value != TOOLBAR_NONE)
274 	{
275 		filer_window->toolbar = create_toolbar(filer_window);
276 		gtk_box_pack_start(filer_window->toplevel_vbox,
277 				filer_window->toolbar, FALSE, TRUE, 0);
278 		gtk_box_reorder_child(filer_window->toplevel_vbox,
279 				filer_window->toolbar, 0);
280 		gtk_widget_show_all(filer_window->toolbar);
281 	}
282 
283 	filer_target_mode(filer_window, NULL, NULL, NULL);
284 	toolbar_update_info(filer_window);
285 }
286 
287 /****************************************************************
288  *			INTERNAL FUNCTIONS			*
289  ****************************************************************/
290 
291 /* Wrapper for gtk_get_current_event() which creates a fake release event
292  * if there is no current event. This is for ATK.
293  */
get_current_event(int default_type)294 static GdkEvent *get_current_event(int default_type)
295 {
296 	GdkEvent *event;
297 
298 	event = gtk_get_current_event();
299 
300 	if (event)
301 		return event;
302 
303 	event = gdk_event_new(default_type);
304 	if (default_type == GDK_BUTTON_PRESS || default_type == GDK_BUTTON_RELEASE)
305 	{
306 		GdkEventButton *bev;
307 		bev = (GdkEventButton *) event;
308 		bev->button = 1;
309 	}
310 	return event;
311 }
312 
toolbar_help_clicked(GtkWidget * widget,FilerWindow * filer_window)313 static void toolbar_help_clicked(GtkWidget *widget, FilerWindow *filer_window)
314 {
315 	GdkEvent	*event;
316 
317 	event = get_current_event(GDK_BUTTON_RELEASE);
318 	if (event->type == GDK_BUTTON_RELEASE &&
319 			((GdkEventButton *) event)->button != 1)
320 		menu_rox_help(NULL, HELP_MANUAL, NULL);
321 	else
322 		filer_opendir(make_path(app_dir, "Help"), NULL, NULL);
323 	gdk_event_free(event);
324 }
325 
toolbar_refresh_clicked(GtkWidget * widget,FilerWindow * filer_window)326 static void toolbar_refresh_clicked(GtkWidget *widget,
327 				    FilerWindow *filer_window)
328 {
329 	GdkEvent	*event;
330 
331 	event = get_current_event(GDK_BUTTON_RELEASE);
332 	if (event->type == GDK_BUTTON_RELEASE &&
333 			((GdkEventButton *) event)->button != 1)
334 	{
335 		filer_opendir(filer_window->sym_path, filer_window, NULL);
336 	}
337 	else
338 		filer_refresh(filer_window);
339 	gdk_event_free(event);
340 }
341 
toolbar_home_clicked(GtkWidget * widget,FilerWindow * filer_window)342 static void toolbar_home_clicked(GtkWidget *widget, FilerWindow *filer_window)
343 {
344 	GdkEvent	*event;
345 
346 	event = get_current_event(GDK_BUTTON_RELEASE);
347 	if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
348 	{
349 		filer_opendir(home_dir, filer_window, NULL);
350 	}
351 	else
352 		filer_change_to(filer_window, home_dir, NULL);
353 	gdk_event_free(event);
354 }
355 
toolbar_bookmarks_clicked(GtkWidget * widget,FilerWindow * filer_window)356 static void toolbar_bookmarks_clicked(GtkWidget *widget,
357 				      FilerWindow *filer_window)
358 {
359 	GdkEvent	*event;
360 
361 	g_return_if_fail(filer_window != NULL);
362 
363 	event = get_current_event(GDK_BUTTON_PRESS);
364 	if (event->type == GDK_BUTTON_PRESS &&
365 			((GdkEventButton *) event)->button == 1)
366 	{
367 		bookmarks_show_menu(filer_window);
368 	}
369 	else if (event->type == GDK_BUTTON_RELEASE &&
370 			((GdkEventButton *) event)->button != 1)
371 	{
372 		bookmarks_edit();
373 	}
374 	gdk_event_free(event);
375 }
376 
toolbar_close_clicked(GtkWidget * widget,FilerWindow * filer_window)377 static void toolbar_close_clicked(GtkWidget *widget, FilerWindow *filer_window)
378 {
379 	GdkEvent	*event;
380 
381 	g_return_if_fail(filer_window != NULL);
382 
383 	event = get_current_event(GDK_BUTTON_RELEASE);
384 	if (event->type == GDK_BUTTON_RELEASE &&
385 			((GdkEventButton *) event)->button != 1)
386 	{
387 		filer_opendir(filer_window->sym_path, filer_window, NULL);
388 	}
389 	else if (!filer_window_delete(filer_window->window, NULL, filer_window))
390 		gtk_widget_destroy(filer_window->window);
391 	gdk_event_free(event);
392 }
393 
toolbar_up_clicked(GtkWidget * widget,FilerWindow * filer_window)394 static void toolbar_up_clicked(GtkWidget *widget, FilerWindow *filer_window)
395 {
396 	GdkEvent	*event;
397 
398 	event = get_current_event(GDK_BUTTON_RELEASE);
399 	if (event->type == GDK_BUTTON_RELEASE && NEW_WIN_BUTTON(event))
400 	{
401 		filer_open_parent(filer_window);
402 	}
403 	else
404 		change_to_parent(filer_window);
405 	gdk_event_free(event);
406 }
407 
toolbar_autosize_clicked(GtkWidget * widget,FilerWindow * filer_window)408 static void toolbar_autosize_clicked(GtkWidget *widget, FilerWindow *filer_window)
409 {
410 	GdkEventButton	*bev;
411 
412 	bev = (GdkEventButton *) get_current_event(GDK_BUTTON_RELEASE);
413 	if (bev->type == GDK_BUTTON_RELEASE)
414 	{
415 		display_set_layout(filer_window, AUTO_SIZE_ICONS, filer_window->details_type,
416 				TRUE);
417 	}
418 	gdk_event_free((GdkEvent *) bev);
419 }
420 
toolbar_size_clicked(GtkWidget * widget,FilerWindow * filer_window)421 static void toolbar_size_clicked(GtkWidget *widget, FilerWindow *filer_window)
422 {
423 	GdkEventButton	*bev;
424 
425 	bev = (GdkEventButton *) get_current_event(GDK_BUTTON_RELEASE);
426 	if (bev->type == GDK_BUTTON_RELEASE)
427 		display_change_size(filer_window, bev->button == 1);
428 	gdk_event_free((GdkEvent *) bev);
429 }
430 
toolbar_sort_clicked(GtkWidget * widget,FilerWindow * filer_window)431 static void toolbar_sort_clicked(GtkWidget *widget,
432 				    FilerWindow *filer_window)
433 {
434 	GdkEventButton	*bev;
435 	int i, current, next, next_wrapped;
436 	gboolean adjust;
437 	GtkSortType dir;
438 	gchar *tip;
439 
440 	static const SortType sorts[]={
441 		SORT_NAME, SORT_TYPE, SORT_DATE, SORT_SIZE,
442 		SORT_OWNER, SORT_GROUP,
443 	};
444 	static const char *sort_names[] = {
445 		N_("Sort by name"), N_("Sort by type"), N_("Sort by date"),
446 		N_("Sort by size"), N_("Sort by owner"), N_("Sort by group"),
447 	};
448 
449 	bev = (GdkEventButton *) get_current_event(GDK_BUTTON_RELEASE);
450 	adjust = (bev->button != 1) && bev->type == GDK_BUTTON_RELEASE;
451 	gdk_event_free((GdkEvent *) bev);
452 
453 	current = -1;
454 	dir = filer_window->sort_order;
455 	for (i=0; i < G_N_ELEMENTS(sort_names); i++)
456 	{
457 		if (filer_window->sort_type == sorts[i])
458 		{
459 			current = i;
460 			break;
461 		}
462 	}
463 
464 	if (current == -1)
465 		next = 0;
466 	else if (adjust)
467 		next = current - 1;
468 	else
469 		next = current + 1;
470 
471 	next_wrapped = next % G_N_ELEMENTS(sorts);
472 
473 	if (next_wrapped != next)
474 		dir = (dir == GTK_SORT_ASCENDING)
475 			? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
476 
477 	display_set_sort_type(filer_window, sorts[next_wrapped], dir);
478  	tip = g_strconcat(_(sort_names[next_wrapped]), ", ",
479 			dir == GTK_SORT_ASCENDING
480 				? _("ascending") : _("descending"),
481 			NULL);
482 	tooltip_show(tip);
483 	g_free(tip);
484 }
485 
toolbar_details_clicked(GtkWidget * widget,FilerWindow * filer_window)486 static void toolbar_details_clicked(GtkWidget *widget,
487 				    FilerWindow *filer_window)
488 {
489 	if (filer_window->view_type == VIEW_TYPE_DETAILS)
490 		filer_set_view_type(filer_window, VIEW_TYPE_COLLECTION);
491 	else
492 		filer_set_view_type(filer_window, VIEW_TYPE_DETAILS);
493 }
494 
toolbar_hidden_clicked(GtkWidget * widget,FilerWindow * filer_window)495 static void toolbar_hidden_clicked(GtkWidget *widget,
496 				   FilerWindow *filer_window)
497 {
498 	GdkEvent	*event;
499 
500 	event = get_current_event(GDK_BUTTON_RELEASE);
501 	if (event->type == GDK_BUTTON_RELEASE &&
502 			((GdkEventButton *) event)->button == 1)
503 	{
504 		display_set_hidden(filer_window, !filer_window->show_hidden);
505 	}
506 	else
507 	{
508 		display_set_thumbs(filer_window, !filer_window->show_thumbs);
509 	}
510 }
511 
invert_cb(ViewIter * iter,gpointer data)512 static gboolean invert_cb(ViewIter *iter, gpointer data)
513 {
514 	return !view_get_selected((ViewIface *) data, iter);
515 }
516 
toolbar_select_clicked(GtkWidget * widget,FilerWindow * filer_window)517 static void toolbar_select_clicked(GtkWidget *widget, FilerWindow *filer_window)
518 {
519 	GdkEvent	*event;
520 
521 	event = get_current_event(GDK_BUTTON_RELEASE);
522 	if (event->type == GDK_BUTTON_RELEASE)
523 	{
524 		if (((GdkEventButton *) event)->button == 1)
525 			view_select_all(filer_window->view);
526 		else
527 			view_select_if(filer_window->view, invert_cb,
528 				       filer_window->view);
529 	}
530 	filer_window->temp_item_selected = FALSE;
531 	gdk_event_free(event);
532 }
533 
534 /* If filer_window is NULL, the toolbar is for the options window */
create_toolbar(FilerWindow * filer_window)535 static GtkWidget *create_toolbar(FilerWindow *filer_window)
536 {
537 	GtkWidget	*bar;
538 	GtkWidget	*b;
539 	int		i;
540 	int		width;
541 
542 	bar = gtk_toolbar_new();
543 
544 	if (o_toolbar.int_value == TOOLBAR_NORMAL || !filer_window)
545 		gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_ICONS);
546 	else if (o_toolbar.int_value == TOOLBAR_HORIZONTAL)
547 		gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH_HORIZ);
548 	else
549 		gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
550 
551 	width=0;
552 	for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
553 	{
554 		Tool	*tool = &all_tools[i];
555 		GtkRequisition req;
556 
557 		if (filer_window && !tool->enabled)
558 			continue;
559 
560 		b = add_button(bar, tool, filer_window);
561 
562 		gtk_widget_size_request(b, &req);
563 		width+=req.width;
564 
565 		if (filer_window && tool->drop_action != DROP_NONE)
566 			handle_drops(filer_window, b, tool->drop_action);
567 	}
568 
569 	if (filer_window)
570 	{
571 		if(o_toolbar_min_width.int_value)
572 		{
573 			/* Make the toolbar wide enough for all icons to be
574 			   seen, plus a little for the (start of the) text
575 			   label */
576 			gtk_widget_set_size_request(bar, width+32, -1);
577 		} else {
578 			gtk_widget_set_size_request(bar, 100, -1);
579 		}
580 
581 		filer_window->toolbar_text = gtk_label_new("");
582 		gtk_misc_set_alignment(GTK_MISC(filer_window->toolbar_text),
583 					0, 0.5);
584 		gtk_toolbar_append_widget(GTK_TOOLBAR(bar),
585 				filer_window->toolbar_text, NULL, NULL);
586 	}
587 
588 	return bar;
589 }
590 
591 /* This is used to simulate a click when button 3 is used (GtkButton
592  * normally ignores this).
593  */
594 static gint toolbar_other_button = 0;
toolbar_button_pressed(GtkButton * button,GdkEventButton * event,FilerWindow * filer_window)595 static gint toolbar_button_pressed(GtkButton *button,
596 				GdkEventButton *event,
597 				FilerWindow *filer_window)
598 {
599 	gint	b = event->button;
600 	Tool	*tool;
601 
602 	tool = g_object_get_data(G_OBJECT(button), "rox-tool");
603 	g_return_val_if_fail(tool != NULL, TRUE);
604 
605 	if (tool->menu && b == 1)
606 	{
607 		tool->clicked((GtkWidget *) button, filer_window);
608 		return TRUE;
609 	}
610 
611 	if ((b == 2 || b == 3) && toolbar_other_button == 0)
612 	{
613 		toolbar_other_button = event->button;
614 		gtk_grab_add(GTK_WIDGET(button));
615 		gtk_button_pressed(button);
616 
617 		return TRUE;
618 	}
619 
620 	return FALSE;
621 }
622 
toolbar_button_released(GtkButton * button,GdkEventButton * event,FilerWindow * filer_window)623 static gint toolbar_button_released(GtkButton *button,
624 				GdkEventButton *event,
625 				FilerWindow *filer_window)
626 {
627 	if (event->button == toolbar_other_button)
628 	{
629 		toolbar_other_button = 0;
630 		gtk_grab_remove(GTK_WIDGET(button));
631 		gtk_button_released(button);
632 
633 		return TRUE;
634 	}
635 
636 	return FALSE;
637 }
638 
639 /* If filer_window is NULL, the toolbar is for the options window */
add_button(GtkWidget * bar,Tool * tool,FilerWindow * filer_window)640 static GtkWidget *add_button(GtkWidget *bar, Tool *tool,
641 				FilerWindow *filer_window)
642 {
643 	GtkWidget 	*button, *icon_widget;
644 
645 	icon_widget = gtk_image_new_from_stock(tool->name,
646 						GTK_ICON_SIZE_LARGE_TOOLBAR);
647 
648 	button = gtk_toolbar_insert_element(GTK_TOOLBAR(bar),
649 			   filer_window ? GTK_TOOLBAR_CHILD_BUTTON
650 					: GTK_TOOLBAR_CHILD_TOGGLEBUTTON,
651 			   NULL,
652 			   _(tool->label),
653 			   _(tool->tip), NULL,
654 			   icon_widget,
655 			   NULL, NULL,	/* CB, userdata */
656 			   GTK_TOOLBAR(bar)->num_children);
657 	GTK_WIDGET_UNSET_FLAGS(button, GTK_CAN_FOCUS);
658 
659 	if (o_toolbar.int_value == TOOLBAR_HORIZONTAL)
660 	{
661 		GtkWidget *hbox, *label;
662 		GList	  *kids;
663 		hbox = GTK_BIN(button)->child;
664 		kids = gtk_container_get_children(GTK_CONTAINER(hbox));
665 		label = g_list_nth_data(kids, 1);
666 		g_list_free(kids);
667 
668 		if (label)
669 		{
670 			gtk_box_set_child_packing(GTK_BOX(hbox), label,
671 						TRUE, TRUE, 0, GTK_PACK_END);
672 			gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
673 		}
674 	}
675 
676 	g_object_set_data(G_OBJECT(button), "rox-tool", tool);
677 
678 	if (filer_window)
679 	{
680 		g_signal_connect(button, "clicked",
681 			G_CALLBACK(tool->clicked), filer_window);
682 		g_signal_connect(button, "button_press_event",
683 			G_CALLBACK(toolbar_button_pressed), filer_window);
684 		g_signal_connect(button, "button_release_event",
685 			G_CALLBACK(toolbar_button_released), filer_window);
686 	}
687 	else
688 	{
689 		g_signal_connect(button, "clicked",
690 			G_CALLBACK(toggle_selected), NULL);
691 		g_object_set_data(G_OBJECT(button), "tool_name",
692 				  (gpointer) tool->name);
693 	}
694 
695 	return button;
696 }
697 
toggle_selected(GtkToggleButton * widget,gpointer data)698 static void toggle_selected(GtkToggleButton *widget, gpointer data)
699 {
700 	option_check_widget(&o_toolbar_disable);
701 }
702 
703 /* Called during the drag when the mouse is in a widget registered
704  * as a drop target. Returns TRUE if we can accept the drop.
705  */
drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,FilerWindow * filer_window)706 static gboolean drag_motion(GtkWidget		*widget,
707                             GdkDragContext	*context,
708                             gint		x,
709                             gint		y,
710                             guint		time,
711 			    FilerWindow		*filer_window)
712 {
713 	GdkDragAction	action = context->suggested_action;
714 	DropDest	dest;
715 	gpointer	type = (gpointer) drop_dest_dir;
716 
717 	dest = (DropDest) g_object_get_data(G_OBJECT(widget), "toolbar_dest");
718 
719 	if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value &&
720 		dest != DROP_BOOKMARK)
721 	{
722 		guint state;
723 		gdk_window_get_pointer(NULL, NULL, NULL, &state);
724 		if (state & GDK_BUTTON1_MASK)
725 			action = GDK_ACTION_ASK;
726 	}
727 
728 	if (dest == DROP_TO_HOME)
729 		g_dataset_set_data(context, "drop_dest_path",
730 				   (gchar *) home_dir);
731 	else if (dest == DROP_BOOKMARK)
732 		type = (gpointer) drop_dest_bookmark;
733 	else
734 		g_dataset_set_data_full(context, "drop_dest_path",
735 				g_path_get_dirname(filer_window->sym_path),
736 				g_free);
737 
738 	g_dataset_set_data(context, "drop_dest_type", type);
739 	gdk_drag_status(context, action, time);
740 
741 	dnd_spring_load(context, filer_window);
742 	gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NORMAL);
743 
744 	return TRUE;
745 }
746 
drag_leave(GtkWidget * widget,GdkDragContext * context,guint32 time,FilerWindow * filer_window)747 static void drag_leave(GtkWidget	*widget,
748                        GdkDragContext	*context,
749 		       guint32		time,
750 		       FilerWindow	*filer_window)
751 {
752 	gtk_button_set_relief(GTK_BUTTON(widget), GTK_RELIEF_NONE);
753 	dnd_spring_abort();
754 }
755 
handle_drops(FilerWindow * filer_window,GtkWidget * button,DropDest dest)756 static void handle_drops(FilerWindow *filer_window,
757 			 GtkWidget *button,
758 			 DropDest dest)
759 {
760 	make_drop_target(button, 0);
761 	g_signal_connect(button, "drag_motion",
762 			G_CALLBACK(drag_motion), filer_window);
763 	g_signal_connect(button, "drag_leave",
764 			G_CALLBACK(drag_leave), filer_window);
765 	g_object_set_data(G_OBJECT(button), "toolbar_dest", (gpointer) dest);
766 }
767 
tally_items(gpointer key,gpointer value,gpointer data)768 static void tally_items(gpointer key, gpointer value, gpointer data)
769 {
770 	DirItem *item = (DirItem *) value;
771 	int     *tally = (int *) data;
772 
773 	if (!filer_match_filter(filer_window_being_counted, item))
774 		(*tally)++;
775 }
776 
option_notify(void)777 static void option_notify(void)
778 {
779 	int		i;
780 	gboolean	changed = FALSE;
781 	guchar		*list = o_toolbar_disable.value;
782 
783 	for (i = 0; i < sizeof(all_tools) / sizeof(*all_tools); i++)
784 	{
785 		Tool	*tool = &all_tools[i];
786 		gboolean old = tool->enabled;
787 
788 		tool->enabled = !in_list(tool->name, list);
789 
790 		if (old != tool->enabled)
791 			changed = TRUE;
792 	}
793 
794 	if (changed || o_toolbar.has_changed || o_toolbar_info.has_changed)
795 	{
796 		GList	*next;
797 
798 		for (next = all_filer_windows; next; next = next->next)
799 		{
800 			FilerWindow *filer_window = (FilerWindow *) next->data;
801 
802 			toolbar_update_toolbar(filer_window);
803 		}
804 	}
805 }
806 
update_tools(Option * option)807 static void update_tools(Option *option)
808 {
809 	GList	*next, *kids;
810 
811 	kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
812 
813 	for (next = kids; next; next = next->next)
814 	{
815 		GtkToggleButton	*kid = (GtkToggleButton *) next->data;
816 		guchar		*name;
817 
818 		name = g_object_get_data(G_OBJECT(kid), "tool_name");
819 
820 		g_return_if_fail(name != NULL);
821 
822 		gtk_toggle_button_set_active(kid,
823 					 !in_list(name, option->value));
824 	}
825 
826 	g_list_free(kids);
827 }
828 
read_tools(Option * option)829 static guchar *read_tools(Option *option)
830 {
831 	GList	*next, *kids;
832 	GString	*list;
833 	guchar	*retval;
834 
835 	list = g_string_new(NULL);
836 
837 	kids = gtk_container_get_children(GTK_CONTAINER(option->widget));
838 
839 	for (next = kids; next; next = next->next)
840 	{
841 		GtkToggleButton	*kid = (GtkToggleButton *) next->data;
842 		guchar		*name;
843 
844 		if (!gtk_toggle_button_get_active(kid))
845 		{
846 			name = g_object_get_data(G_OBJECT(kid), "tool_name");
847 			g_return_val_if_fail(name != NULL, list->str);
848 
849 			if (list->len)
850 				g_string_append(list, ", ");
851 			g_string_append(list, name);
852 		}
853 	}
854 
855 	g_list_free(kids);
856 	retval = list->str;
857 	g_string_free(list, FALSE);
858 
859 	return retval;
860 }
861 
build_tool_options(Option * option,xmlNode * node,guchar * label)862 static GList *build_tool_options(Option *option, xmlNode *node, guchar *label)
863 {
864 	GtkWidget	*bar;
865 
866 	g_return_val_if_fail(option != NULL, NULL);
867 
868 	bar = create_toolbar(NULL);
869 
870 	option->update_widget = update_tools;
871 	option->read_widget = read_tools;
872 	option->widget = bar;
873 
874 	return g_list_append(NULL, bar);
875 }
876