Adding upstream version 0.9.1.
[debian/lxpanel.git] / src / panel.c
CommitLineData
7dd482c5 1/*
0688b017
AG
2 * Copyright (C) 2006-2010 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
3 * 2006-2008 Jim Huang <jserv.tw@gmail.com>
4 * 2008 Fred Chien <fred@lxde.org>
5 * 2009 Jürgen Hötzel <juergen@archlinux.org>
6 * 2009-2010 Marty Jack <martyj19@comcast.net>
7 * 2010 Lajos Kamocsay <lajos@panka.com>
8 * 2012 Piotr Sipika <Piotr.Sipika@gmail.com>
9 * 2012-2013 Henry Gebhardt <hsggebhardt@gmail.com>
10 * 2012 Jack Chen <speed.up08311990@gmail.com>
11 * 2012 Rafał Mużyło <galtgendo@gmail.com>
12 * 2012 Michael Rawson <michaelrawson76@gmail.com>
13 * 2012 Julien Lavergne <julien.lavergne@gmail.com>
14 * 2013 Rouslan <rouslan-k@users.sourceforge.net>
15 * 2013 peadaredwards <peadaredwards@users.sourceforge.net>
7a1c5048
AG
16 * 2014-2016 Andriy Grytsenko <andrej@rep.kiev.ua>
17 * 2015 Rafał Mużyło <galtgendo@gmail.com>
18 * 2015 Hanno Zulla <hhz@users.sf.net>
0688b017
AG
19 *
20 * This file is a part of LXPanel project.
6cc5e1a6
DB
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software Foundation,
34 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
35 */
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40
41#include <glib/gi18n.h>
42#include <stdlib.h>
7486d297 43#include <glib/gstdio.h>
6cc5e1a6
DB
44#include <sys/types.h>
45#include <sys/stat.h>
46#include <unistd.h>
47#include <errno.h>
48#include <locale.h>
49#include <string.h>
50#include <gdk/gdkx.h>
6b775dbb 51#include <libfm/fm-gtk.h>
f7ecd6ce 52#include <cairo-xlib.h>
6cc5e1a6 53
6b775dbb
AG
54#define __LXPANEL_INTERNALS__
55
56#include "private.h"
6cc5e1a6 57#include "misc.h"
7a1c5048 58#include "space.h"
6cc5e1a6 59
6cc5e1a6
DB
60#include "lxpanelctl.h"
61#include "dbg.h"
f7ecd6ce 62#include "gtk-compat.h"
6cc5e1a6 63
6cc5e1a6
DB
64gchar *cprofile = "default";
65
6cc5e1a6
DB
66GSList* all_panels = NULL; /* a single-linked list storing all panels */
67
7486d297
DB
68gboolean is_in_lxde = FALSE;
69
f7ecd6ce
AG
70static GtkWindowGroup* win_grp = NULL; /* window group used to limit the scope of model dialog. */
71
72static gulong monitors_handler = 0;
73
74static void panel_start_gui(LXPanel *p, config_setting_t *list);
6b775dbb
AG
75static void ah_start(LXPanel *p);
76static void ah_stop(LXPanel *p);
f7ecd6ce
AG
77static void _panel_update_background(LXPanel * p, gboolean enforce);
78
79enum
80{
81 ICON_SIZE_CHANGED,
82 PANEL_FONT_CHANGED,
83 N_SIGNALS
84};
85
86static guint signals[N_SIGNALS];
2ba86315 87
6b775dbb
AG
88G_DEFINE_TYPE(PanelToplevel, lxpanel, GTK_TYPE_WINDOW);
89
90static void lxpanel_finalize(GObject *object)
2ba86315 91{
6b775dbb
AG
92 LXPanel *self = LXPANEL(object);
93 Panel *p = self->priv;
94
95 if( p->config_changed )
96 lxpanel_config_save( self );
97 config_destroy(p->config);
98
f7ecd6ce 99 //XFree(p->workarea);
6b775dbb
AG
100 g_free( p->background_file );
101 g_slist_free( p->system_menus );
102
103 g_free( p->name );
104 g_free(p);
105
106 G_OBJECT_CLASS(lxpanel_parent_class)->finalize(object);
107}
108
f7ecd6ce 109static void panel_stop_gui(LXPanel *self)
6cc5e1a6 110{
6b775dbb
AG
111 Panel *p = self->priv;
112 Display *xdisplay;
113
f7ecd6ce 114 g_debug("panel_stop_gui on '%s'", p->name);
6b775dbb
AG
115 if (p->autohide)
116 ah_stop(self);
117
118 if (p->pref_dialog != NULL)
119 gtk_widget_destroy(p->pref_dialog);
120 p->pref_dialog = NULL;
121
122 if (p->plugin_pref_dialog != NULL)
123 /* just close the dialog, it will do all required cleanup */
124 gtk_dialog_response(GTK_DIALOG(p->plugin_pref_dialog), GTK_RESPONSE_CLOSE);
125
6b775dbb
AG
126
127 if (p->initialized)
128 {
129 gtk_window_group_remove_window(win_grp, GTK_WINDOW(self));
130 xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
131 gdk_flush();
132 XFlush(xdisplay);
133 XSync(xdisplay, True);
134 p->initialized = FALSE;
135 }
f7ecd6ce
AG
136 if (p->surface != NULL)
137 {
138 cairo_surface_destroy(p->surface);
139 p->surface = NULL;
140 }
6b775dbb 141
7dd482c5
AG
142 if (p->background_update_queued)
143 {
144 g_source_remove(p->background_update_queued);
145 p->background_update_queued = 0;
146 }
f7ecd6ce
AG
147 if (p->strut_update_queued)
148 {
149 g_source_remove(p->strut_update_queued);
150 p->strut_update_queued = 0;
151 }
8713e384
AG
152 if (p->reconfigure_queued)
153 {
154 g_source_remove(p->reconfigure_queued);
155 p->reconfigure_queued = 0;
156 }
f7ecd6ce
AG
157
158 if (gtk_bin_get_child(GTK_BIN(self)))
159 {
160 gtk_widget_destroy(p->box);
161 p->box = NULL;
162 }
163}
7dd482c5 164
f7ecd6ce
AG
165#if GTK_CHECK_VERSION(3, 0, 0)
166static void lxpanel_destroy(GtkWidget *object)
167#else
168static void lxpanel_destroy(GtkObject *object)
169#endif
170{
171 LXPanel *self = LXPANEL(object);
172
173 panel_stop_gui(self);
174
175#if GTK_CHECK_VERSION(3, 0, 0)
176 GTK_WIDGET_CLASS(lxpanel_parent_class)->destroy(object);
177#else
6b775dbb 178 GTK_OBJECT_CLASS(lxpanel_parent_class)->destroy(object);
f7ecd6ce 179#endif
6b775dbb
AG
180}
181
7dd482c5 182static gboolean idle_update_background(gpointer p)
2ba86315 183{
7dd482c5
AG
184 LXPanel *panel = LXPANEL(p);
185
186 if (g_source_is_destroyed(g_main_current_source()))
187 return FALSE;
188
6b775dbb 189 /* Panel could be destroyed while background update scheduled */
6b775dbb 190 if (gtk_widget_get_realized(p))
6b775dbb
AG
191 {
192 gdk_display_sync( gtk_widget_get_display(p) );
f7ecd6ce 193 _panel_update_background(panel, FALSE);
6b775dbb 194 }
7dd482c5 195 panel->priv->background_update_queued = 0;
6b775dbb
AG
196
197 return FALSE;
2ba86315 198}
6b775dbb 199
7dd482c5
AG
200void _panel_queue_update_background(LXPanel *panel)
201{
202 if (panel->priv->background_update_queued)
203 return;
204 panel->priv->background_update_queued = g_idle_add_full(G_PRIORITY_HIGH,
205 idle_update_background,
206 panel, NULL);
207}
208
f7ecd6ce
AG
209static gboolean idle_update_strut(gpointer p)
210{
211 LXPanel *panel = LXPANEL(p);
212
213 if (g_source_is_destroyed(g_main_current_source()))
214 return FALSE;
215
216 _panel_set_wm_strut(panel);
217 panel->priv->strut_update_queued = 0;
218
219 return FALSE;
220}
221
6b775dbb 222static void lxpanel_realize(GtkWidget *widget)
2ba86315 223{
6b775dbb
AG
224 GTK_WIDGET_CLASS(lxpanel_parent_class)->realize(widget);
225
7dd482c5 226 _panel_queue_update_background(LXPANEL(widget));
2ba86315
DB
227}
228
6b775dbb 229static void lxpanel_style_set(GtkWidget *widget, GtkStyle* prev)
2ba86315 230{
6b775dbb
AG
231 GTK_WIDGET_CLASS(lxpanel_parent_class)->style_set(widget, prev);
232
233 /* FIXME: This dirty hack is used to fix the background of systray... */
7dd482c5 234 _panel_queue_update_background(LXPANEL(widget));
6b775dbb
AG
235}
236
237static void lxpanel_size_request(GtkWidget *widget, GtkRequisition *req)
238{
f7ecd6ce
AG
239 LXPanel *panel = LXPANEL(widget);
240 Panel *p = panel->priv;
241 GdkRectangle rect;
6b775dbb 242
7a1c5048 243#if !GTK_CHECK_VERSION(3, 0, 0)
6b775dbb 244 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_request(widget, req);
7a1c5048
AG
245#else
246 GTK_WIDGET_CLASS(lxpanel_parent_class)->get_preferred_width(widget, &req->width, &req->width);
247 GTK_WIDGET_CLASS(lxpanel_parent_class)->get_preferred_height(widget, &req->height, &req->height);
248#endif
6b775dbb
AG
249
250 if (!p->visible)
251 /* When the panel is in invisible state, the content box also got hidden, thus always
252 * report 0 size. Ask the content box instead for its size. */
253 gtk_widget_size_request(p->box, req);
254
f7ecd6ce
AG
255 rect.width = req->width;
256 rect.height = req->height;
257 _calculate_position(panel, &rect);
258 req->width = rect.width;
259 req->height = rect.height;
7a1c5048
AG
260 /* update data ahead of configuration request */
261 p->cw = rect.width;
262 p->ch = rect.height;
6b775dbb
AG
263}
264
7a1c5048
AG
265#if GTK_CHECK_VERSION(3, 0, 0)
266static void
267lxpanel_get_preferred_width (GtkWidget *widget,
268 gint *minimal_width,
269 gint *natural_width)
270{
271 GtkRequisition requisition;
272
273 lxpanel_size_request (widget, &requisition);
274
275 if (minimal_width)
276 *minimal_width = requisition.width;
277 if (natural_width)
278 *natural_width = requisition.width;
279}
280
281static void
282lxpanel_get_preferred_height (GtkWidget *widget,
283 gint *minimal_height,
284 gint *natural_height)
285{
286 GtkRequisition requisition;
287
288 lxpanel_size_request (widget, &requisition);
289
290 if (minimal_height)
291 *minimal_height = requisition.height;
292 if (natural_height)
293 *natural_height = requisition.height;
294}
295#endif
296
6b775dbb
AG
297static void lxpanel_size_allocate(GtkWidget *widget, GtkAllocation *a)
298{
f7ecd6ce
AG
299 LXPanel *panel = LXPANEL(widget);
300 Panel *p = panel->priv;
301 GdkRectangle rect;
302 gint x, y;
6b775dbb 303
6d535cca 304 /* some WM like mwm are too generous giving us space more than requested
7a1c5048 305 so let correct it right now, as much as we can */
6d535cca
AG
306 rect.x = a->x;
307 rect.y = a->y;
308 rect.width = MAX(8, MIN(p->cw, a->width));
309 rect.height = MAX(8, MIN(p->ch, a->height));
310 _calculate_position(panel, &rect);
7a1c5048 311
6d535cca 312 GTK_WIDGET_CLASS(lxpanel_parent_class)->size_allocate(widget, &rect);
6b775dbb
AG
313
314 if (p->widthtype == WIDTH_REQUEST)
6d535cca 315 p->width = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? rect.width : rect.height;
6b775dbb 316 if (p->heighttype == HEIGHT_REQUEST)
6d535cca 317 p->height = (p->orientation == GTK_ORIENTATION_HORIZONTAL) ? rect.height : rect.width;
6b775dbb 318
f7ecd6ce
AG
319 if (!gtk_widget_get_realized(widget))
320 return;
321
f7ecd6ce
AG
322 /* get real coords since a contains 0, 0 */
323 gdk_window_get_origin(gtk_widget_get_window(widget), &x, &y);
f7ecd6ce
AG
324 p->ax = rect.x;
325 p->ay = rect.y;
326
6d535cca 327 if (rect.width != p->aw || rect.height != p->ah || x != p->ax || y != p->ay)
6b775dbb 328 {
6d535cca
AG
329 p->aw = rect.width;
330 p->ah = rect.height;
331 gtk_window_move(GTK_WINDOW(widget), p->ax, p->ay);
f7ecd6ce
AG
332 /* SF bug #708: strut update does not work while in size allocation */
333 if (!panel->priv->strut_update_queued)
334 panel->priv->strut_update_queued = g_idle_add_full(G_PRIORITY_HIGH,
335 idle_update_strut,
336 panel, NULL);
337 _panel_queue_update_background(panel);
7dd482c5 338 }
f7ecd6ce
AG
339
340 if (gtk_widget_get_mapped(widget))
341 _panel_establish_autohide(panel);
6b775dbb
AG
342}
343
344static gboolean lxpanel_configure_event (GtkWidget *widget, GdkEventConfigure *e)
345{
346 Panel *p = LXPANEL(widget)->priv;
347
6b775dbb
AG
348 p->cw = e->width;
349 p->ch = e->height;
350 p->cx = e->x;
351 p->cy = e->y;
352
6b775dbb
AG
353 return GTK_WIDGET_CLASS(lxpanel_parent_class)->configure_event(widget, e);
354}
355
356static gboolean lxpanel_map_event(GtkWidget *widget, GdkEventAny *event)
357{
358 Panel *p = PLUGIN_PANEL(widget)->priv;
359
360 if (p->autohide)
361 ah_start(LXPANEL(widget));
362 return GTK_WIDGET_CLASS(lxpanel_parent_class)->map_event(widget, event);
363}
364
365/* Handler for "button_press_event" signal with Panel as parameter. */
366static gboolean lxpanel_button_press(GtkWidget *widget, GdkEventButton *event)
367{
7a1c5048
AG
368 LXPanel *panel = PLUGIN_PANEL(widget);
369
370 if ((event->state & gtk_accelerator_get_default_mod_mask()) != 0)
371 /* ignore clicks with modifiers */
372 return FALSE;
373
6b775dbb
AG
374 if (event->button == 3) /* right button */
375 {
7a1c5048 376 GtkMenu* popup = (GtkMenu*) lxpanel_get_plugin_menu(panel, NULL, FALSE);
6b775dbb
AG
377 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
378 return TRUE;
379 }
7a1c5048
AG
380 else if (event->button == 2) /* middle button */
381 {
382 Panel *p = panel->priv;
383 if (p->move_state == PANEL_MOVE_STOP)
384 {
385 gdk_window_get_origin(event->window, &p->move_x, &p->move_y);
386 p->move_x += event->x - p->ax;
387 p->move_y += event->y - p->ay;
388 p->move_state = PANEL_MOVE_DETECT;
389 p->move_device = event->device;
390 /* rest of work see in panel-plugin-move.c file */
391 return TRUE;
392 }
393 }
19ab5cea 394 return FALSE;
6b775dbb
AG
395}
396
397static void lxpanel_class_init(PanelToplevelClass *klass)
398{
399 GObjectClass *gobject_class = (GObjectClass *)klass;
f7ecd6ce 400#if !GTK_CHECK_VERSION(3, 0, 0)
6b775dbb 401 GtkObjectClass *gtk_object_class = (GtkObjectClass *)klass;
f7ecd6ce 402#endif
6b775dbb
AG
403 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
404
405 gobject_class->finalize = lxpanel_finalize;
f7ecd6ce
AG
406#if GTK_CHECK_VERSION(3, 0, 0)
407 widget_class->destroy = lxpanel_destroy;
408#else
6b775dbb 409 gtk_object_class->destroy = lxpanel_destroy;
f7ecd6ce 410#endif
6b775dbb 411 widget_class->realize = lxpanel_realize;
7a1c5048
AG
412#if GTK_CHECK_VERSION(3, 0, 0)
413 widget_class->get_preferred_width = lxpanel_get_preferred_width;
414 widget_class->get_preferred_height = lxpanel_get_preferred_height;
415#else
6b775dbb 416 widget_class->size_request = lxpanel_size_request;
7a1c5048 417#endif
6b775dbb
AG
418 widget_class->size_allocate = lxpanel_size_allocate;
419 widget_class->configure_event = lxpanel_configure_event;
420 widget_class->style_set = lxpanel_style_set;
421 widget_class->map_event = lxpanel_map_event;
422 widget_class->button_press_event = lxpanel_button_press;
7a1c5048
AG
423 widget_class->button_release_event = _lxpanel_button_release;
424 widget_class->motion_notify_event = _lxpanel_motion_notify;
f7ecd6ce
AG
425
426 signals[ICON_SIZE_CHANGED] =
427 g_signal_new("icon-size-changed",
428 G_TYPE_FROM_CLASS(klass),
429 G_SIGNAL_RUN_LAST,
430 G_STRUCT_OFFSET(PanelToplevelClass, icon_size_changed),
431 NULL, NULL,
432 g_cclosure_marshal_VOID__VOID,
433 G_TYPE_NONE, 0, G_TYPE_NONE);
434
435 signals[PANEL_FONT_CHANGED] =
436 g_signal_new("panel-font-changed",
437 G_TYPE_FROM_CLASS(klass),
438 G_SIGNAL_RUN_LAST,
439 G_STRUCT_OFFSET(PanelToplevelClass, panel_font_changed),
440 NULL, NULL,
441 g_cclosure_marshal_VOID__VOID,
442 G_TYPE_NONE, 0, G_TYPE_NONE);
6b775dbb
AG
443}
444
445static void lxpanel_init(PanelToplevel *self)
446{
447 Panel *p = g_new0(Panel, 1);
448
449 self->priv = p;
450 p->topgwin = self;
f7ecd6ce 451 p->align = ALIGN_CENTER;
2ba86315
DB
452 p->edge = EDGE_NONE;
453 p->widthtype = WIDTH_PERCENT;
454 p->width = 100;
455 p->heighttype = HEIGHT_PIXEL;
456 p->height = PANEL_HEIGHT_DEFAULT;
6b775dbb 457 p->monitor = 0;
2ba86315
DB
458 p->setdocktype = 1;
459 p->setstrut = 1;
460 p->round_corners = 0;
461 p->autohide = 0;
462 p->visible = TRUE;
463 p->height_when_hidden = 2;
464 p->transparent = 0;
32a67dc7
DB
465 p->alpha = 255;
466 gdk_color_parse("white", &p->gtintcolor);
467 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
2ba86315
DB
468 p->usefontcolor = 0;
469 p->fontcolor = 0x00000000;
514580cf
DB
470 p->usefontsize = 0;
471 p->fontsize = 10;
2ba86315
DB
472 p->spacing = 0;
473 p->icon_size = PANEL_ICON_SIZE;
aaccad27 474 p->icon_theme = gtk_icon_theme_get_default();
6b775dbb 475 p->config = config_new();
19ab5cea 476 p->defstyle = gtk_widget_get_default_style();
6b775dbb
AG
477}
478
479/* Allocate and initialize new Panel structure. */
7a1c5048 480static LXPanel* panel_allocate(GdkScreen *screen)
6b775dbb 481{
f7ecd6ce
AG
482 return g_object_new(LX_TYPE_PANEL,
483 "border-width", 0,
484 "decorated", FALSE,
485 "name", "PanelToplevel",
486 "resizable", FALSE,
487 "title", "panel",
488 "type-hint", GDK_WINDOW_TYPE_HINT_DOCK,
489 "window-position", GTK_WIN_POS_NONE,
7a1c5048 490 "screen", screen,
f7ecd6ce
AG
491 NULL);
492}
493
494void _panel_emit_icon_size_changed(LXPanel *p)
495{
496 g_signal_emit(p, signals[ICON_SIZE_CHANGED], 0);
497}
498
499void _panel_emit_font_changed(LXPanel *p)
500{
501 g_signal_emit(p, signals[PANEL_FONT_CHANGED], 0);
2ba86315
DB
502}
503
504/* Normalize panel configuration after load from file or reconfiguration. */
505static void panel_normalize_configuration(Panel* p)
506{
507 panel_set_panel_configuration_changed( p );
508 if (p->width < 0)
509 p->width = 100;
510 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
511 p->width = 100;
512 p->heighttype = HEIGHT_PIXEL;
513 if (p->heighttype == HEIGHT_PIXEL) {
514 if (p->height < PANEL_HEIGHT_MIN)
515 p->height = PANEL_HEIGHT_MIN;
516 else if (p->height > PANEL_HEIGHT_MAX)
517 p->height = PANEL_HEIGHT_MAX;
518 }
6b775dbb 519 if (p->monitor < 0)
f7ecd6ce 520 p->monitor = -1;
2ba86315
DB
521 if (p->background)
522 p->transparent = 0;
6cc5e1a6 523}
6cc5e1a6 524
f7ecd6ce
AG
525gboolean _panel_edge_can_strut(LXPanel *panel, int edge, gint monitor, gulong *size)
526{
527 Panel *p;
528 GdkScreen *screen;
529 GdkRectangle rect;
530 GdkRectangle rect2;
531 gint n, i;
532 gulong s;
533
534 if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
535 return FALSE;
536
537 p = panel->priv;
538 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
539 if (p->autohide)
540 s = p->height_when_hidden;
541 else switch (edge)
542 {
543 case EDGE_LEFT:
544 case EDGE_RIGHT:
545 s = p->aw;
546 break;
547 case EDGE_TOP:
548 case EDGE_BOTTOM:
549 s = p->ah;
550 break;
551 default: /* error! */
552 return FALSE;
553 }
554 if (s == 0)
555 return FALSE; /* nothing to strut here */
556
557 if (monitor < 0) /* screen span */
558 {
559 if (G_LIKELY(size))
560 *size = s;
561 return TRUE;
562 }
563
564 screen = gtk_widget_get_screen(GTK_WIDGET(panel));
565 n = gdk_screen_get_n_monitors(screen);
566 if (monitor >= n) /* hidden now */
567 return FALSE;
568 gdk_screen_get_monitor_geometry(screen, monitor, &rect);
569 switch (edge)
570 {
571 case EDGE_LEFT:
572 rect.width = rect.x;
573 rect.x = 0;
574 s += rect.width;
575 break;
576 case EDGE_RIGHT:
577 rect.x += rect.width;
578 rect.width = gdk_screen_get_width(screen) - rect.x;
579 s += rect.width;
580 break;
581 case EDGE_TOP:
582 rect.height = rect.y;
583 rect.y = 0;
584 s += rect.height;
585 break;
586 case EDGE_BOTTOM:
587 rect.y += rect.height;
588 rect.height = gdk_screen_get_height(screen) - rect.y;
589 s += rect.height;
590 break;
591 default: ;
592 }
593 if (rect.height == 0 || rect.width == 0) ; /* on a border of monitor */
594 else
595 {
596 for (i = 0; i < n; i++)
597 {
598 if (i == monitor)
599 continue;
600 gdk_screen_get_monitor_geometry(screen, i, &rect2);
601 if (gdk_rectangle_intersect(&rect, &rect2, NULL))
602 /* that monitor lies over the edge */
603 return FALSE;
604 }
605 }
606 if (G_LIKELY(size))
607 *size = s;
608 return TRUE;
609}
610
2ba86315
DB
611/****************************************************
612 * panel's handlers for WM events *
613 ****************************************************/
6cc5e1a6
DB
614
615void panel_set_wm_strut(Panel *p)
616{
6b775dbb
AG
617 _panel_set_wm_strut(p->topgwin);
618}
619
620void _panel_set_wm_strut(LXPanel *panel)
621{
2ba86315 622 int index;
6b775dbb
AG
623 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
624 Panel *p = panel->priv;
2ba86315
DB
625 gulong strut_size;
626 gulong strut_lower;
627 gulong strut_upper;
6cc5e1a6 628
6b775dbb 629 if (!gtk_widget_get_mapped(GTK_WIDGET(panel)))
6b775dbb
AG
630 return;
631 /* most wm's tend to ignore struts of unmapped windows, and that's how
632 * lxpanel hides itself. so no reason to set it. */
633 if (p->autohide && p->height_when_hidden <= 0)
634 return;
635
2ba86315
DB
636 /* Dispatch on edge to set up strut parameters. */
637 switch (p->edge)
6cc5e1a6 638 {
2ba86315
DB
639 case EDGE_LEFT:
640 index = 0;
2ba86315
DB
641 strut_lower = p->ay;
642 strut_upper = p->ay + p->ah;
643 break;
644 case EDGE_RIGHT:
645 index = 1;
2ba86315
DB
646 strut_lower = p->ay;
647 strut_upper = p->ay + p->ah;
648 break;
649 case EDGE_TOP:
650 index = 2;
2ba86315
DB
651 strut_lower = p->ax;
652 strut_upper = p->ax + p->aw;
653 break;
654 case EDGE_BOTTOM:
655 index = 3;
2ba86315
DB
656 strut_lower = p->ax;
657 strut_upper = p->ax + p->aw;
658 break;
659 default:
660 return;
6cc5e1a6 661 }
6cc5e1a6 662
2ba86315 663 /* Set up strut value in property format. */
7dd482c5 664 gulong desired_strut[12];
2ba86315 665 memset(desired_strut, 0, sizeof(desired_strut));
f7ecd6ce
AG
666 if (p->setstrut &&
667 _panel_edge_can_strut(panel, p->edge, p->monitor, &strut_size))
2ba86315
DB
668 {
669 desired_strut[index] = strut_size;
670 desired_strut[4 + index * 2] = strut_lower;
f7ecd6ce 671 desired_strut[5 + index * 2] = strut_upper - 1;
2ba86315
DB
672 }
673 else
674 {
675 strut_size = 0;
676 strut_lower = 0;
677 strut_upper = 0;
678 }
6cc5e1a6 679
2ba86315
DB
680 /* If strut value changed, set the property value on the panel window.
681 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
6b775dbb 682 if ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge))
2ba86315
DB
683 {
684 p->strut_size = strut_size;
685 p->strut_lower = strut_lower;
686 p->strut_upper = strut_upper;
687 p->strut_edge = p->edge;
688
689 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
690 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
691 if (strut_size != 0)
692 {
6b775dbb 693 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL,
2ba86315 694 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
6b775dbb 695 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STRUT,
2ba86315
DB
696 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
697 }
698 else
699 {
6b775dbb
AG
700 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT);
701 XDeleteProperty(xdisplay, p->topxwin, a_NET_WM_STRUT_PARTIAL);
2ba86315
DB
702 }
703 }
6cc5e1a6 704}
6cc5e1a6 705
f7ecd6ce
AG
706/****************************************************
707 * panel's handlers for GTK events *
708 ****************************************************/
709
710static void paint_root_pixmap(LXPanel *panel, cairo_t *cr)
6cc5e1a6 711{
f7ecd6ce
AG
712 /*
713 * this code was extracted from code for FbBg object
714 *
715 * Copyright (C) 2001, 2002 Ian McKellar <yakk@yakk.net>
716 * 2002 Sun Microsystems, Inc.
717 */
718 XGCValues gcv;
719 uint mask;
720 Window xroot;
721 GC gc;
722 Display *dpy;
723 Pixmap *prop;
724#if GTK_CHECK_VERSION(3, 0, 0)
725 cairo_surface_t *surface;
726#else
727 GdkPixmap *pixmap;
728#endif
729 Pixmap xpixmap;
730 Panel *p = panel->priv;
6b775dbb 731
f7ecd6ce
AG
732 dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
733 xroot = DefaultRootWindow(dpy);
734 gcv.ts_x_origin = 0;
735 gcv.ts_y_origin = 0;
736 gcv.fill_style = FillTiled;
737 mask = GCTileStipXOrigin | GCTileStipYOrigin | GCFillStyle;
738 prop = get_xaproperty(xroot, a_XROOTPMAP_ID, XA_PIXMAP, NULL);
739 if (prop)
740 {
741 gcv.tile = *prop;
742 mask |= GCTile;
743 XFree(prop);
744 }
745 gc = XCreateGC(dpy, xroot, mask, &gcv);
746#if GTK_CHECK_VERSION(3, 0, 0)
747 xpixmap = XCreatePixmap(dpy, xroot, p->aw, p->ah,
748 DefaultDepth(dpy, DefaultScreen(dpy)));
749 surface = cairo_xlib_surface_create(dpy, xpixmap,
750 DefaultVisual(dpy, DefaultScreen(dpy)),
751 p->aw, p->ah);
752#else
753 pixmap = gdk_pixmap_new(gtk_widget_get_window(GTK_WIDGET(panel)),
754 p->aw, p->ah, -1);
755 xpixmap = gdk_x11_drawable_get_xid(pixmap);
756#endif
757 XSetTSOrigin(dpy, gc, -p->ax, -p->ay);
758 XFillRectangle(dpy, xpixmap, gc, 0, 0, p->aw, p->ah);
759 XFreeGC(dpy, gc);
760#if GTK_CHECK_VERSION(3, 0, 0)
761 cairo_set_source_surface(cr, surface, 0, 0);
762#else
763 gdk_cairo_set_source_pixmap(cr, pixmap, 0, 0);
764#endif
765 cairo_paint(cr);
766#if GTK_CHECK_VERSION(3, 0, 0)
767 cairo_surface_destroy(surface);
768 XFreePixmap(dpy, xpixmap);
769#else
770 g_object_unref(pixmap);
7486d297 771#endif
6cc5e1a6
DB
772}
773
f7ecd6ce 774void _panel_determine_background_pixmap(LXPanel * panel)
6cc5e1a6 775{
f7ecd6ce
AG
776#if GTK_CHECK_VERSION(3, 0, 0)
777 cairo_pattern_t *pattern;
778#else
779 GdkPixmap * pixmap = NULL;
780#endif
781 GtkWidget * widget = GTK_WIDGET(panel);
782 GdkWindow * window = gtk_widget_get_window(widget);
783 Panel * p = panel->priv;
784 cairo_t *cr;
785 gint x = 0, y = 0;
786
787 if (!p->background && !p->transparent)
788 goto not_paintable;
789 else if (p->aw <= 1 || p->ah <= 1)
790 goto not_paintable;
791 else if (p->surface == NULL)
aa0e9095 792 {
f7ecd6ce 793 GdkPixbuf *pixbuf = NULL;
6cc5e1a6 794
f7ecd6ce
AG
795 p->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, p->aw, p->ah);
796 cr = cairo_create(p->surface);
797 if (p->background)
aa0e9095 798 {
f7ecd6ce
AG
799 /* User specified background pixmap. */
800 pixbuf = gdk_pixbuf_new_from_file(p->background_file, NULL);
aa0e9095 801 }
f7ecd6ce
AG
802 if ((p->transparent && p->alpha != 255) || /* ignore it for opaque panel */
803 (pixbuf != NULL && gdk_pixbuf_get_has_alpha(pixbuf)))
aa0e9095 804 {
f7ecd6ce
AG
805 /* Transparent. Determine the appropriate value from the root pixmap. */
806 paint_root_pixmap(panel, cr);
aa0e9095 807 }
f7ecd6ce 808 if (pixbuf != NULL)
aa0e9095 809 {
f7ecd6ce
AG
810 gint w = gdk_pixbuf_get_width(pixbuf);
811 gint h = gdk_pixbuf_get_height(pixbuf);
812
813 /* Tile the image */
814 for (y = 0; y < p->ah; y += h)
815 for (x = 0; x < p->aw; x += w)
816 {
817 gdk_cairo_set_source_pixbuf(cr, pixbuf, x, y);
818 cairo_paint(cr);
6cc5e1a6 819 }
f7ecd6ce
AG
820 y = 0;
821 g_object_unref(pixbuf);
aa0e9095 822 }
f7ecd6ce 823 else
aa0e9095 824 {
f7ecd6ce
AG
825 /* Either color is set or image is invalid, fill the background */
826 gdk_cairo_set_source_color(cr, &p->gtintcolor);
827 cairo_paint_with_alpha(cr, p->transparent ? (double)p->alpha/255 : 1.0);
aa0e9095 828 }
f7ecd6ce 829 cairo_destroy(cr);
2ba86315 830 }
6cc5e1a6 831
f7ecd6ce 832 if (p->surface != NULL)
2ba86315 833 {
f7ecd6ce
AG
834 gtk_widget_set_app_paintable(widget, TRUE);
835#if GTK_CHECK_VERSION(3, 0, 0)
836 pattern = cairo_pattern_create_for_surface(p->surface);
837 gdk_window_set_background_pattern(window, pattern);
838 cairo_pattern_destroy(pattern);
839#else
840 pixmap = gdk_pixmap_new(window, p->aw, p->ah, -1);
841 cr = gdk_cairo_create(pixmap);
842 cairo_set_source_surface(cr, p->surface, 0, 0);
843 cairo_paint(cr);
844 cairo_destroy(cr);
845 gdk_window_set_back_pixmap(window, pixmap, FALSE);
846 g_object_unref(pixmap);
847#endif
6cc5e1a6 848 }
f7ecd6ce 849 else
6cc5e1a6 850 {
f7ecd6ce
AG
851not_paintable:
852 gtk_widget_set_app_paintable(widget, FALSE);
6cc5e1a6 853 }
f7ecd6ce 854}
6cc5e1a6 855
f7ecd6ce
AG
856void panel_determine_background_pixmap(Panel * panel, GtkWidget * widget, GdkWindow * window)
857{
858 if (GTK_WIDGET(panel->topgwin) != widget)
6cc5e1a6 859 {
f7ecd6ce
AG
860 /* Backward compatibility:
861 reset background for the child, using background of panel */
862 gtk_widget_set_app_paintable(widget, (panel->background || panel->transparent));
863#if GTK_CHECK_VERSION(3, 0, 0)
864 gdk_window_set_background_pattern(window, NULL);
865#else
866 gdk_window_set_back_pixmap(window, NULL, TRUE);
867#endif
6cc5e1a6
DB
868 }
869 else
f7ecd6ce 870 _panel_determine_background_pixmap(panel->topgwin);
2ba86315 871}
6cc5e1a6 872
2ba86315
DB
873/* Update the background of the entire panel.
874 * This function should only be called after the panel has been realized. */
875void panel_update_background(Panel * p)
876{
f7ecd6ce 877 _panel_update_background(p->topgwin, TRUE);
6b775dbb
AG
878}
879
f7ecd6ce 880static void _panel_update_background(LXPanel * p, gboolean enforce)
6b775dbb
AG
881{
882 GtkWidget *w = GTK_WIDGET(p);
f7ecd6ce
AG
883 GList *plugins = NULL, *l;
884
885 /* reset background image */
886 if (p->priv->surface != NULL) /* FIXME: honor enforce on composited screen */
887 {
888 cairo_surface_destroy(p->priv->surface);
889 p->priv->surface = NULL;
890 }
6b775dbb 891
2ba86315 892 /* Redraw the top level widget. */
f7ecd6ce 893 _panel_determine_background_pixmap(p);
7a1c5048 894#if !GTK_CHECK_VERSION(3, 0, 0)
6b775dbb 895 gdk_window_clear(gtk_widget_get_window(w));
7a1c5048 896#endif
6b775dbb 897 gtk_widget_queue_draw(w);
2ba86315
DB
898
899 /* Loop over all plugins redrawing each plugin. */
f7ecd6ce
AG
900 if (p->priv->box != NULL)
901 plugins = gtk_container_get_children(GTK_CONTAINER(p->priv->box));
6b775dbb
AG
902 for (l = plugins; l != NULL; l = l->next)
903 plugin_widget_set_background(l->data, p);
904 g_list_free(plugins);
6cc5e1a6
DB
905}
906
6b775dbb
AG
907/****************************************************
908 * autohide : borrowed from fbpanel *
909 ****************************************************/
6cc5e1a6 910
6b775dbb
AG
911/* Autohide is behaviour when panel hides itself when mouse is "far enough"
912 * and pops up again when mouse comes "close enough".
913 * Formally, it's a state machine with 3 states that driven by mouse
914 * coordinates and timer:
915 * 1. VISIBLE - ensures that panel is visible. When/if mouse goes "far enough"
916 * switches to WAITING state
917 * 2. WAITING - starts timer. If mouse comes "close enough", stops timer and
918 * switches to VISIBLE. If timer expires, switches to HIDDEN
919 * 3. HIDDEN - hides panel. When mouse comes "close enough" switches to VISIBLE
920 *
921 * Note 1
922 * Mouse coordinates are queried every PERIOD milisec
923 *
924 * Note 2
925 * If mouse is less then GAP pixels to panel it's considered to be close,
926 * otherwise it's far
927 */
6cc5e1a6 928
6b775dbb
AG
929#define GAP 2
930#define PERIOD 300
931
932typedef enum
6cc5e1a6 933{
6b775dbb
AG
934 AH_STATE_VISIBLE,
935 AH_STATE_WAITING,
936 AH_STATE_HIDDEN
937} PanelAHState;
6cc5e1a6 938
6b775dbb
AG
939static void ah_state_set(LXPanel *p, PanelAHState ah_state);
940
941static gboolean
942mouse_watch(LXPanel *panel)
6cc5e1a6 943{
6b775dbb
AG
944 Panel *p = panel->priv;
945 gint x, y;
946
947 if (g_source_is_destroyed(g_main_current_source()))
948 return FALSE;
949
6cc5e1a6 950 ENTER;
6b775dbb 951 gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
6cc5e1a6 952
6b775dbb
AG
953/* Reduce sensitivity area
954 p->ah_far = ((x < p->cx - GAP) || (x > p->cx + p->cw + GAP)
955 || (y < p->cy - GAP) || (y > p->cy + p->ch + GAP));
956*/
eea54180 957
6b775dbb 958 gint cx, cy, cw, ch, gap;
eea54180 959
7dd482c5
AG
960 cx = p->ax;
961 cy = p->ay;
19ab5cea
AG
962 cw = p->cw;
963 ch = p->ch;
eea54180 964
7a1c5048
AG
965 if (p->move_state != PANEL_MOVE_STOP)
966 /* prevent autohide when dragging is on */
967 return TRUE;
968
7dd482c5
AG
969 if (cw == 1) cw = 0;
970 if (ch == 1) ch = 0;
6b775dbb
AG
971 /* reduce area which will raise panel so it does not interfere with apps */
972 if (p->ah_state == AH_STATE_HIDDEN) {
973 gap = MAX(p->height_when_hidden, GAP);
974 switch (p->edge) {
975 case EDGE_LEFT:
976 cw = gap;
977 break;
978 case EDGE_RIGHT:
979 cx = cx + cw - gap;
980 cw = gap;
981 break;
982 case EDGE_TOP:
983 ch = gap;
984 break;
985 case EDGE_BOTTOM:
986 cy = cy + ch - gap;
987 ch = gap;
988 break;
989 }
990 }
991 p->ah_far = ((x < cx) || (x > cx + cw) || (y < cy) || (y > cy + ch));
6cc5e1a6 992
6b775dbb
AG
993 ah_state_set(panel, p->ah_state);
994 RET(TRUE);
6cc5e1a6
DB
995}
996
6b775dbb 997static gboolean ah_state_hide_timeout(gpointer p)
6cc5e1a6 998{
6b775dbb
AG
999 if (!g_source_is_destroyed(g_main_current_source()))
1000 {
1001 ah_state_set(p, AH_STATE_HIDDEN);
1002 ((LXPanel *)p)->priv->hide_timeout = 0;
6cc5e1a6 1003 }
6b775dbb 1004 return FALSE;
6cc5e1a6
DB
1005}
1006
6b775dbb 1007static void ah_state_set(LXPanel *panel, PanelAHState ah_state)
6cc5e1a6 1008{
6b775dbb 1009 Panel *p = panel->priv;
f7ecd6ce 1010 GdkRectangle rect;
6b775dbb 1011
6cc5e1a6 1012 ENTER;
6b775dbb
AG
1013 if (p->ah_state != ah_state) {
1014 p->ah_state = ah_state;
1015 switch (ah_state) {
1016 case AH_STATE_VISIBLE:
f7ecd6ce
AG
1017 p->visible = TRUE;
1018 _calculate_position(panel, &rect);
1019 gtk_window_move(GTK_WINDOW(panel), rect.x, rect.y);
6b775dbb
AG
1020 gtk_widget_show(GTK_WIDGET(panel));
1021 gtk_widget_show(p->box);
7dd482c5 1022 gtk_widget_queue_resize(GTK_WIDGET(panel));
6b775dbb 1023 gtk_window_stick(GTK_WINDOW(panel));
6b775dbb
AG
1024 break;
1025 case AH_STATE_WAITING:
7dd482c5
AG
1026 if (p->hide_timeout)
1027 g_source_remove(p->hide_timeout);
6b775dbb
AG
1028 p->hide_timeout = g_timeout_add(2 * PERIOD, ah_state_hide_timeout, panel);
1029 break;
1030 case AH_STATE_HIDDEN:
1031 if (p->height_when_hidden > 0)
1032 gtk_widget_hide(p->box);
1033 else
1034 gtk_widget_hide(GTK_WIDGET(panel));
1035 p->visible = FALSE;
1036 }
1037 } else if (p->autohide && p->ah_far) {
1038 switch (ah_state) {
1039 case AH_STATE_VISIBLE:
1040 ah_state_set(panel, AH_STATE_WAITING);
1041 break;
1042 case AH_STATE_WAITING:
1043 break;
1044 case AH_STATE_HIDDEN:
1045 /* configurator might change height_when_hidden value */
1046 if (p->height_when_hidden > 0)
1047 {
1048 if (gtk_widget_get_visible(p->box))
1049 {
1050 gtk_widget_hide(p->box);
1051 gtk_widget_show(GTK_WIDGET(panel));
1052 }
1053 }
1054 else
1055 if (gtk_widget_get_visible(GTK_WIDGET(panel)))
1056 {
1057 gtk_widget_hide(GTK_WIDGET(panel));
1058 gtk_widget_show(p->box);
1059 }
1060 }
1061 } else {
1062 switch (ah_state) {
1063 case AH_STATE_VISIBLE:
1064 break;
1065 case AH_STATE_WAITING:
1066 if (p->hide_timeout)
1067 g_source_remove(p->hide_timeout);
1068 p->hide_timeout = 0;
1069 /* continue with setting visible */
1070 case AH_STATE_HIDDEN:
1071 ah_state_set(panel, AH_STATE_VISIBLE);
1072 }
1073 }
1074 RET();
1075}
6cc5e1a6 1076
6b775dbb
AG
1077/* starts autohide behaviour */
1078static void ah_start(LXPanel *p)
1079{
1080 ENTER;
1081 if (!p->priv->mouse_timeout)
1082 p->priv->mouse_timeout = g_timeout_add(PERIOD, (GSourceFunc) mouse_watch, p);
1083 RET();
1084}
6cc5e1a6 1085
6b775dbb
AG
1086/* stops autohide */
1087static void ah_stop(LXPanel *p)
1088{
1089 ENTER;
1090 if (p->priv->mouse_timeout) {
1091 g_source_remove(p->priv->mouse_timeout);
1092 p->priv->mouse_timeout = 0;
1093 }
1094 if (p->priv->hide_timeout) {
1095 g_source_remove(p->priv->hide_timeout);
1096 p->priv->hide_timeout = 0;
1097 }
1098 RET();
6cc5e1a6 1099}
6b775dbb
AG
1100/* end of the autohide code
1101 * ------------------------------------------------------------- */
6cc5e1a6
DB
1102
1103static gint
1104panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
1105{
6b775dbb 1106 panel_configure( (LXPanel*)user_data, 0 );
6cc5e1a6
DB
1107 return TRUE;
1108}
1109
6b775dbb 1110static void panel_popupmenu_config_plugin( GtkMenuItem* item, GtkWidget* plugin )
6cc5e1a6 1111{
6b775dbb 1112 Panel *panel = PLUGIN_PANEL(plugin)->priv;
6cc5e1a6 1113
6b775dbb 1114 lxpanel_plugin_show_config_dialog(plugin);
39c13576
DB
1115
1116 /* FIXME: this should be more elegant */
6b775dbb 1117 panel->config_changed = TRUE;
6cc5e1a6
DB
1118}
1119
6b775dbb 1120static void panel_popupmenu_add_item( GtkMenuItem* item, LXPanel* panel )
6cc5e1a6
DB
1121{
1122 /* panel_add_plugin( panel, panel->topgwin ); */
2ba86315 1123 panel_configure( panel, 2 );
6cc5e1a6
DB
1124}
1125
6b775dbb 1126static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
6cc5e1a6 1127{
7a1c5048
AG
1128 lxpanel_remove_plugin(PLUGIN_PANEL(plugin), plugin);
1129}
1130
1131void lxpanel_remove_plugin(LXPanel *p, GtkWidget *plugin)
1132{
1133 Panel* panel = p->priv;
4652f59b
DB
1134
1135 /* If the configuration dialog is open, there will certainly be a crash if the
1136 * user manipulates the Configured Plugins list, after we remove this entry.
1137 * Close the configuration dialog if it is open. */
1138 if (panel->pref_dialog != NULL)
1139 {
1140 gtk_widget_destroy(panel->pref_dialog);
1141 panel->pref_dialog = NULL;
1142 }
7a1c5048
AG
1143 _lxpanel_remove_plugin(p, plugin);
1144}
1145
1146void _lxpanel_remove_plugin(LXPanel *p, GtkWidget *plugin)
1147{
1148 Panel* panel = p->priv;
1149 GtkWidget *prev, *next;
1150 GList *children;
1151 GtkAllocation alloc;
1152 gint idx = -1, size1, size2;
1153 gboolean expand;
1154
6b775dbb
AG
1155 config_setting_destroy(g_object_get_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf));
1156 /* reset conf pointer because the widget still may be referenced by configurator */
1157 g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
4652f59b 1158
7a1c5048
AG
1159 /* squash previous and next spaces if this plugin was between two spaces */
1160 children = gtk_container_get_children(GTK_CONTAINER(panel->box));
1161 idx = g_list_index(children, plugin);
1162 if (idx > 0)
1163 {
1164
1165 prev = g_list_nth_data(children, idx - 1);
1166 next = g_list_nth_data(children, idx + 1);
1167 if (next && PANEL_IS_SPACE(next) && PANEL_IS_SPACE(prev))
1168 {
1169 expand = FALSE;
1170 gtk_container_child_get(GTK_CONTAINER(panel->box), prev, "expand", &expand, NULL);
1171 if (expand == TRUE)
1172 {
1173 /* prev is expandable, remove next */
1174 config_setting_destroy(g_object_get_qdata(G_OBJECT(next), lxpanel_plugin_qconf));
1175 g_object_set_qdata(G_OBJECT(next), lxpanel_plugin_qconf, NULL);
1176 gtk_widget_destroy(next);
1177 }
1178 else
1179 {
1180 gtk_container_child_get(GTK_CONTAINER(panel->box), next, "expand", &expand, NULL);
1181 if (expand == TRUE)
1182 {
1183 /* next is expandable, remove prev */
1184 config_setting_destroy(g_object_get_qdata(G_OBJECT(prev), lxpanel_plugin_qconf));
1185 g_object_set_qdata(G_OBJECT(prev), lxpanel_plugin_qconf, NULL);
1186 gtk_widget_destroy(prev);
1187 }
1188 else
1189 {
1190 /* calculate size of prev -- add size of this and next to it */
1191 size1 = _panel_space_get_size(prev);
1192 size2 = _panel_space_get_size(next);
1193 gtk_widget_get_allocation(plugin, &alloc);
1194 if (panel->orientation == GTK_ORIENTATION_HORIZONTAL)
1195 size1 += alloc.width + size2;
1196 else
1197 size1 += alloc.height + size2;
1198 /* remove next */
1199 config_setting_destroy(g_object_get_qdata(G_OBJECT(next), lxpanel_plugin_qconf));
1200 g_object_set_qdata(G_OBJECT(next), lxpanel_plugin_qconf, NULL);
1201 gtk_widget_destroy(next);
1202 _panel_space_resize(prev, size1);
1203 }
1204 }
1205 }
1206 }
1207 g_list_free(children);
1208
1209 lxpanel_config_save(p);
6b775dbb 1210 gtk_widget_destroy(plugin);
6cc5e1a6
DB
1211}
1212
1213/* FIXME: Potentially we can support multiple panels at the same edge,
1214 * but currently this cannot be done due to some positioning problems. */
6b775dbb 1215static char* gen_panel_name( int edge, gint monitor )
6cc5e1a6
DB
1216{
1217 const char* edge_str = num2str( edge_pair, edge, "" );
1218 char* name = NULL;
6b775dbb 1219 char* dir = _user_config_file_name("panels", NULL);
6cc5e1a6
DB
1220 int i;
1221 for( i = 0; i < G_MAXINT; ++i )
1222 {
1223 char* f;
6b775dbb
AG
1224 if(monitor != 0)
1225 name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
1226 else if( G_LIKELY( i > 0 ) )
6cc5e1a6
DB
1227 name = g_strdup_printf( "%s%d", edge_str, i );
1228 else
1229 name = g_strdup( edge_str );
6b775dbb 1230
6cc5e1a6
DB
1231 f = g_build_filename( dir, name, NULL );
1232 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
1233 {
1234 g_free( f );
1235 break;
1236 }
1237 g_free( name );
1238 g_free( f );
1239 }
1240 g_free( dir );
1241 return name;
1242}
1243
1244/* FIXME: Potentially we can support multiple panels at the same edge,
1245 * but currently this cannot be done due to some positioning problems. */
6b775dbb 1246static void panel_popupmenu_create_panel( GtkMenuItem* item, LXPanel* panel )
6cc5e1a6 1247{
6b775dbb 1248 gint m, e, monitors;
7a1c5048
AG
1249 GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(panel));
1250 LXPanel *new_panel = panel_allocate(screen);
6b775dbb 1251 Panel *p = new_panel->priv;
7dd482c5 1252 config_setting_t *global;
2ba86315
DB
1253
1254 /* Allocate the edge. */
6b775dbb
AG
1255 g_assert(screen);
1256 monitors = gdk_screen_get_n_monitors(screen);
f7ecd6ce
AG
1257 /* try to allocate edge on current monitor first */
1258 m = panel->priv->monitor;
1259 if (m < 0)
1260 {
1261 /* panel is spanned over the screen, guess from pointer now */
1262 gint x, y;
1263 gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL);
1264 m = gdk_screen_get_monitor_at_point(screen, x, y);
1265 }
1266 for (e = 1; e < 5; ++e)
1267 {
1268 if (panel_edge_available(p, e, m))
1269 {
1270 p->edge = e;
1271 p->monitor = m;
1272 goto found_edge;
1273 }
1274 }
1275 /* try all monitors */
6b775dbb
AG
1276 for(m=0; m<monitors; ++m)
1277 {
1278 /* try each of the four edges */
1279 for(e=1; e<5; ++e)
1280 {
1281 if(panel_edge_available(p,e,m)) {
1282 p->edge = e;
1283 p->monitor = m;
1284 goto found_edge;
1285 }
1286 }
1287 }
1288
1289 gtk_widget_destroy(GTK_WIDGET(new_panel));
1290 g_warning("Error adding panel: There is no room for another panel. All the edges are taken.");
1291 fm_show_error(NULL, NULL, _("There is no room for another panel. All the edges are taken."));
1292 return;
1293
1294found_edge:
1295 p->name = gen_panel_name(p->edge, p->monitor);
2ba86315 1296
6b775dbb 1297 /* create new config with first group "Global" */
7dd482c5
AG
1298 global = config_group_add_subgroup(config_root_setting(p->config), "Global");
1299 config_group_set_string(global, "edge", num2str(edge_pair, p->edge, "none"));
1300 config_group_set_int(global, "monitor", p->monitor);
2ba86315 1301 panel_configure(new_panel, 0);
6b775dbb 1302 panel_normalize_configuration(p);
f7ecd6ce 1303 panel_start_gui(new_panel, NULL);
2ba86315 1304
6b775dbb 1305 lxpanel_config_save(new_panel);
2ba86315 1306 all_panels = g_slist_prepend(all_panels, new_panel);
6cc5e1a6
DB
1307}
1308
6b775dbb 1309static void panel_popupmenu_delete_panel( GtkMenuItem* item, LXPanel* panel )
6cc5e1a6
DB
1310{
1311 GtkWidget* dlg;
1312 gboolean ok;
6b775dbb 1313 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel),
6cc5e1a6
DB
1314 GTK_DIALOG_MODAL,
1315 GTK_MESSAGE_QUESTION,
1316 GTK_BUTTONS_OK_CANCEL,
1317 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
2ba86315 1318 panel_apply_icon(GTK_WINDOW(dlg));
6cc5e1a6
DB
1319 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
1320 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
1321 gtk_widget_destroy( dlg );
1322 if( ok )
1323 {
6b775dbb 1324 gchar *fname;
6cc5e1a6
DB
1325 all_panels = g_slist_remove( all_panels, panel );
1326
1327 /* delete the config file of this panel */
6b775dbb 1328 fname = _user_config_file_name("panels", panel->priv->name);
6cc5e1a6 1329 g_unlink( fname );
6b775dbb
AG
1330 g_free(fname);
1331 panel->priv->config_changed = 0;
1332 gtk_widget_destroy(GTK_WIDGET(panel));
6cc5e1a6
DB
1333 }
1334}
1335
2ba86315
DB
1336static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
1337{
1338 GtkWidget *about;
1339 const gchar* authors[] = {
1340 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
1341 "Jim Huang <jserv.tw@gmail.com>",
1342 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
1343 "Fred Chien <cfsghost@gmail.com>",
1344 "Daniel Kesler <kesler.daniel@gmail.com>",
1345 "Juergen Hoetzel <juergen@archlinux.org>",
1346 "Marty Jack <martyj19@comcast.net>",
cb7f7ba8 1347 "Martin Bagge <brother@bsnet.se>",
6b775dbb
AG
1348 "Andriy Grytsenko <andrej@rep.kiev.ua>",
1349 "Giuseppe Penone <giuspen@gmail.com>",
1350 "Piotr Sipika <piotr.sipika@gmail.com>",
2ba86315
DB
1351 NULL
1352 };
1353 /* TRANSLATORS: Replace this string with your names, one name per line. */
1354 gchar *translators = _( "translator-credits" );
1355
1356 about = gtk_about_dialog_new();
1357 panel_apply_icon(GTK_WINDOW(about));
1358 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
6b775dbb 1359 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
aaccad27
AL
1360
1361 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
1362 {
1363 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1364 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
1365 }
1366 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
1367 {
1368 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1369 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
1370 }
6b775dbb 1371 else
aaccad27
AL
1372 {
1373 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
6b775dbb 1374 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL));
aaccad27
AL
1375 }
1376
7a1c5048 1377 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2016"));
2ba86315
DB
1378 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
1379 gtk_about_dialog_set_license(GTK_ABOUT_DIALOG(about), "This program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.");
1380 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
1381 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
1382 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
1383 gtk_dialog_run(GTK_DIALOG(about));
6b775dbb 1384 gtk_widget_destroy(about);
2ba86315
DB
1385}
1386
1387void panel_apply_icon( GtkWindow *w )
1388{
6b775dbb
AG
1389 GdkPixbuf* window_icon;
1390
1391 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
aaccad27 1392 {
6b775dbb
AG
1393 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
1394 }
1395 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
aaccad27 1396 {
6b775dbb
AG
1397 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
1398 }
aaccad27
AL
1399 else
1400 {
6b775dbb
AG
1401 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL);
1402 }
aaccad27 1403 gtk_window_set_icon(w, window_icon);
2ba86315
DB
1404}
1405
6b775dbb 1406GtkMenu* lxpanel_get_plugin_menu( LXPanel* panel, GtkWidget* plugin, gboolean use_sub_menu )
6cc5e1a6 1407{
7486d297
DB
1408 GtkWidget *menu_item, *img;
1409 GtkMenu *ret,*menu;
6b775dbb 1410 const LXPanelPluginInit *init;
6cc5e1a6 1411 char* tmp;
6b775dbb 1412
7486d297 1413 ret = menu = GTK_MENU(gtk_menu_new());
6cc5e1a6 1414
6b775dbb
AG
1415 if (plugin)
1416 {
1417 init = PLUGIN_CLASS(plugin);
1418 /* create single item - plugin instance settings */
1419 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
7a1c5048
AG
1420 tmp = g_strdup_printf(_("\"%s\" Settings"),
1421 g_dgettext(init->gettext_package, init->name));
6b775dbb
AG
1422 menu_item = gtk_image_menu_item_new_with_label( tmp );
1423 g_free( tmp );
1424 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1425 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
1426 if( init->config )
1427 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
1428 else
1429 gtk_widget_set_sensitive( menu_item, FALSE );
1430 /* add custom items by plugin if requested */
1431 if (init->update_context_menu != NULL)
1432 use_sub_menu = init->update_context_menu(plugin, ret);
1433 /* append a separator */
1434 menu_item = gtk_separator_menu_item_new();
1435 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1436 }
1437 if (use_sub_menu)
1438 menu = GTK_MENU(gtk_menu_new());
1439
6cc5e1a6
DB
1440 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
1441 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
1442 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1443 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1444 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
1445
1446 if( plugin )
1447 {
1448 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
6b775dbb 1449 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
6cc5e1a6
DB
1450 menu_item = gtk_image_menu_item_new_with_label( tmp );
1451 g_free( tmp );
1452 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1453 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1454 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
1455 }
1456
1457 menu_item = gtk_separator_menu_item_new();
1458 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1459
1460 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1461 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
1462 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1463 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1464 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
1465
1466 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
1467 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
1468 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1469 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1470 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
6cc5e1a6
DB
1471
1472 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
1473 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
1474 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1475 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1476 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
1477 if( ! all_panels->next ) /* if this is the only panel */
1478 gtk_widget_set_sensitive( menu_item, FALSE );
1479
2ba86315
DB
1480 menu_item = gtk_separator_menu_item_new();
1481 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1482
1483 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
1484 menu_item = gtk_image_menu_item_new_with_label(_("About"));
1485 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1486 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
6b775dbb 1487 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel->priv );
2ba86315 1488
6cc5e1a6
DB
1489 if( use_sub_menu )
1490 {
6cc5e1a6
DB
1491 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
1492 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
7486d297 1493 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
6cc5e1a6
DB
1494 }
1495
6b775dbb 1496 gtk_widget_show_all(GTK_WIDGET(ret));
6cc5e1a6
DB
1497
1498 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1499 return ret;
1500}
1501
6b775dbb
AG
1502/* for old plugins compatibility */
1503GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
1504{
1505 return lxpanel_get_plugin_menu(panel->topgwin, plugin->pwid, use_sub_menu);
1506}
1507
6cc5e1a6
DB
1508/****************************************************
1509 * panel creation *
1510 ****************************************************/
2ba86315 1511
6cc5e1a6
DB
1512static void
1513make_round_corners(Panel *p)
1514{
1515 /* FIXME: This should be re-written with shape extension of X11 */
1516 /* gdk_window_shape_combine_mask() can be used */
1517}
1518
1519void panel_set_dock_type(Panel *p)
1520{
6b775dbb
AG
1521 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1522
6cc5e1a6
DB
1523 if (p->setdocktype) {
1524 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
6b775dbb 1525 XChangeProperty(xdisplay, p->topxwin,
6cc5e1a6
DB
1526 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
1527 PropModeReplace, (unsigned char *) &state, 1);
1528 }
1529 else {
6b775dbb 1530 XDeleteProperty( xdisplay, p->topxwin, a_NET_WM_WINDOW_TYPE );
6cc5e1a6
DB
1531 }
1532}
1533
6b775dbb 1534void panel_establish_autohide(Panel *p)
2ba86315 1535{
6b775dbb 1536 _panel_establish_autohide(p->topgwin);
2ba86315
DB
1537}
1538
6b775dbb 1539void _panel_establish_autohide(LXPanel *p)
2ba86315 1540{
6b775dbb
AG
1541 if (p->priv->autohide)
1542 ah_start(p);
32a67dc7
DB
1543 else
1544 {
6b775dbb
AG
1545 ah_stop(p);
1546 ah_state_set(p, AH_STATE_VISIBLE);
2ba86315
DB
1547 }
1548}
1549
1550/* Set an image from a file with scaling to the panel icon size. */
514580cf 1551void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
2ba86315
DB
1552{
1553 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
1554 if (pixbuf != NULL)
1555 {
1556 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1557 g_object_unref(pixbuf);
1558 }
1559}
1560
6b775dbb
AG
1561void lxpanel_image_set_from_file(LXPanel * p, GtkWidget * image, const char * file)
1562{
1563 panel_image_set_from_file(p->priv, image, file);
1564}
1565
32a67dc7
DB
1566/* Set an image from a icon theme with scaling to the panel icon size. */
1567gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
1568{
aaccad27 1569 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
32a67dc7 1570 {
aaccad27 1571 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
32a67dc7
DB
1572 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1573 g_object_unref(pixbuf);
1574 return TRUE;
1575 }
1576 return FALSE;
1577}
1578
6b775dbb
AG
1579gboolean lxpanel_image_set_icon_theme(LXPanel * p, GtkWidget * image, const gchar * icon)
1580{
1581 return panel_image_set_icon_theme(p->priv, image, icon);
1582}
1583
f7ecd6ce
AG
1584static int
1585panel_parse_plugin(LXPanel *p, config_setting_t *cfg)
1586{
1587 const char *type = NULL;
1588
1589 ENTER;
1590 config_setting_lookup_string(cfg, "type", &type);
1591 DBG("plug %s\n", type);
1592
1593 if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
1594 g_warning( "lxpanel: can't load %s plugin", type);
1595 goto error;
1596 }
1597 RET(1);
1598
1599error:
1600 RET(0);
1601}
1602
6cc5e1a6 1603static void
f7ecd6ce 1604panel_start_gui(LXPanel *panel, config_setting_t *list)
6cc5e1a6
DB
1605{
1606 Atom state[3];
1607 XWMHints wmhints;
7dd482c5 1608 gulong val;
7a1c5048
AG
1609 Screen *xscreen = GDK_SCREEN_XSCREEN(gtk_widget_get_screen(GTK_WIDGET(panel)));
1610 Display *xdisplay = DisplayOfScreen(xscreen);
6b775dbb
AG
1611 Panel *p = panel->priv;
1612 GtkWidget *w = GTK_WIDGET(panel);
f7ecd6ce
AG
1613 config_setting_t *s;
1614 GdkRectangle rect;
1615 int i;
6cc5e1a6
DB
1616
1617 ENTER;
1618
f7ecd6ce 1619 g_debug("panel_start_gui on '%s'", p->name);
2ba86315
DB
1620 p->curdesk = get_net_current_desktop();
1621 p->desknum = get_net_number_of_desktops();
f7ecd6ce
AG
1622 //p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1623 p->ax = p->ay = p->aw = p->ah = 0;
2ba86315 1624
2ba86315 1625 p->display = gdk_display_get_default();
6b775dbb 1626 gtk_window_set_wmclass(GTK_WINDOW(panel), "panel", "lxpanel");
6b775dbb 1627
f7ecd6ce
AG
1628 if (G_UNLIKELY(win_grp == NULL))
1629 {
1630 win_grp = gtk_window_group_new();
1631 g_object_add_weak_pointer(G_OBJECT(win_grp), (gpointer *)&win_grp);
1632 gtk_window_group_add_window(win_grp, (GtkWindow*)panel);
1633 g_object_unref(win_grp);
1634 }
1635 else
1636 gtk_window_group_add_window(win_grp, (GtkWindow*)panel);
6b775dbb
AG
1637
1638 gtk_widget_add_events( w, GDK_BUTTON_PRESS_MASK );
1639
1640 gtk_widget_realize(w);
1641 //gdk_window_set_decorations(gtk_widget_get_window(p->topgwin), 0);
6cc5e1a6
DB
1642
1643 // main layout manager as a single child of panel
6b775dbb 1644 p->box = panel_box_new(panel, FALSE, 0);
6cc5e1a6 1645 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
6b775dbb 1646 gtk_container_add(GTK_CONTAINER(panel), p->box);
6cc5e1a6
DB
1647 gtk_widget_show(p->box);
1648 if (p->round_corners)
1649 make_round_corners(p);
1650
f7ecd6ce 1651 p->topxwin = GDK_WINDOW_XID(gtk_widget_get_window(w));
6cc5e1a6
DB
1652 DBG("topxwin = %x\n", p->topxwin);
1653
1654 /* the settings that should be done before window is mapped */
1655 wmhints.flags = InputHint;
1656 wmhints.input = 0;
6b775dbb 1657 XSetWMHints (xdisplay, p->topxwin, &wmhints);
6cc5e1a6
DB
1658#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
1659 val = WIN_HINTS_SKIP_FOCUS;
6b775dbb
AG
1660 XChangeProperty(xdisplay, p->topxwin,
1661 XInternAtom(xdisplay, "_WIN_HINTS", False), XA_CARDINAL, 32,
6cc5e1a6
DB
1662 PropModeReplace, (unsigned char *) &val, 1);
1663
1664 panel_set_dock_type(p);
1665
1666 /* window mapping point */
f7ecd6ce
AG
1667 p->visible = TRUE;
1668 _calculate_position(panel, &rect);
1669 gtk_window_move(GTK_WINDOW(panel), rect.x, rect.y);
1670 gtk_window_present(GTK_WINDOW(panel));
6cc5e1a6
DB
1671
1672 /* the settings that should be done after window is mapped */
1673
1674 /* send it to running wm */
7a1c5048 1675 Xclimsgx(xscreen, p->topxwin, a_NET_WM_DESKTOP, G_MAXULONG, 0, 0, 0, 0);
6cc5e1a6 1676 /* and assign it ourself just for case when wm is not running */
7dd482c5 1677 val = G_MAXULONG;
6b775dbb 1678 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
6cc5e1a6
DB
1679 PropModeReplace, (unsigned char *) &val, 1);
1680
1681 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1682 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1683 state[2] = a_NET_WM_STATE_STICKY;
6b775dbb 1684 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STATE, XA_ATOM,
6cc5e1a6
DB
1685 32, PropModeReplace, (unsigned char *) state, 3);
1686
6b775dbb 1687 p->initialized = TRUE;
6cc5e1a6 1688
f7ecd6ce
AG
1689 if (list) for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1690 if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1691 panel_parse_plugin(panel, s)) /* success on plugin start */
1692 i++;
1693 else /* remove invalid data from config */
1694 config_setting_remove_elem(list, i);
1695
6cc5e1a6
DB
1696 RET();
1697}
1698
2ba86315
DB
1699/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1700void panel_adjust_geometry_terminology(Panel * p)
1701{
aa0e9095
DB
1702 if ((p->height_label != NULL) && (p->width_label != NULL)
1703 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
2ba86315
DB
1704 {
1705 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1706 {
1707 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1708 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
1709 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1710 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
1711 }
1712 else
1713 {
1714 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1715 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
1716 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1717 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
1718 }
1719 }
1720}
1721
1722/* Draw text into a label, with the user preference color and optionally bold. */
6b775dbb
AG
1723void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1724 gboolean bold, float custom_size_factor,
1725 gboolean custom_color)
2ba86315 1726{
2ba86315
DB
1727 if (text == NULL)
1728 {
1729 /* Null string. */
1730 gtk_label_set_text(GTK_LABEL(label), NULL);
6b775dbb 1731 return;
2ba86315
DB
1732 }
1733
6b775dbb
AG
1734 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1735 int font_desc;
1736 if (p->usefontsize)
1737 font_desc = p->fontsize;
2ba86315
DB
1738 else
1739 {
19ab5cea
AG
1740 GtkStyle *style = gtk_widget_get_style(label);
1741 font_desc = pango_font_description_get_size(style->font_desc) / PANGO_SCALE;
6b775dbb
AG
1742 }
1743 font_desc *= custom_size_factor;
1744
1745 /* Check the string for characters that need to be escaped.
1746 * If any are found, create the properly escaped string and use it instead. */
1747 const char * valid_markup = text;
1748 char * escaped_text = NULL;
1749 const char * q;
1750 for (q = text; *q != '\0'; q += 1)
1751 {
1752 if ((*q == '<') || (*q == '>') || (*q == '&'))
2ba86315 1753 {
6b775dbb
AG
1754 escaped_text = g_markup_escape_text(text, -1);
1755 valid_markup = escaped_text;
1756 break;
2ba86315 1757 }
6b775dbb 1758 }
2ba86315 1759
6b775dbb
AG
1760 gchar * formatted_text;
1761 if ((custom_color) && (p->usefontcolor))
1762 {
1763 /* Color, optionally bold. */
1764 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
2ba86315
DB
1765 font_desc,
1766 gcolor2rgb24(&p->gfontcolor),
1767 ((bold) ? "<b>" : ""),
1768 valid_markup,
1769 ((bold) ? "</b>" : ""));
6b775dbb
AG
1770 }
1771 else
1772 {
1773 /* No color, optionally bold. */
1774 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
2ba86315
DB
1775 font_desc,
1776 ((bold) ? "<b>" : ""),
1777 valid_markup,
1778 ((bold) ? "</b>" : ""));
2ba86315 1779 }
6b775dbb
AG
1780
1781 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1782 g_free(formatted_text);
1783 g_free(escaped_text);
1784}
1785
1786void lxpanel_draw_label_text(LXPanel * p, GtkWidget * label, const char * text,
1787 gboolean bold, float custom_size_factor,
1788 gboolean custom_color)
1789{
1790 panel_draw_label_text(p->priv, label, text, bold, custom_size_factor, custom_color);
2ba86315
DB
1791}
1792
1793void panel_set_panel_configuration_changed(Panel *p)
6cc5e1a6 1794{
6b775dbb
AG
1795 _panel_set_panel_configuration_changed(p->topgwin);
1796}
1797
0688b017
AG
1798static inline void _update_orientation(Panel *p)
1799{
1800 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1801 ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1802}
1803
8713e384 1804static gboolean _panel_idle_reconfigure(gpointer widget)
6b775dbb 1805{
8713e384
AG
1806 LXPanel *panel;
1807 Panel *p;
6b775dbb 1808 GList *plugins, *l;
8713e384 1809 GtkOrientation previous_orientation;
2ba86315 1810
8713e384
AG
1811 if (g_source_is_destroyed(g_main_current_source()))
1812 return FALSE;
1813
1814 panel = LXPANEL(widget);
1815 p = panel->priv;
1816 previous_orientation = p->orientation;
0688b017 1817 _update_orientation(p);
2ba86315 1818
6b775dbb 1819 /* either first run or orientation was changed */
0688b017 1820 if (previous_orientation != p->orientation)
2ba86315
DB
1821 {
1822 panel_adjust_geometry_terminology(p);
0688b017 1823 p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
2ba86315
DB
1824 if (p->height_control != NULL)
1825 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
1826 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1827 {
6b775dbb 1828 int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
2ba86315
DB
1829 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1830 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1831 }
2ba86315
DB
1832 }
1833
6b775dbb
AG
1834 /* FIXME: it's deprecated, kept for binary compatibility */
1835 if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
6cc5e1a6
DB
1836 p->my_box_new = gtk_hbox_new;
1837 p->my_separator_new = gtk_vseparator_new;
1838 } else {
1839 p->my_box_new = gtk_vbox_new;
1840 p->my_separator_new = gtk_hseparator_new;
1841 }
1842
1843 /* recreate the main layout box */
2ba86315
DB
1844 if (p->box != NULL)
1845 {
6b775dbb 1846 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
6cc5e1a6 1847 }
2ba86315 1848
6cc5e1a6
DB
1849 /* NOTE: This loop won't be executed when panel started since
1850 plugins are not loaded at that time.
1851 This is used when the orientation of the panel is changed
1852 from the config dialog, and plugins should be re-layout.
1853 */
6b775dbb
AG
1854 plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1855 for( l = plugins; l; l = l->next ) {
1856 GtkWidget *w = (GtkWidget*)l->data;
1857 const LXPanelPluginInit *init = PLUGIN_CLASS(w);
1858 if (init->reconfigure)
1859 init->reconfigure(panel, w);
6cc5e1a6 1860 }
6b775dbb
AG
1861 g_list_free(plugins);
1862 /* panel geometry changed? update panel background then */
7dd482c5 1863 _panel_queue_update_background(panel);
8713e384
AG
1864
1865 p->reconfigure_queued = 0;
1866
1867 return FALSE;
1868}
1869
1870void _panel_set_panel_configuration_changed(LXPanel *panel)
1871{
1872 if (panel->priv->reconfigure_queued)
1873 return;
1874 panel->priv->reconfigure_queued = g_idle_add(_panel_idle_reconfigure, panel);
6cc5e1a6
DB
1875}
1876
1877static int
6b775dbb 1878panel_parse_global(Panel *p, config_setting_t *cfg)
6cc5e1a6 1879{
6b775dbb
AG
1880 const char *str;
1881 gint i;
6cc5e1a6 1882
6b775dbb
AG
1883 /* check Global config */
1884 if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
6cc5e1a6 1885 {
6b775dbb
AG
1886 g_warning( "lxpanel: Global section not found");
1887 RET(0);
1888 }
1889 if (config_setting_lookup_string(cfg, "edge", &str))
1890 p->edge = str2num(edge_pair, str, EDGE_NONE);
f7ecd6ce
AG
1891 if (config_setting_lookup_string(cfg, "align", &str) ||
1892 /* NOTE: supporting "allign" for backward compatibility */
1893 config_setting_lookup_string(cfg, "allign", &str))
1894 p->align = str2num(allign_pair, str, ALIGN_NONE);
6b775dbb
AG
1895 config_setting_lookup_int(cfg, "monitor", &p->monitor);
1896 config_setting_lookup_int(cfg, "margin", &p->margin);
1897 if (config_setting_lookup_string(cfg, "widthtype", &str))
1898 p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1899 config_setting_lookup_int(cfg, "width", &p->width);
1900 if (config_setting_lookup_string(cfg, "heighttype", &str))
1901 p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1902 config_setting_lookup_int(cfg, "height", &p->height);
1903 if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1904 p->spacing = i;
1905 if (config_setting_lookup_int(cfg, "setdocktype", &i))
1906 p->setdocktype = i != 0;
1907 if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1908 p->setstrut = i != 0;
1909 if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1910 p->round_corners = i != 0;
1911 if (config_setting_lookup_int(cfg, "transparent", &i))
1912 p->transparent = i != 0;
1913 if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
1914 {
1915 if (p->alpha > 255)
1916 p->alpha = 255;
1917 }
1918 if (config_setting_lookup_int(cfg, "autohide", &i))
1919 p->autohide = i != 0;
1920 if (config_setting_lookup_int(cfg, "heightwhenhidden", &i))
1921 p->height_when_hidden = MAX(0, i);
1922 if (config_setting_lookup_string(cfg, "tintcolor", &str))
1923 {
1924 if (!gdk_color_parse (str, &p->gtintcolor))
1925 gdk_color_parse ("white", &p->gtintcolor);
1926 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1927 DBG("tintcolor=%x\n", p->tintcolor);
6cc5e1a6 1928 }
6b775dbb
AG
1929 if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1930 p->usefontcolor = i != 0;
1931 if (config_setting_lookup_string(cfg, "fontcolor", &str))
1932 {
1933 if (!gdk_color_parse (str, &p->gfontcolor))
1934 gdk_color_parse ("black", &p->gfontcolor);
1935 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1936 DBG("fontcolor=%x\n", p->fontcolor);
1937 }
1938 if (config_setting_lookup_int(cfg, "usefontsize", &i))
1939 p->usefontsize = i != 0;
1940 if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1941 p->fontsize = i;
1942 if (config_setting_lookup_int(cfg, "background", &i))
1943 p->background = i != 0;
1944 if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1945 p->background_file = g_strdup(str);
1946 config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
6cc5e1a6 1947
0688b017 1948 _update_orientation(p);
2ba86315 1949 panel_normalize_configuration(p);
6cc5e1a6 1950
2ba86315 1951 return 1;
6cc5e1a6
DB
1952}
1953
f7ecd6ce 1954static void on_monitors_changed(GdkScreen* screen, gpointer unused)
6cc5e1a6 1955{
f7ecd6ce
AG
1956 GSList *pl;
1957 int monitors = gdk_screen_get_n_monitors(screen);
6cc5e1a6 1958
f7ecd6ce
AG
1959 for (pl = all_panels; pl; pl = pl->next)
1960 {
1961 LXPanel *p = pl->data;
1962
1963 /* handle connecting and disconnecting monitors now */
1964 if (p->priv->monitor < monitors && !p->priv->initialized)
1965 panel_start_gui(p, config_setting_get_member(config_root_setting(p->priv->config), ""));
1966 else if (p->priv->monitor >= monitors && p->priv->initialized)
1967 panel_stop_gui(p);
1968 /* resize panel if appropriate monitor changed its size or position */
1969 else
1970 {
1971 /* SF bug #666: after screen resize panel cannot establish
1972 right size since cannot be moved while is hidden */
1973 ah_state_set(p, AH_STATE_VISIBLE);
1974 gtk_widget_queue_resize(GTK_WIDGET(p));
1975 }
6cc5e1a6 1976 }
6cc5e1a6
DB
1977}
1978
f7ecd6ce 1979static int panel_start(LXPanel *p)
6cc5e1a6 1980{
f7ecd6ce 1981 config_setting_t *list;
7a1c5048 1982 GdkScreen *screen = gtk_widget_get_screen(GTK_WIDGET(p));
6cc5e1a6
DB
1983
1984 /* parse global section */
1985 ENTER;
6cc5e1a6 1986
6b775dbb
AG
1987 list = config_setting_get_member(config_root_setting(p->priv->config), "");
1988 if (!list || !panel_parse_global(p->priv, config_setting_get_elem(list, 0)))
6cc5e1a6
DB
1989 RET(0);
1990
f7ecd6ce
AG
1991 if (p->priv->monitor < gdk_screen_get_n_monitors(screen))
1992 panel_start_gui(p, list);
1993 if (monitors_handler == 0)
1994 monitors_handler = g_signal_connect(screen, "monitors-changed",
1995 G_CALLBACK(on_monitors_changed), NULL);
2ba86315 1996 return 1;
6cc5e1a6
DB
1997}
1998
6cc5e1a6
DB
1999void panel_destroy(Panel *p)
2000{
6b775dbb 2001 gtk_widget_destroy(GTK_WIDGET(p->topgwin));
6cc5e1a6
DB
2002}
2003
f7ecd6ce 2004LXPanel* panel_new( const char* config_file, const char* config_name )
6cc5e1a6 2005{
6b775dbb 2006 LXPanel* panel = NULL;
7486d297 2007
6b775dbb 2008 if (G_LIKELY(config_file))
6cc5e1a6 2009 {
7a1c5048 2010 panel = panel_allocate(gdk_screen_get_default());
6b775dbb
AG
2011 panel->priv->name = g_strdup(config_name);
2012 g_debug("starting panel from file %s",config_file);
2013 if (!config_read_file(panel->priv->config, config_file) ||
2014 !panel_start(panel))
6cc5e1a6 2015 {
6b775dbb
AG
2016 g_warning( "lxpanel: can't start panel");
2017 gtk_widget_destroy(GTK_WIDGET(panel));
2018 panel = NULL;
6cc5e1a6
DB
2019 }
2020 }
2021 return panel;
2022}
2023
6b775dbb
AG
2024
2025GtkOrientation panel_get_orientation(LXPanel *panel)
2026{
2027 return panel->priv->orientation;
2028}
2029
2030gint panel_get_icon_size(LXPanel *panel)
2031{
2032 return panel->priv->icon_size;
2033}
2034
2035gint panel_get_height(LXPanel *panel)
2036{
2037 return panel->priv->height;
2038}
2039
2040Window panel_get_xwindow(LXPanel *panel)
2041{
2042 return panel->priv->topxwin;
2043}
2044
2045gint panel_get_monitor(LXPanel *panel)
2046{
2047 return panel->priv->monitor;
2048}
2049
2050GtkStyle *panel_get_defstyle(LXPanel *panel)
2051{
2052 return panel->priv->defstyle;
2053}
2054
2055GtkIconTheme *panel_get_icon_theme(LXPanel *panel)
2056{
2057 return panel->priv->icon_theme;
2058}
2059
2060gboolean panel_is_at_bottom(LXPanel *panel)
2061{
2062 return panel->priv->edge == EDGE_BOTTOM;
2063}
2064
2065gboolean panel_is_dynamic(LXPanel *panel)
2066{
2067 return panel->priv->widthtype == WIDTH_REQUEST;
2068}
2069
2070GtkWidget *panel_box_new(LXPanel *panel, gboolean homogeneous, gint spacing)
2071{
2072 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2073 return gtk_hbox_new(homogeneous, spacing);
2074 return gtk_vbox_new(homogeneous, spacing);
2075}
2076
2077GtkWidget *panel_separator_new(LXPanel *panel)
2078{
2079 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
2080 return gtk_vseparator_new();
2081 return gtk_hseparator_new();
2082}
2083
2084gboolean _class_is_present(const LXPanelPluginInit *init)
2085{
2086 GSList *sl;
2087
2088 for (sl = all_panels; sl; sl = sl->next )
2089 {
2090 LXPanel *panel = (LXPanel*)sl->data;
2091 GList *plugins, *p;
2092
f7ecd6ce
AG
2093 if (panel->priv->box == NULL)
2094 continue;
6b775dbb
AG
2095 plugins = gtk_container_get_children(GTK_CONTAINER(panel->priv->box));
2096 for (p = plugins; p; p = p->next)
2097 if (PLUGIN_CLASS(p->data) == init)
2098 {
2099 g_list_free(plugins);
2100 return TRUE;
2101 }
2102 g_list_free(plugins);
2103 }
2104 return FALSE;
6cc5e1a6 2105}