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