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