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 /* panel.c - code for dealing with panel windows */
21 
22 #include "config.h"
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <libxml/parser.h>
31 
32 #include <gtk/gtk.h>
33 #include <gdk/gdkx.h>
34 
35 #include "global.h"
36 
37 #include "panel.h"
38 #include "options.h"
39 #include "choices.h"
40 #include "main.h"
41 #include "type.h"
42 #include "gui_support.h"
43 #include "diritem.h"
44 #include "pixmaps.h"
45 #include "filer.h"
46 #include "display.h"
47 #include "bind.h"
48 #include "dnd.h"
49 #include "support.h"
50 #include "icon.h"
51 #include "run.h"
52 #include "appinfo.h"
53 #include "pixmaps.h"
54 #include "xml.h"
55 #include "pinboard.h"		/* For pinboard_get_window() */
56 
57 /* The width of the separator at the inner edge of the panel */
58 #define EDGE_WIDTH 1
59 
60 /* The gap between panel icons */
61 #define PANEL_ICON_SPACING 8
62 
63 enum {TEXT_BESIDE_ICON, TEXT_UNDER_ICON};
64 
65 static gboolean tmp_icon_selected = FALSE;		/* When dragging */
66 
67 typedef struct _PanelIconClass PanelIconClass;
68 typedef struct _PanelIcon PanelIcon;
69 
70 struct _PanelIconClass {
71 	IconClass parent;
72 };
73 
74 struct _PanelIcon {
75 	Icon		icon;
76 	GdkPixbuf 	*image;
77 
78 	Panel		*panel;
79 	GtkWidget	*widget;	/* The drawing area for the icon */
80 	GtkWidget	*label;
81 	GtkWidget	*socket;	/* For applets */
82 
83 	int		style;
84 };
85 
86 #define PANEL_ICON(obj) GTK_CHECK_CAST((obj), panel_icon_get_type(), PanelIcon)
87 #define IS_PANEL_ICON(obj) \
88 	G_TYPE_CHECK_INSTANCE_TYPE((obj), panel_icon_get_type())
89 
90 Panel *current_panel[PANEL_NUMBER_OF_SIDES];
91 
92 /* NULL => Not loading a panel */
93 static Panel *loading_panel = NULL;
94 
95 static GtkWidget *panel_options_dialog = NULL;
96 
97 /* Static prototypes */
98 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel);
99 static void panel_destroyed(GtkWidget *widget, Panel *panel);
100 static const char *pan_from_file(gchar *line);
101 static gint icon_button_release(GtkWidget *widget,
102 				GdkEventButton *event,
103 				PanelIcon *pi);
104 static gint icon_button_press(GtkWidget *widget,
105 			      GdkEventButton *event,
106 			      PanelIcon *pi);
107 static void reposition_panel(GtkWidget *window,
108 				GtkAllocation *alloc, Panel *panel);
109 static gint expose_icon(GtkWidget *widget,
110 			GdkEventExpose *event,
111 			PanelIcon *pi);
112 static gint draw_icon(GtkWidget *widget,
113 			GdkRectangle *badarea,
114 			PanelIcon *pi);
115 static gint panel_button_release(GtkWidget *widget,
116 			      GdkEventButton *event,
117 			      Panel *panel);
118 static gint panel_button_press(GtkWidget *widget,
119 			      GdkEventButton *event,
120 			      Panel *panel);
121 static void panel_post_resize(GtkWidget *box,
122 			GtkRequisition *req, Panel *panel);
123 static void drag_set_panel_dest(PanelIcon *pi);
124 static void add_uri_list(GtkWidget          *widget,
125 			 GdkDragContext     *context,
126 			 gint               x,
127 			 gint               y,
128 			 GtkSelectionData   *selection_data,
129 			 guint              info,
130 			 guint32            time,
131 			 Panel		    *panel);
132 static void panel_add_item(Panel *panel,
133 			   const gchar *path,
134 			   const gchar *name,
135 			   gboolean after,
136 			   const gchar *shortcut,
137 			   const gchar *args,
138 			   gboolean locked);
139 static gboolean panel_drag_motion(GtkWidget	*widget,
140 			    GdkDragContext	*context,
141 			    gint		x,
142 			    gint		y,
143 			    guint		time,
144 			    Panel		*panel);
145 static gboolean insert_drag_motion(GtkWidget	*widget,
146 			    GdkDragContext	*context,
147 			    gint		x,
148 			    gint		y,
149 			    guint		time,
150 			    Panel		*panel);
151 static gboolean drag_motion(GtkWidget		*widget,
152 			    GdkDragContext	*context,
153 			    gint		x,
154 			    gint		y,
155 			    guint		time,
156 			    PanelIcon		*pi);
157 static void panel_drag_leave(GtkWidget	*widget,
158 		       GdkDragContext	*context,
159 		       guint32		time,
160 		       Panel		*panel);
161 static void drag_leave(GtkWidget	*widget,
162 		       GdkDragContext	*context,
163 		       guint32		time,
164 		       Icon	*icon);
165 static GtkWidget *make_insert_frame(Panel *panel);
166 static gboolean enter_icon(GtkWidget *widget,
167 			   GdkEventCrossing *event,
168 			   Icon *icon);
169 static gint icon_motion_event(GtkWidget *widget,
170 			      GdkEventMotion *event,
171 			      PanelIcon *pi);
172 static gint panel_leave_event(GtkWidget *widget,
173 			      GdkEventCrossing *event,
174 			      Panel *panel);
175 static gint panel_motion_event(GtkWidget *widget,
176 			      GdkEventMotion *event,
177 			      Panel *panel);
178 static void reposition_icon(PanelIcon *pi, int index);
179 static void start_drag(PanelIcon *pi, GdkEventMotion *event);
180 static void drag_end(GtkWidget *widget,
181 		     GdkDragContext *context,
182 		     Icon *icon);
183 static void perform_action(Panel *panel,
184 			   PanelIcon *pi,
185 			   GdkEventButton *event);
186 static void run_applet(PanelIcon *pi);
187 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi);
188 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc);
189 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
190 				Panel *panel);
191 static PanelIcon *panel_icon_new(Panel *panel,
192 				 const char *pathname,
193 				 const char *name);
194 static GType panel_icon_get_type(void);
195 static gboolean panel_want_show_text(PanelIcon *pi);
196 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel);
197 static void panel_style_changed(void);
198 static void motion_may_raise(Panel *panel, int x, int y);
199 static void panel_update(Panel *panel);
200 static GList *build_monitor_number(Option *option,
201 					xmlNode *node, guchar *label);
202 static gboolean may_autoscroll(Panel *panel);
203 static void panel_update_geometry(Panel *panel);
204 static gboolean panel_keep_below(Panel *panel, gboolean setting);
205 
206 
207 static GtkWidget *dnd_highlight = NULL; /* (stops flickering) */
208 
209 #define SHOW_BOTH 0
210 #define SHOW_APPS_SMALL 1
211 #define SHOW_ICON 2
212 static Option o_panel_style;
213 static Option o_panel_width;
214 static Option o_panel_xinerama;
215 static Option o_panel_monitor;
216 static Option o_panel_avoid;
217 static Option o_panel_is_dock;
218 static Option o_panel_on_top;
219 static Option o_panel_obey_workarea;
220 
221 static gint panel_monitor = -1;
222 
223 static int closing_panel = 0;	/* Don't panel_save; destroying! */
224 
225 /****************************************************************
226  *			EXTERNAL INTERFACE			*
227  ****************************************************************/
228 
panel_init(void)229 void panel_init(void)
230 {
231 	option_add_int(&o_panel_style, "panel_style", SHOW_APPS_SMALL);
232 	option_add_int(&o_panel_width, "panel_width", 52);
233 
234 	option_add_int(&o_panel_xinerama, "panel_xinerama", 0);
235 	option_add_int(&o_panel_monitor, "panel_monitor", 0);
236 
237 	option_add_int(&o_panel_avoid, "panel_avoid", TRUE);
238 	option_add_int(&o_panel_is_dock, "panel_is_dock", TRUE);
239 	option_add_int(&o_panel_on_top, "panel_on_top", FALSE);
240 
241 	option_add_int(&o_panel_obey_workarea, "panel_obey_workarea", FALSE);
242 
243 	option_add_notify(panel_style_changed);
244 
245 	option_register_widget("monitor-number", build_monitor_number);
246 }
247 
248 /* Return a free edge for a new panel.
249  * If no edge is free, returns PANEL_BOTTOM.
250  */
find_free_side()251 static PanelSide find_free_side()
252 {
253 	if (!current_panel[PANEL_BOTTOM])
254 		return PANEL_BOTTOM;
255 
256 	if (!current_panel[PANEL_TOP])
257 		return PANEL_TOP;
258 
259 	if (!current_panel[PANEL_LEFT])
260 		return PANEL_LEFT;
261 
262 	if (!current_panel[PANEL_RIGHT])
263 		return PANEL_RIGHT;
264 
265 	return PANEL_BOTTOM;
266 }
267 
268 /* Returns TRUE and sets *target if the property exists, otherwise returns
269  * FALSE and leaves *target unchanged */
get_int_prop(xmlNodePtr node,const char * name,int * target)270 static gboolean get_int_prop(xmlNodePtr node, const char *name, int *target)
271 {
272 	char *prop = xmlGetProp(node, name);
273 
274 	if (prop)
275 	{
276 		*target = atoi(prop);
277 		g_free(prop);
278 		return TRUE;
279 	}
280 	return FALSE;
281 }
282 
set_int_prop(xmlNodePtr node,const char * name,int value)283 static void set_int_prop(xmlNodePtr node, const char *name, int value)
284 {
285 	char prop[16];
286 
287 	sprintf(prop, "%d", value);
288 	xmlSetProp(node, name, prop);
289 }
290 
panel_load_options_from_xml(Panel * panel,xmlDocPtr doc)291 static void panel_load_options_from_xml(Panel *panel, xmlDocPtr doc)
292 {
293 	xmlNodePtr root;
294 	xmlNodePtr options;
295 
296 	root = xmlDocGetRootElement(doc);
297 	options = get_subnode(root, NULL, "options");
298 	if (!options)
299 		return;
300 	get_int_prop(options, "style", &panel->style);
301 	get_int_prop(options, "width", &panel->width);
302 	get_int_prop(options, "avoid", &panel->avoid);
303 	get_int_prop(options, "xinerama", &panel->xinerama);
304 	get_int_prop(options, "monitor", &panel->monitor);
305 }
306 
save_panels(void)307 static void save_panels(void)
308 {
309 	char *filename = choices_find_xdg_path_save("panels",
310 				"ROX-Filer", "rox.sourceforge.net", TRUE);
311 	char *tmp = g_strconcat(filename, ".new", NULL);
312 	FILE *fp = fopen(tmp, "w");
313 
314 	if (fp)
315 	{
316 		PanelSide n;
317 
318 		for (n = 0; n < PANEL_NUMBER_OF_SIDES; ++n)
319 		{
320 			if (current_panel[n])
321 				fprintf(fp, "%s\n", current_panel[n]->name);
322 		}
323 		fclose(fp);
324 		if (rename(tmp, filename))
325 			g_critical(_("Unable to replace '%s'"), filename);
326 	}
327 	else
328 	{
329 		g_critical(_("Unable to save '%s'"), tmp);
330 	}
331 	g_free(tmp);
332 	g_free(filename);
333 }
334 
335 /* 'name' may be NULL or "" to remove the panel */
panel_new(const gchar * name,PanelSide side)336 Panel *panel_new(const gchar *name, PanelSide side)
337 {
338 	guchar	*load_path;
339 	Panel	*panel;
340 	GtkWidget *vp, *box, *frame, *align;
341 	xmlDocPtr panel_doc = NULL;
342 	gboolean need_resave = FALSE;
343 
344 	g_return_val_if_fail(side == PANEL_DEFAULT_SIDE ||
345 			(side >= 0 && side < PANEL_NUMBER_OF_SIDES), NULL);
346 	g_return_val_if_fail(loading_panel == NULL, NULL);
347 
348 	if (name && *name == '\0')
349 		name = NULL;
350 
351 	if (!name)
352 		load_path = NULL;
353 	else if (strchr(name, '/'))
354 		load_path = g_strdup(name);
355 	else
356 	{
357 		guchar	*leaf;
358 
359 		leaf = g_strconcat("pan_", name, NULL);
360 		load_path = choices_find_xdg_path_load(leaf, PROJECT, SITE);
361 		g_free(leaf);
362 	}
363 
364 	if (load_path && access(load_path, F_OK) == 0)
365 	{
366 		char *saved_side;
367 		xmlNodePtr root;
368 
369 		panel_doc = xmlParseFile(load_path);
370 		root = xmlDocGetRootElement(panel_doc);
371 
372 		saved_side = xmlGetProp(root, "side");
373 		if (saved_side)
374 		{
375 			PanelSide old_side;
376 			old_side = panel_name_to_side(saved_side);
377 			g_free(saved_side);
378 
379 			if (side == PANEL_DEFAULT_SIDE)
380 				side = old_side;
381 			else if (side != old_side)
382 				need_resave = TRUE;
383 		}
384 		else
385 			need_resave = TRUE;
386 	}
387 
388 	if (side == PANEL_DEFAULT_SIDE)
389 		side = find_free_side();
390 
391 	if (current_panel[side])
392 	{
393 		if (name)
394 			number_of_windows++;
395 		closing_panel++;
396 		gtk_widget_destroy(current_panel[side]->window);
397 		closing_panel--;
398 		if (name)
399 			number_of_windows--;
400 	}
401 
402 	if (name == NULL || *name == '\0')
403 	{
404 		save_panels();
405 		return NULL;
406 	}
407 
408 	panel = g_new(Panel, 1);
409 	panel->name = g_strdup(name);
410 	panel->side = side;
411 	panel->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
412 	panel->autoscroll_speed = 0;
413 
414 	/* These are fallbacks from legacy global options */
415 	panel->style = o_panel_style.int_value;
416 	panel->width = o_panel_width.int_value;
417 	panel->xinerama = o_panel_xinerama.int_value;
418 	panel->monitor = o_panel_monitor.int_value;
419 	panel->avoid = o_panel_avoid.int_value;
420 
421 	/* Now try to load options from this panel's XML */
422 	if (panel_doc)
423 	{
424 		panel_load_options_from_xml(panel, panel_doc);
425 	}
426 	else
427 	{
428 		/* Otherwise ensure old settings are migrated */
429 		need_resave = TRUE;
430 	}
431 
432 	panel_update_geometry(panel);
433 
434 	gtk_window_set_resizable(GTK_WINDOW(panel->window), FALSE);
435 	gtk_window_set_wmclass(GTK_WINDOW(panel->window), "ROX-Panel", PROJECT);
436 	gtk_widget_set_name(panel->window, "rox-panel");
437 	gtk_widget_set_events(panel->window,
438 			GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
439 			GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
440 
441 	/* We make the panel a drop target only so that we can auto-raise! */
442 	gtk_drag_dest_set(panel->window, 0, NULL, 0, GDK_ACTION_PRIVATE);
443 	g_signal_connect(panel->window, "drag_leave",
444 			G_CALLBACK(panel_drag_leave), panel);
445 	g_signal_connect(panel->window, "drag_motion",
446 			G_CALLBACK(panel_drag_motion), panel);
447 
448 	g_signal_connect(panel->window, "delete-event",
449 			G_CALLBACK(panel_delete), panel);
450 	g_signal_connect(panel->window, "destroy",
451 			G_CALLBACK(panel_destroyed), panel);
452 	g_signal_connect(panel->window, "button_press_event",
453 			G_CALLBACK(panel_button_press), panel);
454 	g_signal_connect(panel->window, "button_release_event",
455 			G_CALLBACK(panel_button_release), panel);
456 	g_signal_connect(panel->window, "motion-notify-event",
457 			G_CALLBACK(panel_motion_event), panel);
458 	g_signal_connect(panel->window, "leave-notify-event",
459 			G_CALLBACK(panel_leave_event), panel);
460 
461 	if (panel->side == PANEL_RIGHT)
462 		align = gtk_alignment_new(1.0, 0.0, 0.0, 1.0);
463 	else if (panel->side == PANEL_BOTTOM)
464 		align = gtk_alignment_new(0.0, 1.0, 1.0, 0.0);
465 	else if (panel->side == PANEL_TOP)
466 		align = gtk_alignment_new(0.0, 0.0, 1.0, 0.0);
467 	else
468 		align = gtk_alignment_new(0.0, 0.0, 0.0, 1.0);
469 
470 	gtk_container_add(GTK_CONTAINER(panel->window), align);
471 
472 	vp = gtk_viewport_new(NULL, NULL);
473 	gtk_container_set_resize_mode(GTK_CONTAINER(vp), GTK_RESIZE_PARENT);
474 	gtk_viewport_set_shadow_type(GTK_VIEWPORT(vp), GTK_SHADOW_NONE);
475 	gtk_container_add(GTK_CONTAINER(align), vp);
476 
477 	g_signal_connect(align, "expose-event",
478 			 G_CALLBACK(draw_panel_edge), panel);
479 
480 	if (side == PANEL_TOP || side == PANEL_BOTTOM)
481 	{
482 		panel->adj = gtk_viewport_get_hadjustment(GTK_VIEWPORT(vp));
483 		box = gtk_hbox_new(FALSE, 0);
484 		panel->before = gtk_hbox_new(FALSE, 0);
485 		panel->after = gtk_hbox_new(FALSE, 0);
486 	}
487 	else
488 	{
489 		panel->adj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(vp));
490 		box = gtk_vbox_new(FALSE, 0);
491 		panel->before = gtk_vbox_new(FALSE, 0);
492 		panel->after = gtk_vbox_new(FALSE, 0);
493 	}
494 
495 	gtk_container_add(GTK_CONTAINER(vp), box);
496 	gtk_box_pack_start(GTK_BOX(box), panel->before, FALSE, TRUE, 0);
497 	gtk_box_pack_end(GTK_BOX(box), panel->after, FALSE, TRUE, 0);
498 
499 	frame = make_insert_frame(panel);
500 	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
501 
502 	/* This is used so that we can find the middle easily! */
503 	panel->gap = gtk_event_box_new();
504 	gtk_box_pack_start(GTK_BOX(box), panel->gap, FALSE, FALSE, 0);
505 
506 	frame = make_insert_frame(panel);
507 	g_object_set_data(G_OBJECT(frame), "after", "yes");
508 	gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 4);
509 
510 	if (o_panel_is_dock.int_value)
511 		gtk_window_set_type_hint(GTK_WINDOW(panel->window),
512 				GDK_WINDOW_TYPE_HINT_DOCK);
513 
514 	gtk_widget_realize(panel->window);
515 	make_panel_window(panel->window);
516 	gtk_window_stick(GTK_WINDOW(panel->window));
517 
518 	gtk_widget_show_all(align);
519 
520 	loading_panel = panel;
521 	if (panel_doc)
522 	{
523 		panel_load_from_xml(panel, panel_doc);
524 		xmlFreeDoc(panel_doc);
525 
526 		if (need_resave)
527 			panel_save(panel);
528 	}
529 	else if (load_path)
530 	{
531 		parse_file(load_path, pan_from_file);
532 		info_message(_("Your old panel file has been "
533 					"converted to the new XML format."));
534 		panel_save(panel);
535 	}
536 	else
537 	{
538 		/* Don't scare users with an empty panel... */
539 		guchar *apps;
540 
541 		panel_add_item(panel, "~", "Home", FALSE, NULL, NULL, FALSE);
542 
543 		apps = pathdup(make_path(app_dir, ".."));
544 		if (apps)
545 		{
546 			panel_add_item(panel, apps, "Apps", FALSE, NULL, NULL, FALSE);
547 			g_free(apps);
548 		}
549 	}
550 	loading_panel = NULL;
551 	g_free(load_path);
552 
553 	current_panel[side] = panel;
554 
555 	gtk_widget_queue_resize(box);
556 	g_signal_connect(panel->window, "size-request",
557 			G_CALLBACK(panel_post_resize), panel);
558 	g_signal_connect(panel->window, "size-allocate",
559 			G_CALLBACK(reposition_panel), panel);
560 
561 	number_of_windows++;
562 	gdk_window_lower(panel->window->window);
563 	gtk_widget_show(panel->window);
564 	/* This has no effect until after window is showing; GTK+ bug? */
565 	if (panel_keep_below(panel, TRUE))
566 	{
567 		GdkWindow *pinboard;
568 
569 		pinboard = pinboard_get_window();
570 		/* (if pinboard is NULL, will go right to the back) */
571 		window_put_just_above(panel->window->window, pinboard);
572 	}
573 
574 	save_panels();
575 
576 	return panel;
577 }
578 
579 /* Externally visible function to add an item to a panel */
panel_add(PanelSide side,const gchar * path,const gchar * label,gboolean after,const gchar * shortcut,const gchar * args,gboolean locked)580 gboolean panel_add(PanelSide side,
581 		   const gchar *path, const gchar *label, gboolean after, const gchar *shortcut, const gchar *args,
582 		   gboolean locked)
583 {
584 	g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
585 
586 	g_return_val_if_fail(current_panel[side] != NULL, FALSE);
587 
588 	panel_add_item(current_panel[side], path, label, after, shortcut, args, locked);
589 
590 	return TRUE;
591 }
592 
593 /* Add the area covered by the panels to the region */
panel_mark_used(GdkRegion * used)594 void panel_mark_used(GdkRegion *used)
595 {
596 	int i;
597 
598 	for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
599 	{
600 		Panel *panel = current_panel[i];
601 		GdkRectangle rect;
602 
603 		if (!panel)
604 			continue;
605 
606 		gdk_window_get_root_origin(panel->window->window,
607 					   &rect.x, &rect.y);
608 		rect.width = panel->window->allocation.width;
609 		rect.height = panel->window->allocation.height;
610 
611 		gdk_region_union_with_rect(used, &rect);
612 	}
613 }
614 
615 /* On xrandr screen size changes, update all panels */
panel_update_size(void)616 void panel_update_size(void)
617 {
618 	int i;
619 
620 	for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
621 	{
622 		if (current_panel[i])
623 		{
624 			panel_update_geometry(current_panel[i]);
625 			reposition_panel(current_panel[i]->window,
626 					&current_panel[i]->window->allocation,
627 					current_panel[i]);
628 			gtk_widget_queue_resize(current_panel[i]->window);
629 		}
630 	}
631 }
632 
633 /****************************************************************
634  *			INTERNAL FUNCTIONS			*
635  ****************************************************************/
636 
637 /* User has tried to close the panel via the window manager - confirm */
panel_delete(GtkWidget * widget,GdkEvent * event,Panel * panel)638 static int panel_delete(GtkWidget *widget, GdkEvent *event, Panel *panel)
639 {
640 	return !confirm(_("You have tried to close a panel via the window "
641 			 "manager - I usually find that this is accidental... "
642 			 "really close?"),
643 			 GTK_STOCK_CLOSE, NULL);
644 }
645 
panel_destroyed(GtkWidget * widget,Panel * panel)646 static void panel_destroyed(GtkWidget *widget, Panel *panel)
647 {
648 	if (panel_options_dialog)
649 	{
650 		Panel *dlg_panel = g_object_get_data(G_OBJECT(panel_options_dialog),
651 					 "rox-panel");
652 
653 		if (dlg_panel == panel)
654 			gtk_widget_destroy(panel_options_dialog);
655 	}
656 
657 	if (current_panel[panel->side] == panel)
658 		current_panel[panel->side] = NULL;
659 
660 	if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
661 	{
662 		if (current_panel[PANEL_RIGHT])
663 			gtk_widget_queue_resize(
664 					current_panel[PANEL_RIGHT]->window);
665 		if (current_panel[PANEL_LEFT])
666 			gtk_widget_queue_resize(
667 					current_panel[PANEL_LEFT]->window);
668 	}
669 
670 	if (panel->autoscroll_speed)
671 		g_source_remove(panel->autoscroll_to);
672 
673 	g_free(panel->name);
674 	g_free(panel);
675 
676 	one_less_window();
677 }
678 
panel_load_side(Panel * panel,xmlNodePtr side,gboolean after)679 static void panel_load_side(Panel *panel, xmlNodePtr side, gboolean after)
680 {
681 	xmlNodePtr node;
682 	char	   *label, *path, *shortcut, *args, *tmp;
683 	gboolean locked;
684 
685 	for (node = side->xmlChildrenNode; node; node = node->next)
686 	{
687 		if (node->type != XML_ELEMENT_NODE)
688 			continue;
689 		if (strcmp(node->name, "icon") != 0)
690 			continue;
691 
692 		label = xmlGetProp(node, "label");
693 		if (!label)
694 			label = g_strdup("<missing label>");
695 		path = xmlNodeGetContent(node);
696 		if (!path)
697 			path = g_strdup("<missing path>");
698 		shortcut = xmlGetProp(node, "shortcut");
699 		args = xmlGetProp(node, "args");
700 		tmp = xmlGetProp(node, "locked");
701 		if (tmp)
702 		{
703 			locked = text_to_boolean(tmp, FALSE);
704 			g_free(tmp);
705 		}
706 		else
707 			locked = FALSE;
708 
709 		panel_add_item(panel, path, label, after, shortcut, args, locked);
710 
711 		g_free(path);
712 		g_free(label);
713 		g_free(shortcut);
714 		g_free(args);
715 	}
716 }
717 
718 /* Create one panel icon for each icon in the doc */
panel_load_from_xml(Panel * panel,xmlDocPtr doc)719 static void panel_load_from_xml(Panel *panel, xmlDocPtr doc)
720 {
721 	xmlNodePtr root;
722 
723 	root = xmlDocGetRootElement(doc);
724 	panel_load_side(panel, get_subnode(root, NULL, "start"), FALSE);
725 	panel_load_side(panel, get_subnode(root, NULL, "end"), TRUE);
726 }
727 
728 /* Called for each line in the config file while loading a new panel */
pan_from_file(gchar * line)729 static const char *pan_from_file(gchar *line)
730 {
731 	gchar	*sep, *leaf;
732 
733 	g_return_val_if_fail(line != NULL, NULL);
734 	g_return_val_if_fail(loading_panel != NULL, NULL);
735 
736 	if (*line == '\0')
737 		return NULL;
738 
739 	sep = strpbrk(line, "<>");
740 	if (!sep)
741 		return _("Missing < or > in panel config file");
742 
743 	if (sep != line)
744 		leaf = g_strndup(line, sep - line);
745 	else
746 		leaf = NULL;
747 
748 	panel_add_item(loading_panel, sep + 1, leaf, sep[0] == '>',
749 		       NULL, NULL, FALSE);
750 
751 	g_free(leaf);
752 
753 	return NULL;
754 }
755 
icon_pointer_in(GtkWidget * widget,GdkEventCrossing * event,Icon * icon)756 static gboolean icon_pointer_in(GtkWidget *widget,
757 				GdkEventCrossing *event,
758 				Icon *icon)
759 {
760 	gtk_widget_set_state(widget,
761 		icon->selected ? GTK_STATE_SELECTED : GTK_STATE_PRELIGHT);
762 
763 	return 0;
764 }
765 
icon_pointer_out(GtkWidget * widget,GdkEventCrossing * event,Icon * icon)766 static gboolean icon_pointer_out(GtkWidget *widget,
767 				 GdkEventCrossing *event,
768 				 Icon *icon)
769 {
770 	gtk_widget_set_state(widget,
771 		icon->selected ? GTK_STATE_SELECTED : GTK_STATE_NORMAL);
772 
773 	return 0;
774 }
775 
panel_icon_destroyed(PanelIcon * pi)776 static void panel_icon_destroyed(PanelIcon *pi)
777 {
778 	g_return_if_fail(pi->widget != NULL);
779 
780 	pi->widget = NULL;
781 
782 	g_object_unref(pi);
783 }
784 
785 /* Set the tooltip AND hide/show the label */
panel_icon_set_tip(PanelIcon * pi)786 static void panel_icon_set_tip(PanelIcon *pi)
787 {
788 	XMLwrapper	*ai;
789 	xmlNode 	*node;
790 	Icon		*icon = (Icon *) pi;
791 
792 	g_return_if_fail(pi != NULL);
793 
794 	if (pi->label)
795 	{
796 		if (panel_want_show_text(pi))
797 			gtk_widget_show(pi->label);
798 		else
799 			gtk_widget_hide(pi->label);
800 	}
801 
802 	if (pi->socket)
803 		ai = NULL;
804 	else
805 		ai = appinfo_get(icon->path, icon->item);
806 
807 	if (ai && ((node = xml_get_section(ai, NULL, "Summary"))))
808 	{
809 		guchar *str;
810 		str = xmlNodeListGetString(node->doc,
811 				node->xmlChildrenNode, 1);
812 		if (str)
813 		{
814 			gtk_tooltips_set_tip(tooltips, pi->widget, str, NULL);
815 			g_free(str);
816 		}
817 	}
818 	else if ((!panel_want_show_text(pi)) && !pi->socket)
819 	{
820 		if (icon->item->leafname && icon->item->leafname[0])
821 			gtk_tooltips_set_tip(tooltips, pi->widget,
822 					icon->item->leafname, NULL);
823 	}
824 	else
825 		gtk_tooltips_set_tip(tooltips, pi->widget, NULL, NULL);
826 
827 	if (ai)
828 		g_object_unref(ai);
829 }
830 
831 /* Add an icon with this path to the panel. If after is TRUE then the
832  * icon is added to the right/bottom end of the panel.
833  *
834  * If name is NULL a suitable name is taken from path.
835  */
panel_add_item(Panel * panel,const gchar * path,const gchar * name,gboolean after,const gchar * shortcut,const gchar * args,gboolean locked)836 static void panel_add_item(Panel *panel,
837 			   const gchar *path,
838 			   const gchar *name,
839 			   gboolean after,
840 			   const gchar *shortcut,
841 			   const gchar *args,
842 			   gboolean locked)
843 {
844 	GtkWidget	*widget;
845 	PanelIcon	*pi;
846 	Icon		*icon;
847 
848 	g_return_if_fail(panel != NULL);
849 	g_return_if_fail(path != NULL);
850 
851 	widget = gtk_event_box_new();
852 	gtk_widget_set_events(widget,
853 			GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK |
854 			GDK_BUTTON3_MOTION_MASK |
855 			GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK |
856 			GDK_BUTTON_RELEASE_MASK);
857 
858 	gtk_box_pack_start(GTK_BOX(after ? panel->after : panel->before),
859 			widget, FALSE, TRUE, 0);
860 	if (after)
861 		gtk_box_reorder_child(GTK_BOX(panel->after), widget, 0);
862 
863 	gtk_widget_realize(widget);
864 
865 	pi = panel_icon_new(panel, path, name);
866 	icon = (Icon *) pi;
867 
868 	/* Widget takes the initial ref of Icon */
869 	g_object_set_data(G_OBJECT(widget), "icon", pi);
870 
871 	pi->widget = widget;
872 	g_object_ref(widget);
873 
874 	gtk_widget_set_name(pi->widget, "panel-icon");
875 
876 	g_signal_connect_swapped(widget, "destroy",
877 			  G_CALLBACK(panel_icon_destroyed), pi);
878 
879 	if (icon->item->base_type == TYPE_DIRECTORY)
880 		run_applet(pi);
881 
882 	g_signal_connect(widget, "button_release_event",
883 			G_CALLBACK(icon_button_release), pi);
884 	g_signal_connect(widget, "button_press_event",
885 			G_CALLBACK(icon_button_press), pi);
886 	g_signal_connect(widget, "motion-notify-event",
887 			G_CALLBACK(icon_motion_event), pi);
888 	g_signal_connect(widget, "enter-notify-event",
889 			G_CALLBACK(icon_pointer_in), pi);
890 	g_signal_connect(widget, "leave-notify-event",
891 			G_CALLBACK(icon_pointer_out), pi);
892 
893 	if (!pi->socket)
894 	{
895 		g_signal_connect(widget, "enter-notify-event",
896 				G_CALLBACK(enter_icon), pi);
897 		g_signal_connect_after(widget, "expose_event",
898 				G_CALLBACK(expose_icon), pi);
899 		g_signal_connect(widget, "drag_data_get",
900 				G_CALLBACK(drag_data_get), NULL);
901 
902 		g_signal_connect(widget, "size_request",
903 				G_CALLBACK(size_request), pi);
904 
905 		drag_set_panel_dest(pi);
906 
907 		pi->label = gtk_label_new(icon->item->leafname);
908 		gtk_container_add(GTK_CONTAINER(pi->widget), pi->label);
909 		gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
910 		gtk_misc_set_padding(GTK_MISC(pi->label), 1, 2);
911 	}
912 
913 	icon_set_shortcut(icon, shortcut);
914 	icon_set_arguments(icon, args);
915 	icon->locked = locked;
916 
917 	if (!loading_panel)
918 		panel_save(panel);
919 
920 	panel_icon_set_tip(pi);
921 	gtk_widget_show(widget);
922 }
923 
remove_item_from_side(GtkWidget * container,const gchar * path,const gchar * label)924 static gboolean remove_item_from_side(GtkWidget *container, const gchar *path,
925 				      const gchar *label)
926 {
927 	GList *kids, *next;
928 	gboolean found = FALSE;
929 
930 	kids = gtk_container_get_children(GTK_CONTAINER(container));
931 
932 	for (next = kids; next; next = next->next)
933 	{
934 		Icon    *icon;
935 		icon = g_object_get_data(G_OBJECT(next->data), "icon");
936 		if (!icon)
937 			continue;
938 
939 		if ((!path || strcmp(path, icon->src_path) == 0) &&
940 		    (!label || strcmp(label, icon->item->leafname)==0))
941 		{
942 			icon->locked = FALSE;
943 			icon_destroy(icon);
944 			found = TRUE;
945 			break;
946 		}
947 	}
948 
949 	g_list_free(kids);
950 
951 	return found;
952 }
953 
954 /* Remove an item with this path. If more than one item matches, only
955  * one is removed. If label is not NULL then it must also match the item.
956  * Returns TRUE if an item was successfully removed.
957  */
panel_remove_item(PanelSide side,const gchar * path,const gchar * label)958 gboolean panel_remove_item(PanelSide side, const gchar *path,
959 			   const gchar *label)
960 {
961 	Panel *panel;
962 
963 	g_return_val_if_fail(side >= 0 && side < PANEL_NUMBER_OF_SIDES, FALSE);
964 
965 	g_return_val_if_fail(path != NULL || label != NULL, FALSE);
966 
967 	panel = current_panel[side];
968 	if (!panel)
969 	{
970 		g_warning("No panel on this side of the screen!");
971 		return FALSE;
972 	}
973 
974 	if (remove_item_from_side(panel->before, path, label) ||
975 	    remove_item_from_side(panel->after, path, label))
976 	{
977 		panel_save(panel);
978 		panel_update(panel);
979 		return TRUE;
980 	}
981 
982 	g_warning("Panel item path='%s', label='%s' not found", path, label);
983 	return FALSE;
984 }
985 
986 /* Called when Gtk+ wants to know how much space an icon needs.
987  * 'req' is already big enough for the label, if shown.
988  */
size_request(GtkWidget * widget,GtkRequisition * req,PanelIcon * pi)989 static void size_request(GtkWidget *widget, GtkRequisition *req, PanelIcon *pi)
990 {
991 	Icon *icon = (Icon *) pi;
992 	gboolean horz = (pi->panel->side == PANEL_TOP ||
993 			 pi->panel->side == PANEL_BOTTOM);
994 	int max_width = 100;
995 	int max_height = 100;
996 	int image_width, image_height;
997 	Panel *panel = pi->panel;
998 
999 	if (horz)
1000 		max_height = panel->width - req->height;
1001 	else
1002 		max_width = MAX(panel->width, req->width);
1003 
1004 	/* TODO: really need to recreate? */
1005 	if (pi->image)
1006 		g_object_unref(pi->image);
1007 
1008 	pi->image = scale_pixbuf(di_image(icon->item)->src_pixbuf,
1009 			MAX(20, max_width), MAX(20, max_height));
1010 
1011 	image_width = gdk_pixbuf_get_width(pi->image);
1012 	image_height = gdk_pixbuf_get_height(pi->image);
1013 
1014 	if (req->height > 0 && max_height < req->height)
1015 	{
1016 		pi->style = TEXT_BESIDE_ICON;
1017 		req->width += image_width;
1018 		req->height = MAX(req->height, image_height);
1019 		gtk_misc_set_alignment(GTK_MISC(pi->label), 1, 0.5);
1020 	}
1021 	else
1022 	{
1023 		pi->style = TEXT_UNDER_ICON;
1024 		req->width = MAX(req->width, image_width);
1025 		req->height += image_height;
1026 		gtk_misc_set_alignment(GTK_MISC(pi->label), 0.5, 1);
1027 	}
1028 
1029 	if (horz)
1030 		req->width += PANEL_ICON_SPACING;
1031 	else
1032 		req->height += PANEL_ICON_SPACING;
1033 }
1034 
expose_icon(GtkWidget * widget,GdkEventExpose * event,PanelIcon * pi)1035 static gint expose_icon(GtkWidget *widget,
1036 			GdkEventExpose *event,
1037 			PanelIcon *pi)
1038 {
1039 	return draw_icon(widget, &event->area, pi);
1040 }
1041 
draw_icon(GtkWidget * widget,GdkRectangle * badarea,PanelIcon * pi)1042 static gint draw_icon(GtkWidget *widget, GdkRectangle *badarea, PanelIcon *pi)
1043 {
1044 	GdkRectangle	area;
1045 	int		width, height;
1046 	Icon		*icon = (Icon *) pi;
1047 	int		image_x;
1048 	int		image_y;
1049 	GdkPixbuf	*image;
1050 	int		text_height = 0;
1051 
1052 	gdk_drawable_get_size(widget->window, &area.width, &area.height);
1053 
1054 	if (panel_want_show_text(pi))
1055 		text_height = pi->label->requisition.height;
1056 
1057 	g_return_val_if_fail(pi->image != NULL, FALSE);
1058 
1059 	image = pi->image;
1060 
1061 	width = gdk_pixbuf_get_width(image);
1062 	height = gdk_pixbuf_get_height(image);
1063 
1064 	if (pi->style == TEXT_UNDER_ICON)
1065 	{
1066 		image_x = (area.width - width) >> 1;
1067 		image_y = (area.height - height - text_height) >> 1;
1068 	}
1069 	else
1070 	{
1071 		image_x = PANEL_ICON_SPACING - 2;
1072 		image_y = (area.height - height) >> 1;
1073 	}
1074 
1075 	gdk_pixbuf_render_to_drawable_alpha(
1076 			image,
1077 			widget->window,
1078 			0, 0, 				/* src */
1079 			image_x, image_y,		/* dest */
1080 			width, height,
1081 			GDK_PIXBUF_ALPHA_FULL, 128,	/* (unused) */
1082 			GDK_RGB_DITHER_NORMAL, 0, 0);
1083 
1084 	if (icon->item->flags & ITEM_FLAG_SYMLINK)
1085 	{
1086 		draw_emblem_on_icon(widget->window, widget->style,
1087 				    ROX_STOCK_SYMLINK, &image_x, image_y+2);
1088 	}
1089 	if (icon->item->flags & ITEM_FLAG_MOUNT_POINT)
1090 	{
1091 		draw_emblem_on_icon(widget->window, widget->style,
1092 				    icon->item->flags & ITEM_FLAG_MOUNTED
1093 				    ? ROX_STOCK_MOUNTED
1094 				    : ROX_STOCK_MOUNT,
1095 				    &image_x, image_y+2);
1096 	}
1097 	return FALSE;
1098 }
1099 
panel_icon_wink(Icon * icon)1100 static void panel_icon_wink(Icon *icon)
1101 {
1102 	PanelIcon *pi = (PanelIcon *) icon;
1103 
1104 	wink_widget(pi->widget);
1105 }
1106 
1107 /* icon may be NULL if the event is on the background */
perform_action(Panel * panel,PanelIcon * pi,GdkEventButton * event)1108 static void perform_action(Panel *panel, PanelIcon *pi, GdkEventButton *event)
1109 {
1110 	BindAction	action;
1111 	Icon		*icon = (Icon *) pi;
1112 
1113 	action = bind_lookup_bev(icon ? BIND_PANEL_ICON : BIND_PANEL, event);
1114 
1115 	if (pi && pi->socket)
1116 		if (action != ACT_POPUP_MENU && action != ACT_MOVE_ICON)
1117 			return;
1118 
1119 	switch (action)
1120 	{
1121 		case ACT_OPEN_ITEM:
1122 			dnd_motion_ungrab();
1123 			wink_widget(pi->widget);
1124 			icon_run(icon);
1125 			break;
1126 		case ACT_EDIT_ITEM:
1127 			dnd_motion_ungrab();
1128 			wink_widget(pi->widget);
1129 			run_diritem(icon->path, icon->item, NULL, NULL, TRUE);
1130 			break;
1131 		case ACT_POPUP_MENU:
1132 			dnd_motion_ungrab();
1133 			panel_show_menu(event, pi, panel);
1134 			break;
1135 		case ACT_MOVE_ICON:
1136 			dnd_motion_start(MOTION_REPOSITION);
1137 			break;
1138 		case ACT_PRIME_AND_SELECT:
1139 			if (!icon->selected)
1140 				icon_select_only(icon);
1141 			dnd_motion_start(MOTION_READY_FOR_DND);
1142 			break;
1143 		case ACT_PRIME_AND_TOGGLE:
1144 			icon_set_selected(icon, !icon->selected);
1145 			dnd_motion_start(MOTION_READY_FOR_DND);
1146 			break;
1147 		case ACT_PRIME_FOR_DND:
1148 			dnd_motion_start(MOTION_READY_FOR_DND);
1149 			break;
1150 		case ACT_TOGGLE_SELECTED:
1151 			icon_set_selected(icon, !icon->selected);
1152 			break;
1153 		case ACT_SELECT_EXCL:
1154 			icon_set_selected(icon, TRUE);
1155 			break;
1156 		case ACT_IGNORE:
1157 			break;
1158 		case ACT_CLEAR_SELECTION:
1159 			dnd_motion_ungrab();
1160 			icon_select_only(NULL);
1161 			break;
1162 		default:
1163 			g_warning("Unsupported action : %d\n", action);
1164 			break;
1165 	}
1166 }
1167 
panel_button_release(GtkWidget * widget,GdkEventButton * event,Panel * panel)1168 static gint panel_button_release(GtkWidget *widget,
1169 			      GdkEventButton *event,
1170 			      Panel *panel)
1171 {
1172 	if (dnd_motion_release(event))
1173 		return TRUE;
1174 
1175 	perform_action(panel, NULL, event);
1176 
1177 	return TRUE;
1178 }
1179 
panel_button_press(GtkWidget * widget,GdkEventButton * event,Panel * panel)1180 static gint panel_button_press(GtkWidget *widget,
1181 			      GdkEventButton *event,
1182 			      Panel *panel)
1183 {
1184 	if (dnd_motion_press(panel->window, event))
1185 		perform_action(panel, NULL, event);
1186 
1187 	return TRUE;
1188 }
1189 
icon_button_release(GtkWidget * widget,GdkEventButton * event,PanelIcon * pi)1190 static gint icon_button_release(GtkWidget *widget,
1191 				GdkEventButton *event,
1192 				PanelIcon *pi)
1193 {
1194 	if (pi->socket && event->button == 1)
1195 		return FALSE;	/* Restart button */
1196 
1197 	if (dnd_motion_release(event))
1198 		return TRUE;
1199 
1200 	perform_action(pi->panel, pi, event);
1201 
1202 	return TRUE;
1203 }
1204 
icon_button_press(GtkWidget * widget,GdkEventButton * event,PanelIcon * pi)1205 static gint icon_button_press(GtkWidget *widget,
1206 			      GdkEventButton *event,
1207 			      PanelIcon *pi)
1208 {
1209 	if (pi->socket && event->button == 1)
1210 		return FALSE;	/* Restart button */
1211 
1212 	if (dnd_motion_press(widget, event))
1213 		perform_action(pi->panel, pi, event);
1214 
1215 	return TRUE;
1216 }
1217 
1218 /* Stop windows from maximising over all/part of us */
panel_setup_struts(Panel * panel,GtkAllocation * alloc)1219 static void panel_setup_struts(Panel *panel, GtkAllocation *alloc)
1220 {
1221 	int thickness;
1222 	struct {
1223 		gulong left, right, top, bottom;
1224 		gulong left_start_y, left_end_y;
1225 		gulong right_start_y, right_end_y;
1226 		gulong top_start_x, top_end_x;
1227 		gulong bottom_start_x, bottom_end_x;
1228 	} strut = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1229 
1230 	if (panel->avoid == FALSE)
1231 		thickness = 2;
1232 	else if (panel->side == PANEL_TOP ||
1233 		 panel->side == PANEL_BOTTOM)
1234 		thickness = alloc->height;
1235 	else
1236 		thickness = alloc->width;
1237 
1238 	switch (panel->side)
1239 	{
1240 		case PANEL_LEFT:
1241 			if (!panel->xinerama || !monitor_adjacent[panel->monitor].left)
1242 			{
1243 				strut.left = panel->geometry.x + thickness;
1244 				strut.left_start_y = panel->geometry.y;
1245 				strut.left_end_y = panel->geometry.y +
1246 					panel->geometry.height - 1;
1247 			}
1248 			/* else there is (part of) a monitor
1249 			 * to the left */
1250 			else
1251 			{
1252 				thickness = 0;
1253 			}
1254 			break;
1255 		case PANEL_RIGHT:
1256 			if (!panel->xinerama || !monitor_adjacent[panel->monitor].right)
1257 			{
1258 				/* RHS of monitor might not abut edge
1259 				 * of total virtual screen */
1260 				strut.right = screen_width -
1261 					panel->geometry.x -
1262 					panel->geometry.width +
1263 					thickness;
1264 				strut.right_start_y = panel->geometry.y;
1265 				strut.right_end_y = panel->geometry.y +
1266 					panel->geometry.height - 1;
1267 			}
1268 			/* else there is (part of) a monitor
1269 			 * to the right */
1270 			else
1271 			{
1272 				thickness = 0;
1273 			}
1274 			break;
1275 		case PANEL_TOP:
1276 			if (!panel->xinerama || !monitor_adjacent[panel->monitor].top)
1277 			{
1278 				strut.top = panel->geometry.y + thickness;
1279 				strut.top_start_x = panel->geometry.x;
1280 				strut.top_end_x = panel->geometry.x +
1281 					panel->geometry.width - 1;
1282 			}
1283 			/* else there is (part of) a monitor above */
1284 			else
1285 			{
1286 				thickness = 0;
1287 			}
1288 			break;
1289 		default:	/* PANEL_BOTTOM */
1290 			if (!panel->xinerama ||
1291 					!monitor_adjacent[panel->monitor].bottom)
1292 			{
1293 				/* Bottom of monitor might not abut
1294 				 * edge of total virtual screen */
1295 				strut.bottom = screen_height -
1296 					panel->geometry.y -
1297 					panel->geometry.height +
1298 					thickness;
1299 				strut.bottom_start_x = panel->geometry.x;
1300 				strut.bottom_end_x = panel->geometry.x +
1301 					panel->geometry.width - 1;
1302 			}
1303 			/* else there is (part of) a monitor below */
1304 			else
1305 			{
1306 				thickness = 0;
1307 			}
1308 			break;
1309 	}
1310 
1311 	if (thickness)
1312 	{
1313 		/* Set full-width strut as well as partial in case
1314 		 * partial isn't supported by wm */
1315 		gdk_property_change(panel->window->window,
1316 				gdk_atom_intern("_NET_WM_STRUT",
1317 					FALSE),
1318 				gdk_atom_intern("CARDINAL", FALSE),
1319 				32, GDK_PROP_MODE_REPLACE,
1320 				(gchar *) &strut, 4);
1321 		gdk_property_change(panel->window->window,
1322 				gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1323 					FALSE),
1324 				gdk_atom_intern("CARDINAL", FALSE),
1325 				32, GDK_PROP_MODE_REPLACE,
1326 				(gchar *) &strut, 12);
1327 	}
1328 	else
1329 	{
1330 		gdk_property_delete(panel->window->window,
1331 				gdk_atom_intern("_NET_WM_STRUT_PARTIAL",
1332 					FALSE));
1333 		gdk_property_delete(panel->window->window,
1334 				gdk_atom_intern("_NET_WM_STRUT",
1335 					FALSE));
1336 	}
1337 }
1338 
reposition_panel(GtkWidget * window,GtkAllocation * alloc,Panel * panel)1339 static void reposition_panel(GtkWidget *window,
1340 				GtkAllocation *alloc, Panel *panel)
1341 {
1342 	int		x = panel->geometry.x;
1343 	int		y = panel->geometry.y;
1344 	PanelSide	side = panel->side;
1345 
1346 	if (side == PANEL_LEFT || side == PANEL_RIGHT)
1347 	{
1348 		if (side == PANEL_RIGHT)
1349 			x += panel->geometry.width - alloc->width;
1350 
1351 		if (current_panel[PANEL_TOP])
1352 		{
1353 			GtkWidget *win = current_panel[PANEL_TOP]->window;
1354 			y += win->allocation.height;
1355 		}
1356 	}
1357 
1358 	if (side == PANEL_BOTTOM)
1359 		y += panel->geometry.height - alloc->height;
1360 
1361 	gtk_window_move(GTK_WINDOW(panel->window), x, y);
1362 	gdk_window_move(panel->window->window, x, y);
1363 
1364 	if (side == PANEL_BOTTOM || side == PANEL_TOP)
1365 	{
1366 		if (current_panel[PANEL_RIGHT])
1367 			gtk_widget_queue_resize(
1368 					current_panel[PANEL_RIGHT]->window);
1369 		if (current_panel[PANEL_LEFT])
1370 			gtk_widget_queue_resize(
1371 					current_panel[PANEL_LEFT]->window);
1372 	}
1373 
1374 	panel_setup_struts(panel, alloc);
1375 }
1376 
1377 /* Same as drag_set_dest(), but for panel icons */
drag_set_panel_dest(PanelIcon * pi)1378 static void drag_set_panel_dest(PanelIcon *pi)
1379 {
1380 	GtkWidget	*obj = pi->widget;
1381 
1382 	make_drop_target(pi->widget, 0);
1383 
1384 	g_signal_connect(obj, "drag_motion", G_CALLBACK(drag_motion), pi);
1385 	g_signal_connect(obj, "drag_leave", G_CALLBACK(drag_leave), pi);
1386 	g_signal_connect(obj, "drag_end", G_CALLBACK(drag_end), pi);
1387 }
1388 
drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,PanelIcon * pi)1389 static gboolean drag_motion(GtkWidget		*widget,
1390 			    GdkDragContext	*context,
1391 			    gint		x,
1392 			    gint		y,
1393 			    guint		time,
1394 			    PanelIcon		*pi)
1395 {
1396 	GdkDragAction	action = context->suggested_action;
1397 	const char	*type = NULL;
1398 	Icon		*icon = (Icon *) pi;
1399 	DirItem		*item = icon->item;
1400 	int		panel_x, panel_y;
1401 
1402 	gdk_window_get_pointer(pi->panel->window->window,
1403 				&panel_x, &panel_y, NULL);
1404 	motion_may_raise(pi->panel, panel_x, panel_y);
1405 
1406 	/* Should we scroll the panel when dragging? */
1407 	if (motion_state != MOTION_REPOSITION)
1408 		if (pi->panel->autoscroll_speed == 0)
1409 			may_autoscroll(pi->panel);
1410 
1411 	if (icon->selected)
1412 		goto out;	/* Can't drag a selection to itself */
1413 
1414 	type = dnd_motion_item(context, &item);
1415 
1416 	if ((context->actions & GDK_ACTION_ASK) && o_dnd_left_menu.int_value
1417 		&& type != drop_dest_prog)
1418 	{
1419 		guint state;
1420 		gdk_window_get_pointer(NULL, NULL, NULL, &state);
1421 		if (state & GDK_BUTTON1_MASK)
1422 			action = GDK_ACTION_ASK;
1423 	}
1424 
1425 	if (!item)
1426 		type = NULL;
1427 out:
1428 	/* We actually must pretend to accept the drop, even if the
1429 	 * directory isn't writeable, so that the spring-opening
1430 	 * thing works.
1431 	 */
1432 
1433 	/* Don't allow drops to non-writeable directories */
1434 	if (o_dnd_spring_open.int_value == FALSE &&
1435 			type == drop_dest_dir &&
1436 			access(icon->path, W_OK) != 0)
1437 	{
1438 		type = NULL;
1439 	}
1440 
1441 	g_dataset_set_data(context, "drop_dest_type", (gpointer) type);
1442 	if (type)
1443 	{
1444 		gdk_drag_status(context, action, time);
1445 		g_dataset_set_data_full(context, "drop_dest_path",
1446 				g_strdup(icon->path), g_free);
1447 		if (type == drop_dest_dir)
1448 			dnd_spring_load(context, NULL);
1449 
1450 		if (dnd_highlight && dnd_highlight != pi->widget)
1451 		{
1452 			gtk_drag_unhighlight(dnd_highlight);
1453 			dnd_highlight = NULL;
1454 		}
1455 
1456 		if (dnd_highlight == NULL)
1457 		{
1458 			gtk_drag_highlight(pi->widget);
1459 			dnd_highlight = pi->widget;
1460 		}
1461 	}
1462 
1463 	return type != NULL;
1464 }
1465 
1466 
add_uri_list(GtkWidget * widget,GdkDragContext * context,gint x,gint y,GtkSelectionData * selection_data,guint info,guint32 time,Panel * panel)1467 static void add_uri_list(GtkWidget          *widget,
1468 			 GdkDragContext     *context,
1469 			 gint               x,
1470 			 gint               y,
1471 			 GtkSelectionData   *selection_data,
1472 			 guint              info,
1473 			 guint32            time,
1474 			 Panel		    *panel)
1475 {
1476 	gboolean after = FALSE;
1477 	GList *uris, *next;
1478 
1479 	if (!selection_data->data)
1480 		return;
1481 
1482 	g_return_if_fail(selection_data->data[selection_data->length] == '\0');
1483 
1484 	if (g_object_get_data(G_OBJECT(widget), "after"))
1485 		after = TRUE;
1486 
1487 	uris = uri_list_to_glist(selection_data->data);
1488 
1489 	for (next = uris; next; next = next->next)
1490 	{
1491 		guchar	*path;
1492 
1493 		path = get_local_path((EscapedPath *) next->data);
1494 
1495 		if (path) {
1496 			panel_add_item(panel, path, NULL, after, NULL, NULL, FALSE);
1497 			g_free(path);
1498 		}
1499 	}
1500 
1501 	g_list_free(uris);
1502 }
1503 
drag_end(GtkWidget * widget,GdkDragContext * context,Icon * icon)1504 static void drag_end(GtkWidget *widget,
1505 		     GdkDragContext *context,
1506 		     Icon *icon)
1507 {
1508 	if (tmp_icon_selected)
1509 	{
1510 		icon_select_only(NULL);
1511 		tmp_icon_selected = FALSE;
1512 	}
1513 }
1514 
drag_leave(GtkWidget * widget,GdkDragContext * context,guint32 time,Icon * icon)1515 static void drag_leave(GtkWidget	*widget,
1516 		       GdkDragContext	*context,
1517 		       guint32		time,
1518 		       Icon	*icon)
1519 {
1520 	panel_drag_leave(widget, context, time, ((PanelIcon *) icon)->panel);
1521 
1522 	if (dnd_highlight && dnd_highlight == widget)
1523 	{
1524 		gtk_drag_unhighlight(dnd_highlight);
1525 		dnd_highlight = NULL;
1526 	}
1527 
1528 	dnd_spring_abort();
1529 }
1530 
1531 /* Create XML icon nodes for these widgets.
1532  * Always frees the widgets list.
1533  */
make_widgets(xmlNodePtr side,GList * widgets)1534 static void make_widgets(xmlNodePtr side, GList *widgets)
1535 {
1536 	GList	*next;
1537 
1538 	for (next = widgets; next; next = next->next)
1539 	{
1540 		Icon	*icon;
1541 		xmlNodePtr tree;
1542 
1543 		icon = g_object_get_data(G_OBJECT(next->data), "icon");
1544 
1545 		if (!icon)
1546 		{
1547 			g_warning("Can't find Icon from widget\n");
1548 			continue;
1549 		}
1550 
1551 		tree = xmlNewTextChild(side, NULL, "icon", icon->src_path);
1552 
1553 		xmlSetProp(tree, "label", icon->item->leafname);
1554 		if (icon->shortcut)
1555 			xmlSetProp(tree, "shortcut", icon->shortcut);
1556 		if (icon->args)
1557 			xmlSetProp(tree, "args", icon->args);
1558 		if (icon->locked)
1559 			xmlSetProp(tree, "locked", "true");
1560 	}
1561 
1562 	if (widgets)
1563 		g_list_free(widgets);
1564 }
1565 
panel_save(Panel * panel)1566 void panel_save(Panel *panel)
1567 {
1568 	xmlDocPtr doc;
1569 	xmlNodePtr root;
1570 	xmlNodePtr options;
1571 	guchar	*save = NULL;
1572 	guchar	*save_new = NULL;
1573 
1574 	g_return_if_fail(panel != NULL);
1575 
1576 	if (strchr(panel->name, '/'))
1577 		save = g_strdup(panel->name);
1578 	else
1579 	{
1580 		guchar	*leaf;
1581 
1582 		leaf = g_strconcat("pan_", panel->name, NULL);
1583 		save = choices_find_xdg_path_save(leaf, PROJECT, SITE, TRUE);
1584 		g_free(leaf);
1585 	}
1586 
1587 	if (!save)
1588 		return;
1589 
1590 	doc = xmlNewDoc("1.0");
1591 	xmlDocSetRootElement(doc, xmlNewDocNode(doc, NULL, "panel", NULL));
1592 
1593 	root = xmlDocGetRootElement(doc);
1594 
1595 	xmlSetProp(root, "side", panel_side_to_name(panel->side));
1596 
1597 	options = xmlNewChild(root, NULL, "options", NULL);
1598 	set_int_prop(options, "style", panel->style);
1599 	set_int_prop(options, "width", panel->width);
1600 	set_int_prop(options, "avoid", panel->avoid);
1601 	set_int_prop(options, "xinerama", panel->xinerama);
1602 	set_int_prop(options, "monitor", panel->monitor);
1603 
1604 	make_widgets(xmlNewChild(root, NULL, "start", NULL),
1605 		gtk_container_get_children(GTK_CONTAINER(panel->before)));
1606 
1607 	make_widgets(xmlNewChild(root, NULL, "end", NULL),
1608 			g_list_reverse(gtk_container_get_children(
1609 					GTK_CONTAINER(panel->after))));
1610 
1611 	save_new = g_strconcat(save, ".new", NULL);
1612 	if (save_xml_file(doc, save_new) || rename(save_new, save))
1613 		delayed_error(_("Error saving panel %s: %s"),
1614 				save, g_strerror(errno));
1615 	g_free(save_new);
1616 
1617 	g_free(save);
1618 	if (doc)
1619 		xmlFreeDoc(doc);
1620 }
1621 
1622 /* Create a frame widget which can be used to add icons to the panel */
make_insert_frame(Panel * panel)1623 static GtkWidget *make_insert_frame(Panel *panel)
1624 {
1625 	GtkWidget *frame;
1626 	GtkTargetEntry 	target_table[] = {
1627 		{"text/uri-list", 0, TARGET_URI_LIST},
1628 	};
1629 
1630 	frame = gtk_frame_new(NULL);
1631 	gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE);
1632 	gtk_widget_set_size_request(frame, 16, 16);
1633 
1634 	g_signal_connect(frame, "drag-motion",
1635 			G_CALLBACK(insert_drag_motion), panel);
1636 	g_signal_connect(frame, "drag-leave",
1637 			G_CALLBACK(panel_drag_leave), panel);
1638 
1639 	g_signal_connect(frame, "drag-data-received",
1640 			G_CALLBACK(add_uri_list), panel);
1641 	gtk_drag_dest_set(frame,
1642 			GTK_DEST_DEFAULT_ALL,
1643 			target_table,
1644 			sizeof(target_table) / sizeof(*target_table),
1645 			GDK_ACTION_COPY);
1646 
1647 	return frame;
1648 }
1649 
enter_icon(GtkWidget * widget,GdkEventCrossing * event,Icon * icon)1650 static gboolean enter_icon(GtkWidget *widget,
1651 			   GdkEventCrossing *event,
1652 			   Icon *icon)
1653 {
1654 	icon_may_update(icon);
1655 	panel_icon_set_tip((PanelIcon *) icon);
1656 
1657 	return FALSE;
1658 }
1659 
panel_leave_event(GtkWidget * widget,GdkEventCrossing * event,Panel * panel)1660 static gint panel_leave_event(GtkWidget *widget,
1661 			      GdkEventCrossing *event,
1662 			      Panel *panel)
1663 {
1664 	GdkWindow *pinboard;
1665 
1666 	if (event->mode != GDK_CROSSING_NORMAL)
1667 		return FALSE;	/* Grab for menu, DnD, etc */
1668 
1669 	if (event->x >= 0 &&
1670 	    event->y >= 0 &&
1671 	    event->x < widget->allocation.width &&
1672 	    event->y < widget->allocation.height)
1673 	{
1674 		/* Still inside the panel's area.
1675 		 * Probably we've moved over a panel applet. */
1676 		return FALSE;
1677 	}
1678 
1679 	if (panel_keep_below(panel, TRUE))
1680 	{
1681 		/* Shouldn't need this as well as keep_below but some WMs don't
1682 		 * automatically lower as soon as the hint is set */
1683 		pinboard = pinboard_get_window();
1684 		window_put_just_above(panel->window->window, pinboard);
1685 	}
1686 
1687 	return FALSE;
1688 }
1689 
1690 /* If (x, y) is at the edge of the panel then raise */
motion_may_raise(Panel * panel,int x,int y)1691 static void motion_may_raise(Panel *panel, int x, int y)
1692 {
1693 	gboolean raise;
1694 
1695 	if (panel->side == PANEL_TOP)
1696 		raise = y == 0;
1697 	else if (panel->side == PANEL_BOTTOM)
1698 		raise = y == panel->window->allocation.height - 1;
1699 	else if (panel->side == PANEL_LEFT)
1700 		raise = x == 0;
1701 	else
1702 		raise = x == panel->window->allocation.width - 1;
1703 
1704 	if (raise && panel_keep_below(panel, FALSE))
1705 	{
1706 		/* Shouldn't need this as well as keep_below but some WMs don't
1707 		 * automatically raise as soon as the hint is set */
1708 		gdk_window_raise(panel->window->window);
1709 	}
1710 }
1711 
may_autoscroll(Panel * panel)1712 static gboolean may_autoscroll(Panel *panel)
1713 {
1714 	gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1715 	gint max, panel_x, panel_y, delta, new;
1716 
1717 	if (panel->adj->upper <= panel->adj->page_size)
1718 		goto stop_scrolling;	/* Can see everything already */
1719 
1720 	gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
1721 
1722 	if (horz)
1723 	{
1724 		delta = panel_x;
1725 		max   = panel->window->allocation.width;
1726 		if (panel_y < 0 || panel_y > panel->window->allocation.height)
1727 			goto stop_scrolling;	/* Not over the panel */
1728 	}
1729 	else
1730 	{
1731 		delta = panel_y;
1732 		max   = panel->window->allocation.height;
1733 		if (panel_x < 0 || panel_x > panel->window->allocation.width)
1734 			goto stop_scrolling;	/* Not over the panel */
1735 	}
1736 
1737 	if (delta >= 20 && delta <= max - 20)
1738 		goto stop_scrolling;	/* Not at either end */
1739 
1740 	panel->autoscroll_speed = MIN(panel->autoscroll_speed + 2, 200);
1741 
1742 	new = panel->adj->value - ((delta < 20) ? panel->autoscroll_speed
1743 						: -panel->autoscroll_speed);
1744 	new = CLAMP(new, 0, panel->adj->upper - panel->adj->page_size);
1745 	gtk_adjustment_set_value(panel->adj, new);
1746 
1747 	panel->autoscroll_to = g_timeout_add(40,
1748 			(GSourceFunc) may_autoscroll, panel);
1749 
1750 	return FALSE;
1751 
1752 stop_scrolling:
1753 	panel->autoscroll_speed = 0;
1754 	return FALSE;
1755 }
1756 
panel_motion_event(GtkWidget * widget,GdkEventMotion * event,Panel * panel)1757 static gint panel_motion_event(GtkWidget *widget,
1758 			      GdkEventMotion *event,
1759 			      Panel *panel)
1760 {
1761 	motion_may_raise(panel, event->x, event->y);
1762 
1763 	if (motion_state != MOTION_REPOSITION)
1764 		if (panel->autoscroll_speed == 0)
1765 			may_autoscroll(panel);
1766 
1767 	return FALSE;
1768 }
1769 
icon_motion_event(GtkWidget * widget,GdkEventMotion * event,PanelIcon * pi)1770 static gint icon_motion_event(GtkWidget *widget,
1771 			      GdkEventMotion *event,
1772 			      PanelIcon *pi)
1773 {
1774 	Panel	*panel = pi->panel;
1775 	GList	*list, *me;
1776 	gboolean horz = panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM;
1777 	int	val;
1778 	int	dir = 0;
1779 
1780 	if (motion_state == MOTION_READY_FOR_DND)
1781 	{
1782 		if (dnd_motion_moved(event))
1783 			start_drag(pi, event);
1784 		return TRUE;
1785 	}
1786 	else if (motion_state != MOTION_REPOSITION)
1787 		return FALSE;
1788 
1789 	list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1790 	list = g_list_append(list, NULL);	/* The gap in the middle */
1791 	list = g_list_concat(list,
1792 		gtk_container_get_children(GTK_CONTAINER(panel->after)));
1793 	me = g_list_find(list, widget);
1794 
1795 	g_return_val_if_fail(me != NULL, TRUE);
1796 
1797 	val = horz ? event->x_root : event->y_root;
1798 
1799 	if (me->prev)
1800 	{
1801 		GtkWidget *prev;
1802 		int	  x, y;
1803 
1804 		if (me->prev->data)
1805 			prev = GTK_WIDGET(me->prev->data);
1806 		else
1807 			prev = panel->gap;
1808 
1809 		gdk_window_get_origin(prev->window, &x, &y);
1810 
1811 		if (val <= (horz ? x : y))
1812 			dir = -1;
1813 	}
1814 
1815 	if (dir == 0 && me->next)
1816 	{
1817 		GtkWidget *next;
1818 		int	  x, y, w, h;
1819 
1820 		if (me->next->data)
1821 			next = GTK_WIDGET(me->next->data);
1822 		else
1823 			next = panel->gap;
1824 
1825 		gdk_window_get_origin(next->window, &x, &y);
1826 
1827 		gdk_drawable_get_size(next->window, &w, &h);
1828 
1829 		x += w;
1830 		y += h;
1831 
1832 		if (val >= (horz ? x : y)-1)
1833 		{
1834 			if (next == panel->gap)
1835 				dir = +2;
1836 			else
1837 				dir = +1;
1838 		}
1839 	}
1840 
1841 	if (dir)
1842 		reposition_icon(pi, g_list_index(list, widget) + dir);
1843 
1844 	return TRUE;
1845 }
1846 
reposition_icon_on_side(GtkWidget * side,GtkWidget * widget,int index)1847 static void reposition_icon_on_side(GtkWidget *side, GtkWidget *widget,
1848 				    int index)
1849 {
1850 	GList *list;
1851 
1852 	list = gtk_container_get_children(GTK_CONTAINER(side));
1853 
1854 	/* Want to move icon to the list in the given 'side'. Is it there
1855 	 * already?
1856 	 */
1857 
1858 	if (!g_list_find(list, widget))
1859 	{
1860 		/* No, reparent */
1861 		gtk_grab_remove(widget);
1862 		gtk_widget_reparent(widget, side);
1863 		dnd_motion_grab_pointer();
1864 		gtk_grab_add(widget);
1865 	}
1866 
1867 	gtk_box_reorder_child(GTK_BOX(side), widget, index);
1868 
1869 	g_list_free(list);
1870 }
1871 
1872 /* Move icon to this index in the complete widget list.
1873  * 0 makes the icon the left-most icon. The gap in the middle has
1874  * an index number, which allows you to specify that the icon should
1875  * go on the left or right side.
1876  */
reposition_icon(PanelIcon * pi,int index)1877 static void reposition_icon(PanelIcon *pi, int index)
1878 {
1879 	Panel	  *panel = pi->panel;
1880 	GtkWidget *widget = pi->widget;
1881 	GList	  *list;
1882 	int	  before_len;
1883 
1884 	list = gtk_container_get_children(GTK_CONTAINER(panel->before));
1885 	before_len = g_list_length(list);
1886 	g_list_free(list);
1887 
1888 	if (index <= before_len)
1889 		reposition_icon_on_side(panel->before, widget, index);
1890 	else
1891 		reposition_icon_on_side(panel->after, widget,
1892 					index - (before_len + 1));
1893 
1894 	panel_save(panel);
1895 }
1896 
start_drag(PanelIcon * pi,GdkEventMotion * event)1897 static void start_drag(PanelIcon *pi, GdkEventMotion *event)
1898 {
1899 	GtkWidget *widget = pi->widget;
1900 	Icon	  *icon = (Icon *) pi;
1901 
1902 	if (!icon->selected)
1903 	{
1904 		if (event->state & GDK_BUTTON1_MASK)
1905 		{
1906 			/* Select just this one */
1907 			icon_select_only(icon);
1908 			tmp_icon_selected = TRUE;
1909 		}
1910 		else
1911 			icon_set_selected(icon, TRUE);
1912 	}
1913 
1914 	g_return_if_fail(icon_selection != NULL);
1915 
1916 	if (icon_selection->next == NULL)
1917 		drag_one_item(widget, event, icon->path, icon->item, NULL);
1918 	else
1919 	{
1920 		guchar	*uri_list;
1921 
1922 		uri_list = icon_create_uri_list();
1923 		drag_selection(widget, event, uri_list);
1924 		g_free(uri_list);
1925 	}
1926 }
1927 
applet_died(GtkWidget * socket)1928 static void applet_died(GtkWidget *socket)
1929 {
1930 	gboolean never_plugged;
1931 
1932 	never_plugged = (!g_object_get_data(G_OBJECT(socket), "lost_plug"))
1933 		      && !GTK_SOCKET(socket)->plug_window;
1934 
1935 	if (never_plugged)
1936 	{
1937 		report_error(
1938 			_("Applet quit without ever creating a widget!"));
1939 		gtk_widget_destroy(socket);
1940 	}
1941 
1942 	gtk_widget_unref(socket);
1943 }
1944 
socket_destroyed(GtkWidget * socket,GtkWidget * widget)1945 static void socket_destroyed(GtkWidget *socket, GtkWidget *widget)
1946 {
1947 	g_object_set_data(G_OBJECT(socket), "lost_plug", "yes");
1948 
1949 	gtk_widget_unref(socket);
1950 
1951 	gtk_widget_destroy(widget);	/* Remove from panel */
1952 
1953 	if (!closing_panel)
1954 		panel_save(g_object_get_data(G_OBJECT(socket), "panel"));
1955 }
1956 
1957 /* Try to run this applet.
1958  * Cases:
1959  *
1960  * - No executable AppletRun:
1961  * 	icon->socket == NULL (unchanged) on return.
1962  *
1963  * Otherwise, create socket (setting icon->socket) and ref it twice.
1964  *
1965  * - AppletRun quits without connecting a plug:
1966  * 	On child death lost_plug is unset and socket is empty.
1967  * 	Unref socket.
1968  * 	Report error and destroy widget (to 'socket destroyed').
1969  *
1970  * - AppletRun quits while plug is in socket:
1971  * 	Unref socket once. Socket will be destroyed later.
1972  *
1973  * - Socket is destroyed.
1974  * 	Set lost_plug = "yes" and remove widget from panel.
1975  * 	Unref socket.
1976  */
run_applet(PanelIcon * pi)1977 static void run_applet(PanelIcon *pi)
1978 {
1979 	GError	*error = NULL;
1980 	char	*argv[3];
1981 	gint	pid;
1982 	Icon	*icon = (Icon *) pi;
1983 
1984 	argv[0] = (char *) make_path(icon->path, "AppletRun");
1985 
1986 	if (access(argv[0], X_OK) != 0)
1987 		return;
1988 
1989 	pi->socket = gtk_socket_new();
1990 
1991 	gtk_container_add(GTK_CONTAINER(pi->widget), pi->socket);
1992 	gtk_widget_show_all(pi->socket);
1993 	gtk_widget_realize(pi->socket);
1994 
1995 	/* Always get button-2 events so we can drag */
1996 	XGrabButton(gdk_display, Button2, AnyModifier,
1997 			GDK_WINDOW_XWINDOW(pi->socket->window),
1998 			False,
1999 			ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
2000 			GrabModeAsync,	/* Pointer */
2001 			GrabModeAsync,	/* Keyboard */
2002 			None, None);
2003 
2004 	{
2005 		gchar		*pos;
2006 		PanelSide	side = pi->panel->side;
2007 
2008 		/* Set a hint to let applets position their menus correctly */
2009 		pos = g_strdup_printf("%s,%d",
2010 				panel_side_to_name(side), MENU_MARGIN(side));
2011 		gdk_property_change(pi->socket->window,
2012 				gdk_atom_intern("_ROX_PANEL_MENU_POS", FALSE),
2013 				gdk_atom_intern("STRING", FALSE),
2014 				8, GDK_PROP_MODE_REPLACE,
2015 				pos, strlen(pos));
2016 		g_free(pos);
2017 
2018 		/* Ensure that the properties are set before starting the
2019 		 * applet.
2020 		 */
2021 		gdk_flush();
2022 	}
2023 
2024 	g_object_set_data(G_OBJECT(pi->widget), "icon", pi);
2025 	g_object_set_data(G_OBJECT(pi->socket), "panel", pi->panel);
2026 
2027 	argv[1] = g_strdup_printf("%ld",
2028 			GDK_WINDOW_XWINDOW(pi->socket->window));
2029 	argv[2] = NULL;
2030 
2031 	if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
2032 			NULL, NULL, &pid, &error))
2033 	{
2034 		delayed_error(_("Error running applet:\n%s"), error->message);
2035 		g_error_free(error);
2036 		gtk_widget_destroy(pi->socket);
2037 		pi->socket = NULL;
2038 	}
2039 	else
2040 	{
2041 		gtk_widget_ref(pi->socket);
2042 		on_child_death(pid, (CallbackFn) applet_died, pi->socket);
2043 
2044 		gtk_widget_ref(pi->socket);
2045 		g_signal_connect(pi->socket, "destroy",
2046 				G_CALLBACK(socket_destroyed), pi->widget);
2047 	}
2048 
2049 	g_free(argv[1]);
2050 }
2051 
panel_post_resize(GtkWidget * win,GtkRequisition * req,Panel * panel)2052 static void panel_post_resize(GtkWidget *win, GtkRequisition *req, Panel *panel)
2053 {
2054 	if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2055 	{
2056 		req->width = panel->geometry.width;
2057 		req->height += EDGE_WIDTH;
2058 	}
2059 	else
2060 	{
2061 		int h = panel->geometry.height;
2062 
2063 		if (current_panel[PANEL_TOP])
2064 		{
2065 			GtkWidget *win = current_panel[PANEL_TOP]->window;
2066 			h -= win->allocation.height;
2067 		}
2068 
2069 		if (current_panel[PANEL_BOTTOM])
2070 		{
2071 			GtkWidget *win = current_panel[PANEL_BOTTOM]->window;
2072 			h -= win->allocation.height;
2073 		}
2074 
2075 		req->height = h;
2076 		req->width += EDGE_WIDTH;
2077 	}
2078 }
2079 
update_side(GtkWidget * side)2080 static void update_side(GtkWidget *side)
2081 {
2082 	GList	*kids, *next;
2083 
2084 	kids = gtk_container_get_children(GTK_CONTAINER(side));
2085 	for (next = kids; next; next = next->next)
2086 	{
2087 		PanelIcon *pi;
2088 		pi = g_object_get_data(next->data, "icon");
2089 		panel_icon_set_tip(pi);
2090 	}
2091 	g_list_free(kids);
2092 }
2093 
2094 /* Tips or style has changed -- update everything on this panel */
panel_set_style(Panel * panel)2095 static void panel_set_style(Panel *panel)
2096 {
2097 	update_side(panel->before);
2098 	update_side(panel->after);
2099 	gtk_widget_queue_resize(panel->window);
2100 }
2101 
recreate_panels(char ** names)2102 static gboolean recreate_panels(char **names)
2103 {
2104 	int i;
2105 
2106 	for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2107 	{
2108 		if (names[i])
2109 		{
2110 			panel_new(names[i], i);
2111 			g_free(names[i]);
2112 		}
2113 	}
2114 
2115 	g_free(names);
2116 
2117 	return FALSE;
2118 }
2119 
update_side_size(GtkWidget * side)2120 static void update_side_size(GtkWidget *side)
2121 {
2122 	GList	*kids, *next;
2123 
2124 	kids = gtk_container_get_children(GTK_CONTAINER(side));
2125 	for (next = kids; next; next = next->next)
2126 	{
2127 		PanelIcon *pi;
2128 		pi = g_object_get_data(next->data, "icon");
2129 		gtk_widget_queue_resize(pi->widget);
2130 	}
2131 	g_list_free(kids);
2132 }
2133 
2134 /* Update panel size and redraw */
panel_update(Panel * panel)2135 static void panel_update(Panel *panel)
2136 {
2137 	update_side_size(panel->before);
2138 	update_side_size(panel->after);
2139 	gtk_widget_queue_resize(panel->window);
2140 	gtk_widget_queue_draw(panel->window);
2141 }
2142 
panel_style_changed(void)2143 static void panel_style_changed(void)
2144 {
2145 	int i;
2146 
2147 	if (o_override_redirect.has_changed)
2148 	{
2149 		gchar **names;
2150 
2151 		names = g_new(char *, PANEL_NUMBER_OF_SIDES);
2152 
2153 		for (i = 0; i < PANEL_NUMBER_OF_SIDES; i++)
2154 		{
2155 			Panel *panel = current_panel[i];
2156 			names[i] = panel ? g_strdup(panel->name) : NULL;
2157 			panel_new(NULL, i);
2158 		}
2159 
2160 		g_idle_add((GtkFunction) recreate_panels, names);
2161 	}
2162 }
2163 
draw_panel_edge(GtkWidget * widget,GdkEventExpose * event,Panel * panel)2164 static gboolean draw_panel_edge(GtkWidget *widget, GdkEventExpose *event,
2165 				Panel *panel)
2166 {
2167 	int	x, y, width, height;
2168 
2169 	if (panel->side == PANEL_TOP || panel->side == PANEL_BOTTOM)
2170 	{
2171 		width = panel->geometry.width;
2172 		height = EDGE_WIDTH;
2173 
2174 		x = 0;
2175 		if (panel->side == PANEL_BOTTOM)
2176 			y = 0;
2177 		else
2178 			y = widget->allocation.height - EDGE_WIDTH;
2179 	}
2180 	else
2181 	{
2182 		width = EDGE_WIDTH;
2183 		height = panel->geometry.height;
2184 
2185 		y = 0;
2186 		if (panel->side == PANEL_RIGHT)
2187 			x = 0;
2188 		else
2189 			x = widget->allocation.width - EDGE_WIDTH;
2190 	}
2191 
2192 	gdk_draw_rectangle(widget->window,
2193 			widget->style->fg_gc[GTK_STATE_NORMAL], TRUE,
2194 			x, y, width, height);
2195 
2196 	return FALSE;
2197 }
2198 
2199 static gpointer parent_class;
2200 
panel_icon_destroy(Icon * icon)2201 static void panel_icon_destroy(Icon *icon)
2202 {
2203 	PanelIcon *pi = (PanelIcon *) icon;
2204 
2205 	g_return_if_fail(pi != NULL);
2206 
2207 	if (pi->image)
2208 		g_object_unref(pi->image);
2209 
2210 	g_return_if_fail(pi->widget != NULL);
2211 
2212 	gtk_widget_destroy(pi->widget);
2213 }
2214 
panel_remove_items(void)2215 static void panel_remove_items(void)
2216 {
2217 	Panel *panel;
2218 
2219 	g_return_if_fail(icon_selection != NULL);
2220 
2221 	panel = ((PanelIcon *) icon_selection->data)->panel;
2222 
2223 	while (icon_selection)
2224 		icon_destroy((Icon *) icon_selection->data);
2225 
2226 	panel_save(panel);
2227 }
2228 
2229 /* Icon's size, shape or appearance has changed - update the display */
panel_icon_redraw(Icon * icon)2230 static void panel_icon_redraw(Icon *icon)
2231 {
2232 	PanelIcon *pi = (PanelIcon *) icon;
2233 
2234 	gtk_widget_set_state(pi->widget,
2235 			icon->selected ? GTK_STATE_SELECTED
2236 				       : GTK_STATE_NORMAL);
2237 
2238 	/* Will regenerate the scaled icon from the new image */
2239 	gtk_widget_queue_resize(pi->widget);
2240 
2241 	panel_icon_set_tip((PanelIcon *) icon);
2242 }
2243 
panel_icon_update(Icon * icon)2244 static void panel_icon_update(Icon *icon)
2245 {
2246 	PanelIcon	*pi = (PanelIcon *) icon;
2247 
2248 	gtk_widget_queue_draw(pi->widget);
2249 	gtk_label_set_text(GTK_LABEL(pi->label), icon->item->leafname);
2250 	panel_save(pi->panel);
2251 }
2252 
2253 /* The point of this is to clear the selection if the existing icons
2254  * aren't from the same panel...
2255  */
panel_icon_same_group(Icon * icon,Icon * other)2256 static gboolean panel_icon_same_group(Icon *icon, Icon *other)
2257 {
2258 	if (IS_PANEL_ICON(other))
2259 	{
2260 		PanelIcon *a = (PanelIcon *) icon;
2261 		PanelIcon *b = (PanelIcon *) other;
2262 
2263 		return a->panel == b->panel;
2264 	}
2265 	else
2266 		return FALSE;
2267 }
2268 
panel_icon_class_init(gpointer gclass,gpointer data)2269 static void panel_icon_class_init(gpointer gclass, gpointer data)
2270 {
2271 	IconClass *icon = (IconClass *) gclass;
2272 
2273 	parent_class = g_type_class_peek_parent(gclass);
2274 
2275 	icon->destroy = panel_icon_destroy;
2276 	icon->redraw = panel_icon_redraw;
2277 	icon->update = panel_icon_update;
2278 	icon->remove_items = panel_remove_items;
2279 	icon->same_group = panel_icon_same_group;
2280 	icon->wink = panel_icon_wink;
2281 }
2282 
panel_icon_init(GTypeInstance * object,gpointer gclass)2283 static void panel_icon_init(GTypeInstance *object, gpointer gclass)
2284 {
2285 	PanelIcon *pi = (PanelIcon *) object;
2286 
2287 	pi->widget = NULL;
2288 	pi->image = NULL;
2289 	pi->label = NULL;
2290 	pi->socket = NULL;
2291 	pi->style = TEXT_UNDER_ICON;
2292 }
2293 
panel_icon_get_type(void)2294 static GType panel_icon_get_type(void)
2295 {
2296 	static GType type = 0;
2297 
2298 	if (!type)
2299 	{
2300 		static const GTypeInfo info =
2301 		{
2302 			sizeof (PanelIconClass),
2303 			NULL,			/* base_init */
2304 			NULL,			/* base_finalise */
2305 			panel_icon_class_init,
2306 			NULL,			/* class_finalise */
2307 			NULL,			/* class_data */
2308 			sizeof(PanelIcon),
2309 			0,			/* n_preallocs */
2310 			panel_icon_init
2311 		};
2312 
2313 		type = g_type_register_static(icon_get_type(),
2314 						"PanelIcon", &info, 0);
2315 	}
2316 
2317 	return type;
2318 }
2319 
panel_icon_new(Panel * panel,const char * pathname,const char * name)2320 static PanelIcon *panel_icon_new(Panel *panel,
2321 				 const char *pathname,
2322 				 const char *name)
2323 {
2324 	PanelIcon *pi;
2325 	Icon	  *icon;
2326 
2327 	pi = g_object_new(panel_icon_get_type(), NULL);
2328 	icon = (Icon *) pi;
2329 
2330 	icon_set_path(icon, pathname, name);
2331 	pi->panel = panel;
2332 
2333 	return pi;
2334 }
2335 
panel_want_show_text(PanelIcon * pi)2336 static gboolean panel_want_show_text(PanelIcon *pi)
2337 {
2338 	Icon *icon = (Icon *) pi;
2339 	Panel *panel = pi->panel;
2340 
2341 	if (!icon->item->leafname[0])
2342 		return FALSE;
2343 
2344 	if (panel->style == SHOW_BOTH)
2345 		return TRUE;
2346 	if (panel->style == SHOW_ICON)
2347 		return FALSE;
2348 
2349 	if (icon->item->flags & ITEM_FLAG_APPDIR)
2350 		return FALSE;
2351 
2352 	if (EXECUTABLE_FILE(icon->item))
2353 		return FALSE;
2354 
2355 	return TRUE;
2356 }
2357 
xinerama_sensitive(GtkBuilder * builder,gboolean sensitive)2358 static void xinerama_sensitive(GtkBuilder *builder, gboolean sensitive)
2359 {
2360 	gtk_widget_set_sensitive(
2361 			GTK_WIDGET(gtk_builder_get_object(builder, "panel_xinerama_monitor")),
2362 			sensitive);
2363 }
2364 
panel_from_opts_widget(GtkWidget * widget)2365 inline static Panel *panel_from_opts_widget(GtkWidget *widget)
2366 {
2367 	return g_object_get_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2368 			"rox-panel");
2369 }
2370 
panel_style_radio_toggled(GtkToggleButton * widget,int style)2371 static void panel_style_radio_toggled(GtkToggleButton *widget, int style)
2372 {
2373 	Panel *panel;
2374 
2375 	if (!gtk_toggle_button_get_active(widget))
2376 		return;
2377 	panel = panel_from_opts_widget(GTK_WIDGET(widget));
2378 	if (style != panel->style)
2379 	{
2380 		panel->style = style;
2381 		panel_set_style(panel);
2382 		panel_save(panel);
2383 	}
2384 }
2385 
panel_xinerama_changed(Panel * panel)2386 static void panel_xinerama_changed(Panel *panel)
2387 {
2388 	panel_update_geometry(panel);
2389 	reposition_panel(panel->window, &panel->window->allocation, panel);
2390 	gtk_widget_queue_resize(panel->window);
2391 	panel_save(panel);
2392 }
2393 
panel_side_radio_toggled(GtkWidget * widget,PanelSide new_side)2394 static void panel_side_radio_toggled(GtkWidget *widget, PanelSide new_side)
2395 {
2396 	Panel *panel;
2397 	PanelSide old_side;
2398 	char *name, *other_side_name;
2399 
2400 	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
2401 		return;
2402 
2403 	panel = panel_from_opts_widget(widget);
2404 	old_side = panel->side;
2405 	if (new_side == old_side)
2406 		return;
2407 
2408 	name = g_strdup(panel->name);
2409 	other_side_name = current_panel[new_side]
2410 			? g_strdup(current_panel[new_side]->name)
2411 			: NULL;
2412 
2413 	panel_new(name, new_side);
2414 	g_object_set_data(G_OBJECT(gtk_widget_get_toplevel(widget)),
2415 			"rox-panel", current_panel[new_side]);
2416 	panel_new(other_side_name, old_side);
2417 
2418 	g_free(name);
2419 	g_free(other_side_name);
2420 }
2421 
panel_style_radio_0_toggled_cb(GtkToggleButton * widget)2422 static void panel_style_radio_0_toggled_cb(GtkToggleButton *widget)
2423 {
2424 	panel_style_radio_toggled(widget, 0);
2425 }
2426 
panel_style_radio_1_toggled_cb(GtkToggleButton * widget)2427 static void panel_style_radio_1_toggled_cb(GtkToggleButton *widget)
2428 {
2429 	panel_style_radio_toggled(widget, 1);
2430 }
2431 
panel_style_radio_2_toggled_cb(GtkToggleButton * widget)2432 static void panel_style_radio_2_toggled_cb(GtkToggleButton *widget)
2433 {
2434 	panel_style_radio_toggled(widget, 2);
2435 }
2436 
panel_width_changed_cb(GtkSpinButton * widget)2437 static void panel_width_changed_cb(GtkSpinButton *widget)
2438 {
2439 	Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2440 	int width = gtk_spin_button_get_value_as_int(widget);
2441 
2442 	if (width != panel->width)
2443 	{
2444 		panel->width = width;
2445 		panel_update(panel);
2446 		panel_save(panel);
2447 	}
2448 }
2449 
panel_avoid_toggled_cb(GtkToggleButton * widget)2450 static void panel_avoid_toggled_cb(GtkToggleButton *widget)
2451 {
2452 	Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2453 	gboolean avoid = gtk_toggle_button_get_active(widget);
2454 
2455 	if (avoid != panel->avoid)
2456 	{
2457 		panel->avoid = avoid;
2458 		panel_setup_struts(panel, &panel->window->allocation);
2459 		panel_save(panel);
2460 	}
2461 }
2462 
panel_xinerama_confine_toggled_cb(GtkWidget * widget,GtkWidget * spinner)2463 static void panel_xinerama_confine_toggled_cb(GtkWidget *widget, GtkWidget *spinner)
2464 {
2465 	Panel *panel = panel_from_opts_widget(widget);
2466 	gboolean xinerama = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
2467 
2468 	gtk_widget_set_sensitive(spinner, xinerama);
2469 
2470 	if (xinerama != panel->xinerama)
2471 	{
2472 		panel->xinerama = xinerama;
2473 		panel_xinerama_changed(panel);
2474 	}
2475 }
2476 
panel_xinerama_monitor_changed_cb(GtkSpinButton * widget)2477 static void panel_xinerama_monitor_changed_cb(GtkSpinButton *widget)
2478 {
2479 	Panel *panel = panel_from_opts_widget(GTK_WIDGET(widget));
2480 	int monitor = gtk_spin_button_get_value_as_int(widget);
2481 
2482 	if (monitor != panel->monitor)
2483 	{
2484 		panel->monitor = monitor;
2485 		panel_xinerama_changed(panel);
2486 	}
2487 }
2488 
panel_pos_top_toggled_cb(GtkWidget * widget)2489 static void panel_pos_top_toggled_cb(GtkWidget *widget)
2490 {
2491 	panel_side_radio_toggled(widget, PANEL_TOP);
2492 }
2493 
panel_pos_bottom_toggled_cb(GtkWidget * widget)2494 static void panel_pos_bottom_toggled_cb(GtkWidget *widget)
2495 {
2496 	panel_side_radio_toggled(widget, PANEL_BOTTOM);
2497 }
2498 
panel_pos_left_toggled_cb(GtkWidget * widget)2499 static void panel_pos_left_toggled_cb(GtkWidget *widget)
2500 {
2501 	panel_side_radio_toggled(widget, PANEL_LEFT);
2502 }
2503 
panel_pos_right_toggled_cb(GtkWidget * widget)2504 static void panel_pos_right_toggled_cb(GtkWidget *widget)
2505 {
2506 	panel_side_radio_toggled(widget, PANEL_RIGHT);
2507 }
2508 
panel_connect_dialog_signal_handlers(GtkBuilder * builder,GObject * object,const gchar * signal_name,const gchar * handler_name,GObject * connect_object,GConnectFlags flags,gpointer user_data)2509 static void panel_connect_dialog_signal_handlers(GtkBuilder *builder,
2510 						 GObject *object,
2511 						 const gchar *signal_name,
2512 						 const gchar *handler_name,
2513 						 GObject *connect_object,
2514 						 GConnectFlags flags,
2515 						 gpointer user_data)
2516 {
2517 	void *fn = NULL;
2518 
2519 	if (strcmp(handler_name, "gtk_widget_destroy") == 0)
2520 		fn = gtk_widget_destroy;
2521 	else if (strcmp(handler_name, "panel_style_radio_0_toggled_cb") == 0)
2522 		fn = panel_style_radio_0_toggled_cb;
2523 	else if (strcmp(handler_name, "panel_style_radio_0_toggled_cb") == 0)
2524 		fn = panel_style_radio_0_toggled_cb;
2525 	else if (strcmp(handler_name, "panel_style_radio_1_toggled_cb") == 0)
2526 		fn = panel_style_radio_1_toggled_cb;
2527 	else if (strcmp(handler_name, "panel_style_radio_2_toggled_cb") == 0)
2528 		fn = panel_style_radio_2_toggled_cb;
2529 	else if (strcmp(handler_name, "panel_width_changed_cb") == 0)
2530 		fn = panel_width_changed_cb;
2531 	else if (strcmp(handler_name, "panel_avoid_toggled_cb") == 0)
2532 		fn = panel_avoid_toggled_cb;
2533 	else if (strcmp(handler_name, "panel_xinerama_confine_toggled_cb") == 0)
2534 		fn = panel_xinerama_confine_toggled_cb;
2535 	else if (strcmp(handler_name, "panel_xinerama_monitor_changed_cb") == 0)
2536 		fn = panel_xinerama_monitor_changed_cb;
2537 	else if (strcmp(handler_name, "panel_pos_top_toggled_cb") == 0)
2538 		fn = panel_pos_top_toggled_cb;
2539 	else if (strcmp(handler_name, "panel_pos_bottom_toggled_cb") == 0)
2540 		fn = panel_pos_bottom_toggled_cb;
2541 	else if (strcmp(handler_name, "panel_pos_left_toggled_cb") == 0)
2542 		fn = panel_pos_left_toggled_cb;
2543 	else if (strcmp(handler_name, "panel_pos_right_toggled_cb") == 0)
2544 		fn = panel_pos_right_toggled_cb;
2545 
2546 	if (fn != NULL)
2547 	{
2548 		g_signal_connect(object, signal_name, fn, connect_object);
2549 	}
2550 	else
2551 	{
2552 		g_warning("Unknown handler '%s'", handler_name);
2553 	}
2554 }
2555 
panel_setup_options_dialog(GtkBuilder * builder,Panel * panel)2556 static void panel_setup_options_dialog(GtkBuilder *builder, Panel *panel)
2557 {
2558 	char *wnm;
2559 	const char *pos_radio;
2560 
2561 	wnm = g_strdup_printf("panel_style_radio_%d", panel->style);
2562 	gtk_toggle_button_set_active(
2563 			GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, wnm)),
2564 			TRUE);
2565 	g_free(wnm);
2566 	gtk_spin_button_set_value(
2567 		GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "panel_width")),
2568 		panel->width);
2569 	gtk_toggle_button_set_active(
2570 		GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "panel_avoid")),
2571 		panel->avoid);
2572 	gtk_toggle_button_set_active(
2573 		GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder,
2574 				"panel_xinerama_confine")),
2575 		panel->xinerama);
2576 	gtk_spin_button_set_adjustment(
2577 		GTK_SPIN_BUTTON(gtk_builder_get_object(builder, "panel_xinerama_monitor")),
2578 		GTK_ADJUSTMENT(gtk_adjustment_new(MAX(0, panel->monitor),
2579 				0, n_monitors - 1, 1, 10, 0)));
2580 	xinerama_sensitive(builder, panel->xinerama);
2581 	switch (panel->side)
2582 	{
2583 		case PANEL_TOP:
2584 			pos_radio = "panel_pos_top";
2585 			break;
2586 		case PANEL_BOTTOM:
2587 			pos_radio = "panel_pos_bottom";
2588 			break;
2589 		case PANEL_LEFT:
2590 			pos_radio = "panel_pos_left";
2591 			break;
2592 		case PANEL_RIGHT:
2593 			pos_radio = "panel_pos_right";
2594 			break;
2595 		default:
2596 			pos_radio = NULL;
2597 			break;
2598 	}
2599 	g_return_if_fail(pos_radio != NULL);
2600 	gtk_toggle_button_set_active(
2601 		GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, pos_radio)),
2602 		TRUE);
2603 }
2604 
panel_show_options(Panel * panel)2605 static void panel_show_options(Panel *panel)
2606 {
2607 	GtkWidget *dialog;
2608 	gboolean already_showing = FALSE;
2609 	GtkBuilder *builder;
2610 	gchar *ids[] = {"adjustment1", "adjustment2", "Panel Options", NULL};
2611 
2612 	builder = get_gtk_builder(ids);
2613 
2614 	if (panel_options_dialog)
2615 	{
2616 		dialog = panel_options_dialog;
2617 		already_showing = TRUE;
2618 	}
2619 	else
2620 	{
2621 		dialog = GTK_WIDGET(gtk_builder_get_object(builder, "Panel Options"));
2622 		panel_options_dialog = dialog;
2623 		g_signal_connect(dialog, "destroy",
2624 				G_CALLBACK(gtk_widget_destroyed),
2625 				&panel_options_dialog);
2626 		gtk_builder_connect_signals_full(builder, &panel_connect_dialog_signal_handlers, NULL);
2627 	}
2628 	g_object_set_data(G_OBJECT(panel_options_dialog), "rox-panel", panel);
2629 
2630 	panel_setup_options_dialog(builder, panel);
2631 
2632 	if (already_showing)
2633 	{
2634 		GtkWindow *win = GTK_WINDOW(dialog);
2635 
2636 		gtk_widget_hide(dialog);
2637 		/* This extra set_position() should ensure it moves to new position
2638 		 * under pointer */
2639 		gtk_window_set_position(win, GTK_WIN_POS_CENTER_ALWAYS);
2640 		gtk_window_set_position(win, GTK_WIN_POS_MOUSE);
2641 		gtk_window_present(win);
2642 	}
2643 	else
2644 	{
2645 		gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2646 		gtk_widget_show_all(dialog);
2647 	}
2648 
2649 	g_object_unref(builder);
2650 }
2651 
panel_position_menu(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer data)2652 static void panel_position_menu(GtkMenu *menu, gint *x, gint *y,
2653 				gboolean  *push_in, gpointer data)
2654 {
2655 	int		*pos = (int *) data;
2656 	GtkRequisition 	requisition;
2657 	int		margin = pos[2];
2658 	int		mon = pos[3];
2659 	int		mon_right = monitor_geom[mon].x +
2660 				monitor_geom[mon].width;
2661 	int		mon_bottom = monitor_geom[mon].y +
2662 				monitor_geom[mon].height;
2663 
2664 	gtk_widget_size_request(GTK_WIDGET(menu), &requisition);
2665 
2666 	if (pos[0] == -1)
2667 		*x = mon_right - margin - requisition.width;
2668 	else if (pos[0] == -2)
2669 		*x = monitor_geom[mon].x + margin;
2670 	else
2671 		*x = pos[0] - (requisition.width >> 2);
2672 
2673 	if (pos[1] == -1)
2674 		*y = mon_bottom - margin - requisition.height;
2675 	else if (pos[1] == -2)
2676 		*y = monitor_geom[mon].y + margin;
2677 	else
2678 		*y = pos[1] - (requisition.height >> 2);
2679 
2680 	*x = CLAMP(*x, 0, mon_right - requisition.width);
2681 	*y = CLAMP(*y, 0, mon_bottom - requisition.height);
2682 
2683 	*push_in = FALSE;
2684 }
2685 
panel_remove_callback(PanelSide side)2686 static void panel_remove_callback(PanelSide side)
2687 {
2688 	GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2689 			GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
2690 			_("Are you sure you want to remove this panel from the desktop?"));
2691 
2692 	gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
2693 	gtk_window_set_title(GTK_WINDOW(dialog), _("Remove Panel"));
2694 	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
2695 	if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
2696 		panel_new(NULL, side);
2697 	gtk_widget_destroy(dialog);
2698 }
2699 
panel_show_menu(GdkEventButton * event,PanelIcon * pi,Panel * panel)2700 static void panel_show_menu(GdkEventButton *event, PanelIcon *pi, Panel *panel)
2701 {
2702 	GtkWidget	*option_item;
2703 	GtkWidget	*del_item;
2704 	PanelSide	side = panel->side;
2705 	int		pos[4];
2706 
2707 	pos[0] = event->x_root;
2708 	pos[1] = event->y_root;
2709 	pos[2] = MENU_MARGIN(side);
2710 	/* FIXME: Should we read screen from event's window rather than
2711 	 * using default? */
2712 	pos[3] = gdk_screen_get_monitor_at_point(
2713 				gdk_screen_get_default(),
2714 				event->x_root, event->y_root);
2715 
2716 	option_item = gtk_image_menu_item_new_with_label(_("Panel Options..."));
2717 	g_signal_connect_swapped(option_item, "activate",
2718 			 G_CALLBACK(panel_show_options), panel);
2719 
2720 	del_item = gtk_image_menu_item_new_with_label(_("Remove Panel"));
2721 	add_stock_to_menu_item(del_item, GTK_STOCK_REMOVE);
2722 	g_signal_connect_swapped(del_item, "activate",
2723 			 G_CALLBACK(panel_remove_callback), GINT_TO_POINTER(side));
2724 
2725 	icon_prepare_menu((Icon *) pi, option_item, del_item, NULL);
2726 
2727 	if (side == PANEL_LEFT)
2728 		pos[0] = -2;
2729 	else if (side == PANEL_RIGHT)
2730 		pos[0] = -1;
2731 
2732 	if (side == PANEL_TOP)
2733 		pos[1] = -2;
2734 	else if (side == PANEL_BOTTOM)
2735 		pos[1] = -1;
2736 
2737 	gtk_menu_popup(GTK_MENU(icon_menu), NULL, NULL,
2738 			panel_position_menu,
2739 			(gpointer) pos, event->button, event->time);
2740 }
2741 
2742 /* Note: also called from icon handler */
panel_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,Panel * panel)2743 static gboolean panel_drag_motion(GtkWidget	*widget,
2744 			    GdkDragContext	*context,
2745 			    gint		x,
2746 			    gint		y,
2747 			    guint		time,
2748 			    Panel		*panel)
2749 {
2750 	int panel_x, panel_y;
2751 
2752 	gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2753 
2754 	motion_may_raise(panel, panel_x, panel_y);
2755 	gdk_drag_status(context, 0, time);
2756 
2757 	return TRUE;
2758 }
2759 
insert_drag_motion(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,Panel * panel)2760 static gboolean insert_drag_motion(GtkWidget	*widget,
2761 			    GdkDragContext	*context,
2762 			    gint		x,
2763 			    gint		y,
2764 			    guint		time,
2765 			    Panel		*panel)
2766 {
2767 	int panel_x, panel_y;
2768 
2769 	gdk_window_get_pointer(panel->window->window, &panel_x, &panel_y, NULL);
2770 	motion_may_raise(panel, panel_x, panel_y);
2771 
2772 	return FALSE;
2773 }
2774 
2775 /* Note: also called from icon handler */
panel_drag_leave(GtkWidget * widget,GdkDragContext * context,guint32 time,Panel * panel)2776 static void panel_drag_leave(GtkWidget	*widget,
2777 		       GdkDragContext	*context,
2778 		       guint32		time,
2779 		       Panel		*panel)
2780 {
2781 	GdkWindow *pinboard, *window;
2782 	GtkAllocation *alloc = &panel->window->allocation;
2783 	int x, y;
2784 
2785 	window = panel->window->window;
2786 	gdk_window_get_pointer(window, &x, &y, NULL);
2787 	if ((x < 0 || y < 0 || x > alloc->width || y > alloc->height) &&
2788 		panel_keep_below(panel, TRUE))
2789 	{
2790 		/* Shouldn't need this as well as keep_below but some WMs don't
2791 		 * automatically lower as soon as the hint is set */
2792 		pinboard = pinboard_get_window();
2793 		window_put_just_above(panel->window->window, pinboard);
2794 	}
2795 }
2796 
panel_update_geometry(Panel * panel)2797 static void panel_update_geometry(Panel *panel)
2798 {
2799 	if (panel->xinerama && panel->monitor >= n_monitors)
2800 	{
2801 		g_warning(_("Xinerama monitor %d unavailable"), panel->monitor);
2802 		panel->xinerama = FALSE;
2803 	}
2804 
2805 	if (panel->xinerama)
2806 	{
2807 		panel->geometry = monitor_geom[panel->monitor];
2808 	}
2809 	else if (o_panel_obey_workarea.int_value)
2810 	{
2811                 get_work_area(&panel->geometry.x, &panel->geometry.y,
2812                               &panel->geometry.width, &panel->geometry.height);
2813 	}
2814         else
2815 	{
2816 		panel->geometry.x = panel->geometry.y = 0;
2817                 panel->geometry.width = screen_width;
2818                 panel->geometry.height = screen_height;
2819 	}
2820 }
2821 
build_monitor_number(Option * option,xmlNode * node,guchar * label)2822 static GList *build_monitor_number(Option *option, xmlNode *node, guchar *label)
2823 {
2824 	GtkObject *adj;
2825 
2826 	adj = gtk_adjustment_new(MAX(0, panel_monitor),
2827 				0, n_monitors - 1, 1, 10, 1);
2828 	return build_numentry_base(option, node, label, GTK_ADJUSTMENT(adj));
2829 }
2830 
panel_side_to_translated_name(PanelSide side)2831 static const char *panel_side_to_translated_name(PanelSide side)
2832 {
2833 	switch (side)
2834 	{
2835 		case PANEL_TOP:
2836 			return _("Top");
2837 		case PANEL_BOTTOM:
2838 			return _("Bottom");
2839 		case PANEL_LEFT:
2840 			return _("Left");
2841 		case PANEL_RIGHT:
2842 			return _("Right");
2843 		case PANEL_DEFAULT_SIDE:
2844 			return _("Default");
2845 		default:
2846 			break;
2847 	}
2848 	return _("Unknown side");
2849 }
2850 
panel_side_to_name(PanelSide side)2851 const char *panel_side_to_name(PanelSide side)
2852 {
2853 	switch (side)
2854 	{
2855 		case PANEL_TOP:
2856 			return "Top";
2857 		case PANEL_BOTTOM:
2858 			return "Bottom";
2859 		case PANEL_LEFT:
2860 			return "Left";
2861 		case PANEL_RIGHT:
2862 			return "Right";
2863 		case PANEL_DEFAULT_SIDE:
2864 			return "Default";
2865 		default:
2866 			break;
2867 	}
2868 	return "UnknownSide";
2869 }
2870 
2871 /* Returns PANEL_NUMBER_OF_SIDES if name is invalid */
panel_name_to_side(gchar * side)2872 PanelSide panel_name_to_side(gchar *side)
2873 {
2874 	if (strcmp(side, "Top") == 0)
2875 		return PANEL_TOP;
2876 	else if (strcmp(side, "Bottom") == 0)
2877 		return PANEL_BOTTOM;
2878 	else if (strcmp(side, "Left") == 0)
2879 		return PANEL_LEFT;
2880 	else if (strcmp(side, "Right") == 0)
2881 		return PANEL_RIGHT;
2882 	else
2883 		g_warning("Unknown panel side '%s'", side);
2884 	return PANEL_NUMBER_OF_SIDES;
2885 }
2886 
panel_add_callback(PanelSide side)2887 static void panel_add_callback(PanelSide side)
2888 {
2889 	g_return_if_fail(current_panel[side] == NULL);
2890 	panel_new(panel_side_to_name(side), side);
2891 }
2892 
panel_new_panel_submenu(void)2893 GtkWidget *panel_new_panel_submenu(void)
2894 {
2895 	GtkWidget *menu = gtk_menu_new();
2896 	PanelSide side;
2897 
2898 	for (side = 0; side < PANEL_NUMBER_OF_SIDES; ++side)
2899 	{
2900 		GtkWidget *item = gtk_menu_item_new_with_label(
2901 				panel_side_to_translated_name(side));
2902 
2903 		g_signal_connect_swapped(item, "activate",
2904 				G_CALLBACK(panel_add_callback), GINT_TO_POINTER(side));
2905 		gtk_widget_set_sensitive(item, current_panel[side] == NULL);
2906 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
2907 		gtk_widget_show(item);
2908 	}
2909 	return menu;
2910 }
2911 
2912 /* Set the panel to be above/below other windows (setting = TRUE for below),
2913  * and return TRUE.
2914  *
2915  * If o_panel_on_top is set then do nothing and return FALSE.
2916  */
panel_keep_below(Panel * panel,gboolean setting)2917 static gboolean panel_keep_below(Panel *panel, gboolean setting)
2918 {
2919 	if (!o_panel_on_top.int_value)
2920 	{
2921 		keep_below(panel->window->window, setting);
2922 		return TRUE;
2923 	}
2924 	return FALSE;
2925 }
2926 
2927