Fix crash in taskbar after visible task changed name then gone.
[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
0bfcc6c9 76 XFree(p->workarea);
a7bd16a4
AG
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. */
0bfcc6c9 383 gulong 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 543 LXPanel* p = (LXPanel*)l->data;
0bfcc6c9 544 XFree( p->priv->workarea );
a7bd16a4 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
9a762e63
AG
694 cx = p->ax;
695 cy = p->ay;
c6c6d2e9
AG
696 cw = p->cw;
697 ch = p->ch;
9a2c13d0 698
9a762e63
AG
699 if (cw == 1) cw = 0;
700 if (ch == 1) ch = 0;
9a2c13d0
AG
701 /* reduce area which will raise panel so it does not interfere with apps */
702 if (p->ah_state == AH_STATE_HIDDEN) {
703 gap = MAX(p->height_when_hidden, GAP);
704 switch (p->edge) {
705 case EDGE_LEFT:
706 cw = gap;
707 break;
708 case EDGE_RIGHT:
709 cx = cx + cw - gap;
710 cw = gap;
711 break;
712 case EDGE_TOP:
713 ch = gap;
714 break;
715 case EDGE_BOTTOM:
716 cy = cy + ch - gap;
717 ch = gap;
718 break;
719 }
720 }
721 p->ah_far = ((x < cx) || (x > cx + cw) || (y < cy) || (y > cy + ch));
722
a7bd16a4 723 ah_state_set(panel, p->ah_state);
9a2c13d0
AG
724 RET(TRUE);
725}
726
727static gboolean ah_state_hide_timeout(gpointer p)
728{
729 if (!g_source_is_destroyed(g_main_current_source()))
730 {
731 ah_state_set(p, AH_STATE_HIDDEN);
a7bd16a4 732 ((LXPanel *)p)->priv->hide_timeout = 0;
9a2c13d0
AG
733 }
734 return FALSE;
735}
736
a7bd16a4 737static void ah_state_set(LXPanel *panel, PanelAHState ah_state)
9a2c13d0 738{
a7bd16a4
AG
739 Panel *p = panel->priv;
740
9a2c13d0
AG
741 ENTER;
742 if (p->ah_state != ah_state) {
743 p->ah_state = ah_state;
744 switch (ah_state) {
745 case AH_STATE_VISIBLE:
a7bd16a4 746 gtk_widget_show(GTK_WIDGET(panel));
9a2c13d0 747 gtk_widget_show(p->box);
9a762e63 748 gtk_widget_queue_resize(GTK_WIDGET(panel));
a7bd16a4 749 gtk_window_stick(GTK_WINDOW(panel));
9a2c13d0
AG
750 p->visible = TRUE;
751 break;
752 case AH_STATE_WAITING:
9a762e63
AG
753 if (p->hide_timeout)
754 g_source_remove(p->hide_timeout);
a7bd16a4 755 p->hide_timeout = g_timeout_add(2 * PERIOD, ah_state_hide_timeout, panel);
9a2c13d0
AG
756 break;
757 case AH_STATE_HIDDEN:
758 if (p->height_when_hidden > 0)
759 gtk_widget_hide(p->box);
760 else
a7bd16a4 761 gtk_widget_hide(GTK_WIDGET(panel));
9a2c13d0
AG
762 p->visible = FALSE;
763 }
764 } else if (p->autohide && p->ah_far) {
e1fd11ee
AG
765 switch (ah_state) {
766 case AH_STATE_VISIBLE:
a7bd16a4 767 ah_state_set(panel, AH_STATE_WAITING);
e1fd11ee
AG
768 break;
769 case AH_STATE_WAITING:
770 break;
771 case AH_STATE_HIDDEN:
772 /* configurator might change height_when_hidden value */
773 if (p->height_when_hidden > 0)
774 {
775 if (gtk_widget_get_visible(p->box))
776 {
777 gtk_widget_hide(p->box);
a7bd16a4 778 gtk_widget_show(GTK_WIDGET(panel));
e1fd11ee
AG
779 }
780 }
781 else
a7bd16a4 782 if (gtk_widget_get_visible(GTK_WIDGET(panel)))
e1fd11ee 783 {
a7bd16a4 784 gtk_widget_hide(GTK_WIDGET(panel));
e1fd11ee
AG
785 gtk_widget_show(p->box);
786 }
787 }
9a2c13d0
AG
788 } else {
789 switch (ah_state) {
790 case AH_STATE_VISIBLE:
791 break;
792 case AH_STATE_WAITING:
793 if (p->hide_timeout)
794 g_source_remove(p->hide_timeout);
795 p->hide_timeout = 0;
796 /* continue with setting visible */
797 case AH_STATE_HIDDEN:
a7bd16a4 798 ah_state_set(panel, AH_STATE_VISIBLE);
9a2c13d0
AG
799 }
800 }
801 RET();
802}
803
804/* starts autohide behaviour */
a7bd16a4 805static void ah_start(LXPanel *p)
9a2c13d0
AG
806{
807 ENTER;
a7bd16a4
AG
808 if (!p->priv->mouse_timeout)
809 p->priv->mouse_timeout = g_timeout_add(PERIOD, (GSourceFunc) mouse_watch, p);
9a2c13d0
AG
810 RET();
811}
812
813/* stops autohide */
a7bd16a4 814static void ah_stop(LXPanel *p)
9a2c13d0
AG
815{
816 ENTER;
a7bd16a4
AG
817 if (p->priv->mouse_timeout) {
818 g_source_remove(p->priv->mouse_timeout);
819 p->priv->mouse_timeout = 0;
9a2c13d0 820 }
a7bd16a4
AG
821 if (p->priv->hide_timeout) {
822 g_source_remove(p->priv->hide_timeout);
823 p->priv->hide_timeout = 0;
9a2c13d0
AG
824 }
825 RET();
826}
9a2c13d0
AG
827/* end of the autohide code
828 * ------------------------------------------------------------- */
829
fddae119
FC
830static gint
831panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
832{
a7bd16a4 833 panel_configure( (LXPanel*)user_data, 0 );
4b93d81e 834 return TRUE;
fddae119
FC
835}
836
17fab6e5 837static void panel_popupmenu_config_plugin( GtkMenuItem* item, GtkWidget* plugin )
cf701cb7 838{
a7bd16a4 839 Panel *panel = PLUGIN_PANEL(plugin)->priv;
17fab6e5 840
2f1173de 841 lxpanel_plugin_show_config_dialog(plugin);
930af9fd
HJYP
842
843 /* FIXME: this should be more elegant */
17fab6e5 844 panel->config_changed = TRUE;
cf701cb7
HJYP
845}
846
a7bd16a4 847static void panel_popupmenu_add_item( GtkMenuItem* item, LXPanel* panel )
4b93d81e
HJYP
848{
849 /* panel_add_plugin( panel, panel->topgwin ); */
8d8b66da 850 panel_configure( panel, 2 );
cf701cb7
HJYP
851}
852
17fab6e5 853static void panel_popupmenu_remove_item( GtkMenuItem* item, GtkWidget* plugin )
cf701cb7 854{
a7bd16a4 855 Panel* panel = PLUGIN_PANEL(plugin)->priv;
b7e8ad02
MJ
856
857 /* If the configuration dialog is open, there will certainly be a crash if the
858 * user manipulates the Configured Plugins list, after we remove this entry.
859 * Close the configuration dialog if it is open. */
860 if (panel->pref_dialog != NULL)
861 {
862 gtk_widget_destroy(panel->pref_dialog);
863 panel->pref_dialog = NULL;
864 }
17fab6e5
AG
865 config_setting_destroy(g_object_get_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf));
866 /* reset conf pointer because the widget still may be referenced by configurator */
867 g_object_set_qdata(G_OBJECT(plugin), lxpanel_plugin_qconf, NULL);
b7e8ad02 868
a7bd16a4 869 lxpanel_config_save(PLUGIN_PANEL(plugin));
17fab6e5 870 gtk_widget_destroy(plugin);
cf701cb7
HJYP
871}
872
3e7b8eb7
HJYP
873/* FIXME: Potentially we can support multiple panels at the same edge,
874 * but currently this cannot be done due to some positioning problems. */
64afc832 875static char* gen_panel_name( int edge, gint monitor )
3e7b8eb7
HJYP
876{
877 const char* edge_str = num2str( edge_pair, edge, "" );
878 char* name = NULL;
1f4bc3aa 879 char* dir = _user_config_file_name("panels", NULL);
3e7b8eb7
HJYP
880 int i;
881 for( i = 0; i < G_MAXINT; ++i )
882 {
883 char* f;
64afc832
R
884 if(monitor != 0)
885 name = g_strdup_printf( "%s-m%d-%d", edge_str, monitor, i );
886 else if( G_LIKELY( i > 0 ) )
3e7b8eb7
HJYP
887 name = g_strdup_printf( "%s%d", edge_str, i );
888 else
889 name = g_strdup( edge_str );
64afc832 890
3e7b8eb7
HJYP
891 f = g_build_filename( dir, name, NULL );
892 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
893 {
894 g_free( f );
895 break;
896 }
897 g_free( name );
898 g_free( f );
899 }
900 g_free( dir );
901 return name;
902}
903
904/* FIXME: Potentially we can support multiple panels at the same edge,
905 * but currently this cannot be done due to some positioning problems. */
a7bd16a4 906static void panel_popupmenu_create_panel( GtkMenuItem* item, LXPanel* panel )
cf701cb7 907{
64afc832
R
908 gint m, e, monitors;
909 GdkScreen *screen;
a7bd16a4
AG
910 LXPanel *new_panel = panel_allocate();
911 Panel *p = new_panel->priv;
9dd114c4 912
913 /* Allocate the edge. */
64afc832
R
914 screen = gdk_screen_get_default();
915 g_assert(screen);
916 monitors = gdk_screen_get_n_monitors(screen);
917 for(m=0; m<monitors; ++m)
918 {
919 /* try each of the four edges */
920 for(e=1; e<5; ++e)
921 {
a7bd16a4
AG
922 if(panel_edge_available(p,e,m)) {
923 p->edge = e;
924 p->monitor = m;
64afc832
R
925 goto found_edge;
926 }
927 }
928 }
929
a7bd16a4 930 gtk_widget_destroy(GTK_WIDGET(new_panel));
06e29ce1 931 g_warning("Error adding panel: There is no room for another panel. All the edges are taken.");
bb4cf963 932 fm_show_error(NULL, NULL, _("There is no room for another panel. All the edges are taken."));
64afc832
R
933 return;
934
935found_edge:
a7bd16a4 936 p->name = gen_panel_name(p->edge, p->monitor);
9dd114c4 937
17fab6e5 938 /* create new config with first group "Global" */
a7bd16a4 939 config_group_add_subgroup(config_root_setting(p->config), "Global");
9dd114c4 940 panel_configure(new_panel, 0);
a7bd16a4 941 panel_normalize_configuration(p);
9dd114c4 942 panel_start_gui(new_panel);
a7bd16a4 943 gtk_widget_show_all(GTK_WIDGET(new_panel));
9dd114c4 944
a7bd16a4 945 lxpanel_config_save(new_panel);
9dd114c4 946 all_panels = g_slist_prepend(all_panels, new_panel);
cf701cb7
HJYP
947}
948
a7bd16a4 949static void panel_popupmenu_delete_panel( GtkMenuItem* item, LXPanel* panel )
cf701cb7
HJYP
950{
951 GtkWidget* dlg;
952 gboolean ok;
a7bd16a4 953 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel),
cf701cb7
HJYP
954 GTK_DIALOG_MODAL,
955 GTK_MESSAGE_QUESTION,
956 GTK_BUTTONS_OK_CANCEL,
957 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
d1d43629 958 panel_apply_icon(GTK_WINDOW(dlg));
cf701cb7
HJYP
959 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
960 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
961 gtk_widget_destroy( dlg );
962 if( ok )
963 {
1f4bc3aa 964 gchar *fname;
cf701cb7 965 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
966
967 /* delete the config file of this panel */
a7bd16a4 968 fname = _user_config_file_name("panels", panel->priv->name);
4b93d81e 969 g_unlink( fname );
1f4bc3aa 970 g_free(fname);
a7bd16a4
AG
971 panel->priv->config_changed = 0;
972 gtk_widget_destroy(GTK_WIDGET(panel));
cf701cb7
HJYP
973 }
974}
975
e7a42ecf 976static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
977{
978 GtkWidget *about;
979 const gchar* authors[] = {
980 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
981 "Jim Huang <jserv.tw@gmail.com>",
982 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
983 "Fred Chien <cfsghost@gmail.com>",
984 "Daniel Kesler <kesler.daniel@gmail.com>",
985 "Juergen Hoetzel <juergen@archlinux.org>",
2918994e 986 "Marty Jack <martyj19@comcast.net>",
815e1027 987 "Martin Bagge <brother@bsnet.se>",
bb4cf963
AG
988 "Andriy Grytsenko <andrej@rep.kiev.ua>",
989 "Giuseppe Penone <giuspen@gmail.com>",
990 "Piotr Sipika <piotr.sipika@gmail.com>",
e7a42ecf 991 NULL
992 };
993 /* TRANSLATORS: Replace this string with your names, one name per line. */
994 gchar *translators = _( "translator-credits" );
995
996 about = gtk_about_dialog_new();
997 panel_apply_icon(GTK_WINDOW(about));
998 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
09fa171b 999 gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
4c91be3e
JL
1000
1001 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
1002 {
1003 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1004 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
1005 }
1006 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
1007 {
1008 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
1009 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
1010 }
0bcf9045 1011 else
4c91be3e
JL
1012 {
1013 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
0b806437 1014 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL));
4c91be3e
JL
1015 }
1016
b840f7cc 1017 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2014"));
e7a42ecf 1018 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
1019 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.");
1020 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
1021 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
1022 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
1023 gtk_dialog_run(GTK_DIALOG(about));
0bcf9045 1024 gtk_widget_destroy(about);
e7a42ecf 1025}
1026
1027void panel_apply_icon( GtkWindow *w )
1028{
0bcf9045
AG
1029 GdkPixbuf* window_icon;
1030
1031 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
4c91be3e 1032 {
0bcf9045
AG
1033 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
1034 }
1035 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
4c91be3e 1036 {
0bcf9045
AG
1037 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
1038 }
89496346 1039 else
4c91be3e 1040 {
0bcf9045
AG
1041 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/images/my-computer.png", NULL);
1042 }
21f91705 1043 gtk_window_set_icon(w, window_icon);
e7a42ecf 1044}
1045
a7bd16a4 1046GtkMenu* lxpanel_get_plugin_menu( LXPanel* panel, GtkWidget* plugin, gboolean use_sub_menu )
cf701cb7 1047{
e3b89f43 1048 GtkWidget *menu_item, *img;
1049 GtkMenu *ret,*menu;
191694fb 1050 const LXPanelPluginInit *init;
cf701cb7 1051 char* tmp;
eb1762e5 1052
e3b89f43 1053 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 1054
eb1762e5
AG
1055 if (plugin)
1056 {
1057 init = PLUGIN_CLASS(plugin);
1058 /* create single item - plugin instance settings */
1059 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1060 tmp = g_strdup_printf( _("\"%s\" Settings"), _(init->name) );
1061 menu_item = gtk_image_menu_item_new_with_label( tmp );
1062 g_free( tmp );
1063 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1064 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
1065 if( init->config )
1066 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
1067 else
1068 gtk_widget_set_sensitive( menu_item, FALSE );
1069 /* add custom items by plugin if requested */
1070 if (init->update_context_menu != NULL)
1071 use_sub_menu = init->update_context_menu(plugin, ret);
1072 /* append a separator */
1073 menu_item = gtk_separator_menu_item_new();
1074 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
1075 }
1076 if (use_sub_menu)
1077 menu = GTK_MENU(gtk_menu_new());
1078
4b93d81e
HJYP
1079 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
1080 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
1081 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1082 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1083 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
1084
1085 if( plugin )
1086 {
1087 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
17fab6e5 1088 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(init->name) );
cf701cb7
HJYP
1089 menu_item = gtk_image_menu_item_new_with_label( tmp );
1090 g_free( tmp );
1091 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1092 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1093 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
1094 }
1095
1096 menu_item = gtk_separator_menu_item_new();
1097 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1098
4b93d81e
HJYP
1099 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
1100 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
1101 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1102 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1103 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
1104
cf701cb7
HJYP
1105 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
1106 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
1107 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1108 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1109 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
1110
1111 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
1112 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
1113 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1114 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1115 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
1116 if( ! all_panels->next ) /* if this is the only panel */
1117 gtk_widget_set_sensitive( menu_item, FALSE );
1118
e7a42ecf 1119 menu_item = gtk_separator_menu_item_new();
1120 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1121
1122 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
1123 menu_item = gtk_image_menu_item_new_with_label(_("About"));
1124 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
1125 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
a7bd16a4 1126 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel->priv );
e7a42ecf 1127
cf701cb7
HJYP
1128 if( use_sub_menu )
1129 {
cf701cb7
HJYP
1130 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
1131 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 1132 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
3e7b8eb7
HJYP
1133 }
1134
eb1762e5 1135 gtk_widget_show_all(GTK_WIDGET(ret));
3e7b8eb7 1136
cf701cb7
HJYP
1137 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1138 return ret;
1139}
1140
17fab6e5
AG
1141/* for old plugins compatibility */
1142GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
1143{
a7bd16a4 1144 return lxpanel_get_plugin_menu(panel->topgwin, plugin->pwid, use_sub_menu);
17fab6e5
AG
1145}
1146
a52c2257
HJYP
1147/****************************************************
1148 * panel creation *
1149 ****************************************************/
176fb687 1150
a52c2257 1151static void
22242ed4 1152make_round_corners(Panel *p)
a52c2257 1153{
a97d06a6 1154 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 1155 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
1156}
1157
22242ed4 1158void panel_set_dock_type(Panel *p)
bee4c26e 1159{
09fa171b
AG
1160 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
1161
bee4c26e
HJYP
1162 if (p->setdocktype) {
1163 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
09fa171b 1164 XChangeProperty(xdisplay, p->topxwin,
bee4c26e
HJYP
1165 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
1166 PropModeReplace, (unsigned char *) &state, 1);
1167 }
1168 else {
09fa171b 1169 XDeleteProperty( xdisplay, p->topxwin, a_NET_WM_WINDOW_TYPE );
bee4c26e 1170 }
a52c2257
HJYP
1171}
1172
176fb687 1173void panel_establish_autohide(Panel *p)
1174{
a7bd16a4
AG
1175 _panel_establish_autohide(p->topgwin);
1176}
1177
1178void _panel_establish_autohide(LXPanel *p)
1179{
1180 if (p->priv->autohide)
9a2c13d0
AG
1181 ah_start(p);
1182 else
176fb687 1183 {
9a2c13d0
AG
1184 ah_stop(p);
1185 ah_state_set(p, AH_STATE_VISIBLE);
176fb687 1186 }
1187}
1188
8f9e6256 1189/* Set an image from a file with scaling to the panel icon size. */
fcb35553 1190void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
8f9e6256 1191{
1192 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
1193 if (pixbuf != NULL)
1194 {
1195 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1196 g_object_unref(pixbuf);
1197 }
1198}
1199
a7bd16a4
AG
1200void lxpanel_image_set_from_file(LXPanel * p, GtkWidget * image, const char * file)
1201{
1202 panel_image_set_from_file(p->priv, image, file);
1203}
1204
c14620f2
MJ
1205/* Set an image from a icon theme with scaling to the panel icon size. */
1206gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
1207{
1b532bce 1208 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
c14620f2 1209 {
1b532bce 1210 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
c14620f2
MJ
1211 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
1212 g_object_unref(pixbuf);
1213 return TRUE;
1214 }
1215 return FALSE;
1216}
1217
a7bd16a4
AG
1218gboolean lxpanel_image_set_icon_theme(LXPanel * p, GtkWidget * image, const gchar * icon)
1219{
1220 return panel_image_set_icon_theme(p->priv, image, icon);
1221}
1222
0defe4b9 1223static void
a7bd16a4 1224panel_start_gui(LXPanel *panel)
a52c2257
HJYP
1225{
1226 Atom state[3];
1227 XWMHints wmhints;
0bfcc6c9 1228 gulong val;
09fa171b 1229 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
a7bd16a4
AG
1230 Panel *p = panel->priv;
1231 GtkWidget *w = GTK_WIDGET(panel);
6db11841 1232
a52c2257
HJYP
1233 ENTER;
1234
9dd114c4 1235 p->curdesk = get_net_current_desktop();
1236 p->desknum = get_net_number_of_desktops();
1237 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1238
cb22c87c
HJYP
1239 /* main toplevel window */
1240 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
a7bd16a4 1241 gtk_widget_set_name(w, "PanelToplevel");
176fb687 1242 p->display = gdk_display_get_default();
a7bd16a4
AG
1243 gtk_container_set_border_width(GTK_CONTAINER(panel), 0);
1244 gtk_window_set_resizable(GTK_WINDOW(panel), FALSE);
1245 gtk_window_set_wmclass(GTK_WINDOW(panel), "panel", "lxpanel");
1246 gtk_window_set_title(GTK_WINDOW(panel), "panel");
1247 gtk_window_set_position(GTK_WINDOW(panel), GTK_WIN_POS_NONE);
1248 gtk_window_set_decorated(GTK_WINDOW(panel), FALSE);
77886b88 1249
a7bd16a4 1250 gtk_window_group_add_window( win_grp, (GtkWindow*)panel );
e2957bd2 1251
a7bd16a4 1252 gtk_widget_add_events( w, GDK_BUTTON_PRESS_MASK );
f2d54481 1253
a7bd16a4 1254 gtk_widget_realize(w);
175f73d1 1255 //gdk_window_set_decorations(gtk_widget_get_window(p->topgwin), 0);
2de71c90 1256
4542c20d 1257 // main layout manager as a single child of panel
a7bd16a4 1258 p->box = panel_box_new(panel, FALSE, 0);
a52c2257 1259 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
a7bd16a4 1260 gtk_container_add(GTK_CONTAINER(panel), p->box);
a52c2257 1261 gtk_widget_show(p->box);
a97d06a6
HJYP
1262 if (p->round_corners)
1263 make_round_corners(p);
6db11841 1264
a7bd16a4 1265 p->topxwin = GDK_WINDOW_XWINDOW(gtk_widget_get_window(w));
a52c2257
HJYP
1266 DBG("topxwin = %x\n", p->topxwin);
1267
1268 /* the settings that should be done before window is mapped */
1269 wmhints.flags = InputHint;
1270 wmhints.input = 0;
09fa171b 1271 XSetWMHints (xdisplay, p->topxwin, &wmhints);
24053345 1272#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257 1273 val = WIN_HINTS_SKIP_FOCUS;
09fa171b
AG
1274 XChangeProperty(xdisplay, p->topxwin,
1275 XInternAtom(xdisplay, "_WIN_HINTS", False), XA_CARDINAL, 32,
a52c2257
HJYP
1276 PropModeReplace, (unsigned char *) &val, 1);
1277
bee4c26e 1278 panel_set_dock_type(p);
a52c2257
HJYP
1279
1280 /* window mapping point */
a7bd16a4 1281 gtk_widget_show_all(w);
a52c2257
HJYP
1282
1283 /* the settings that should be done after window is mapped */
a7bd16a4 1284 _panel_establish_autohide(panel);
a52c2257
HJYP
1285
1286 /* send it to running wm */
0bfcc6c9 1287 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, G_MAXULONG, 0, 0, 0, 0);
a52c2257 1288 /* and assign it ourself just for case when wm is not running */
0bfcc6c9 1289 val = G_MAXULONG;
09fa171b 1290 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
a52c2257
HJYP
1291 PropModeReplace, (unsigned char *) &val, 1);
1292
1293 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1294 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1295 state[2] = a_NET_WM_STATE_STICKY;
09fa171b 1296 XChangeProperty(xdisplay, p->topxwin, a_NET_WM_STATE, XA_ATOM,
a52c2257
HJYP
1297 32, PropModeReplace, (unsigned char *) state, 3);
1298
a7bd16a4
AG
1299 _calculate_position(panel);
1300 gdk_window_move_resize(gtk_widget_get_window(w), p->ax, p->ay, p->aw, p->ah);
1301 _panel_set_wm_strut(panel);
5eaabb1b 1302 p->initialized = TRUE;
77886b88 1303
a52c2257
HJYP
1304 RET();
1305}
1306
2918994e 1307/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1308void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 1309{
8ed3b3d4 1310 if ((p->height_label != NULL) && (p->width_label != NULL)
1311 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
9dd114c4 1312 {
1313 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1314 {
1315 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1316 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 1317 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1318 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 1319 }
1320 else
1321 {
1322 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1323 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 1324 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1325 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 1326 }
1327 }
1328}
1329
2918994e 1330/* Draw text into a label, with the user preference color and optionally bold. */
38ac664c
AG
1331void panel_draw_label_text(Panel * p, GtkWidget * label, const char * text,
1332 gboolean bold, float custom_size_factor,
1333 gboolean custom_color)
2918994e 1334{
2918994e 1335 if (text == NULL)
1336 {
1337 /* Null string. */
1338 gtk_label_set_text(GTK_LABEL(label), NULL);
83d410c1 1339 return;
2918994e 1340 }
1341
83d410c1
HG
1342 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1343 int font_desc;
1344 if (p->usefontsize)
1345 font_desc = p->fontsize;
f669ce5e 1346 else
2918994e 1347 {
761e0dae
AG
1348 GtkStyle *style = gtk_widget_get_style(label);
1349 font_desc = pango_font_description_get_size(style->font_desc) / PANGO_SCALE;
83d410c1
HG
1350 }
1351 font_desc *= custom_size_factor;
1352
1353 /* Check the string for characters that need to be escaped.
1354 * If any are found, create the properly escaped string and use it instead. */
38ac664c 1355 const char * valid_markup = text;
83d410c1 1356 char * escaped_text = NULL;
38ac664c 1357 const char * q;
83d410c1
HG
1358 for (q = text; *q != '\0'; q += 1)
1359 {
1360 if ((*q == '<') || (*q == '>') || (*q == '&'))
2918994e 1361 {
83d410c1
HG
1362 escaped_text = g_markup_escape_text(text, -1);
1363 valid_markup = escaped_text;
1364 break;
2918994e 1365 }
83d410c1 1366 }
2918994e 1367
83d410c1
HG
1368 gchar * formatted_text;
1369 if ((custom_color) && (p->usefontcolor))
1370 {
1371 /* Color, optionally bold. */
1372 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
f669ce5e 1373 font_desc,
2918994e 1374 gcolor2rgb24(&p->gfontcolor),
1375 ((bold) ? "<b>" : ""),
1376 valid_markup,
1377 ((bold) ? "</b>" : ""));
83d410c1
HG
1378 }
1379 else
1380 {
1381 /* No color, optionally bold. */
1382 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
f669ce5e 1383 font_desc,
1384 ((bold) ? "<b>" : ""),
1385 valid_markup,
1386 ((bold) ? "</b>" : ""));
2918994e 1387 }
83d410c1
HG
1388
1389 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1390 g_free(formatted_text);
1391 g_free(escaped_text);
2918994e 1392}
1393
a7bd16a4
AG
1394void lxpanel_draw_label_text(LXPanel * p, GtkWidget * label, const char * text,
1395 gboolean bold, float custom_size_factor,
1396 gboolean custom_color)
1397{
1398 panel_draw_label_text(p->priv, label, text, bold, custom_size_factor, custom_color);
1399}
1400
2918994e 1401void panel_set_panel_configuration_changed(Panel *p)
a97d06a6 1402{
a7bd16a4
AG
1403 _panel_set_panel_configuration_changed(p->topgwin);
1404}
1405
1406void _panel_set_panel_configuration_changed(LXPanel *panel)
1407{
1408 Panel *p = panel->priv;
17fab6e5 1409 GList *plugins, *l;
9dd114c4 1410
cfde283a 1411 GtkOrientation previous_orientation = p->orientation;
a97d06a6 1412 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
cfde283a 1413 ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
9dd114c4 1414
cfde283a 1415 /* either first run or orientation was changed */
5eaabb1b 1416 if (!p->initialized || previous_orientation != p->orientation)
9dd114c4 1417 {
1418 panel_adjust_geometry_terminology(p);
5eaabb1b 1419 if (p->initialized)
f5c7784a 1420 p->height = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
9dd114c4 1421 if (p->height_control != NULL)
1422 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1423 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1424 {
cfde283a 1425 int value = ((p->orientation == GTK_ORIENTATION_HORIZONTAL) ? gdk_screen_width() : gdk_screen_height());
2918994e 1426 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1427 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1428 }
9dd114c4 1429 }
1430
5eaabb1b 1431 /* FIXME: it's deprecated, kept for binary compatibility */
cfde283a 1432 if (p->orientation == GTK_ORIENTATION_HORIZONTAL) {
a97d06a6
HJYP
1433 p->my_box_new = gtk_hbox_new;
1434 p->my_separator_new = gtk_vseparator_new;
1435 } else {
1436 p->my_box_new = gtk_vbox_new;
1437 p->my_separator_new = gtk_hseparator_new;
1438 }
1439
1440 /* recreate the main layout box */
964b8b7e 1441 if (p->box != NULL)
1442 {
cfde283a 1443 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), p->orientation);
a97d06a6 1444 }
9dd114c4 1445
a97d06a6
HJYP
1446 /* NOTE: This loop won't be executed when panel started since
1447 plugins are not loaded at that time.
1448 This is used when the orientation of the panel is changed
1449 from the config dialog, and plugins should be re-layout.
1450 */
17fab6e5
AG
1451 plugins = p->box ? gtk_container_get_children(GTK_CONTAINER(p->box)) : NULL;
1452 for( l = plugins; l; l = l->next ) {
1453 GtkWidget *w = (GtkWidget*)l->data;
191694fb 1454 const LXPanelPluginInit *init = PLUGIN_CLASS(w);
17fab6e5 1455 if (init->reconfigure)
a7bd16a4 1456 init->reconfigure(panel, w);
a97d06a6 1457 }
17fab6e5 1458 g_list_free(plugins);
3154b4ef
AG
1459 /* panel geometry changed? update panel background then */
1460 g_idle_add_full( G_PRIORITY_LOW,
a7bd16a4 1461 (GSourceFunc)delay_update_background, panel, NULL );
a97d06a6
HJYP
1462}
1463
a52c2257 1464static int
17fab6e5 1465panel_parse_global(Panel *p, config_setting_t *cfg)
a52c2257 1466{
4bca3e51
AG
1467 const char *str;
1468 gint i;
6db11841 1469
17fab6e5
AG
1470 /* check Global config */
1471 if (!cfg || strcmp(config_setting_get_name(cfg), "Global") != 0)
3e7b8eb7 1472 {
06e29ce1 1473 g_warning( "lxpanel: Global section not found");
17fab6e5
AG
1474 RET(0);
1475 }
4bca3e51
AG
1476 if (config_setting_lookup_string(cfg, "edge", &str))
1477 p->edge = str2num(edge_pair, str, EDGE_NONE);
1478 if (config_setting_lookup_string(cfg, "allign", &str))
1479 p->allign = str2num(allign_pair, str, ALLIGN_NONE);
1480 config_setting_lookup_int(cfg, "monitor", &p->monitor);
1481 config_setting_lookup_int(cfg, "margin", &p->margin);
1482 if (config_setting_lookup_string(cfg, "widthtype", &str))
1483 p->widthtype = str2num(width_pair, str, WIDTH_NONE);
1484 config_setting_lookup_int(cfg, "width", &p->width);
1485 if (config_setting_lookup_string(cfg, "heighttype", &str))
1486 p->heighttype = str2num(height_pair, str, HEIGHT_NONE);
1487 config_setting_lookup_int(cfg, "height", &p->height);
1488 if (config_setting_lookup_int(cfg, "spacing", &i) && i > 0)
1489 p->spacing = i;
1490 if (config_setting_lookup_int(cfg, "setdocktype", &i))
1491 p->setdocktype = i != 0;
1492 if (config_setting_lookup_int(cfg, "setpartialstrut", &i))
1493 p->setstrut = i != 0;
1494 if (config_setting_lookup_int(cfg, "RoundCorners", &i))
1495 p->round_corners = i != 0;
1496 if (config_setting_lookup_int(cfg, "transparent", &i))
1497 p->transparent = i != 0;
1498 if (config_setting_lookup_int(cfg, "alpha", &p->alpha))
17fab6e5 1499 {
17fab6e5
AG
1500 if (p->alpha > 255)
1501 p->alpha = 255;
1502 }
4bca3e51
AG
1503 if (config_setting_lookup_int(cfg, "autohide", &i))
1504 p->autohide = i != 0;
9a2c13d0
AG
1505 if (config_setting_lookup_int(cfg, "heightwhenhidden", &i))
1506 p->height_when_hidden = MAX(0, i);
4bca3e51 1507 if (config_setting_lookup_string(cfg, "tintcolor", &str))
17fab6e5 1508 {
4bca3e51 1509 if (!gdk_color_parse (str, &p->gtintcolor))
17fab6e5
AG
1510 gdk_color_parse ("white", &p->gtintcolor);
1511 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1512 DBG("tintcolor=%x\n", p->tintcolor);
1513 }
4bca3e51
AG
1514 if (config_setting_lookup_int(cfg, "usefontcolor", &i))
1515 p->usefontcolor = i != 0;
1516 if (config_setting_lookup_string(cfg, "fontcolor", &str))
17fab6e5 1517 {
4bca3e51 1518 if (!gdk_color_parse (str, &p->gfontcolor))
17fab6e5
AG
1519 gdk_color_parse ("black", &p->gfontcolor);
1520 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1521 DBG("fontcolor=%x\n", p->fontcolor);
1522 }
4bca3e51
AG
1523 if (config_setting_lookup_int(cfg, "usefontsize", &i))
1524 p->usefontsize = i != 0;
1525 if (config_setting_lookup_int(cfg, "fontsize", &i) && i > 0)
1526 p->fontsize = i;
1527 if (config_setting_lookup_int(cfg, "background", &i))
1528 p->background = i != 0;
1529 if (config_setting_lookup_string(cfg, "backgroundfile", &str))
1530 p->background_file = g_strdup(str);
1531 config_setting_lookup_int(cfg, "iconsize", &p->icon_size);
4542c20d 1532
9dd114c4 1533 panel_normalize_configuration(p);
239cb032 1534
9dd114c4 1535 return 1;
a52c2257
HJYP
1536}
1537
1538static int
a7bd16a4 1539panel_parse_plugin(LXPanel *p, config_setting_t *cfg)
a52c2257 1540{
4bca3e51 1541 const char *type = NULL;
db449f6e 1542
a52c2257 1543 ENTER;
4bca3e51 1544 config_setting_lookup_string(cfg, "type", &type);
17fab6e5 1545 DBG("plug %s\n", type);
db449f6e 1546
17fab6e5 1547 if (!type || lxpanel_add_plugin(p, type, cfg, -1) == NULL) {
06e29ce1 1548 g_warning( "lxpanel: can't load %s plugin", type);
a52c2257
HJYP
1549 goto error;
1550 }
a52c2257 1551 RET(1);
db449f6e 1552
17fab6e5 1553error:
a52c2257 1554 RET(0);
a52c2257
HJYP
1555}
1556
adf42c84 1557static int panel_start( LXPanel *p )
a52c2257 1558{
17fab6e5
AG
1559 config_setting_t *list, *s;
1560 int i;
db449f6e 1561
a52c2257
HJYP
1562 /* parse global section */
1563 ENTER;
8110399f 1564
a7bd16a4
AG
1565 list = config_setting_get_member(config_root_setting(p->priv->config), "");
1566 if (!list || !panel_parse_global(p->priv, config_setting_get_elem(list, 0)))
a52c2257
HJYP
1567 RET(0);
1568
9dd114c4 1569 panel_start_gui(p);
1570
17fab6e5
AG
1571 for (i = 1; (s = config_setting_get_elem(list, i)) != NULL; )
1572 if (strcmp(config_setting_get_name(s), "Plugin") == 0 &&
1573 panel_parse_plugin(p, s)) /* success on plugin start */
1574 i++;
1575 else /* remove invalid data from config */
1576 config_setting_remove_elem(list, i);
4542c20d
HJYP
1577
1578 /* update backgrond of panel and all plugins */
a7bd16a4 1579 _panel_update_background(p);
9dd114c4 1580 return 1;
a52c2257
HJYP
1581}
1582
8110399f 1583void panel_destroy(Panel *p)
a52c2257 1584{
a7bd16a4 1585 gtk_widget_destroy(GTK_WIDGET(p->topgwin));
a52c2257
HJYP
1586}
1587
a7bd16a4 1588static LXPanel* panel_new( const char* config_file, const char* config_name )
8110399f 1589{
a7bd16a4 1590 LXPanel* panel = NULL;
16fbda14 1591
17fab6e5 1592 if (G_LIKELY(config_file))
cf701cb7 1593 {
17fab6e5 1594 panel = panel_allocate();
a7bd16a4 1595 panel->priv->name = g_strdup(config_name);
17fab6e5 1596 g_debug("starting panel from file %s",config_file);
a7bd16a4 1597 if (!config_read_file(panel->priv->config, config_file) ||
17fab6e5 1598 !panel_start(panel))
3e7b8eb7 1599 {
06e29ce1 1600 g_warning( "lxpanel: can't start panel");
a7bd16a4 1601 gtk_widget_destroy(GTK_WIDGET(panel));
17fab6e5 1602 panel = NULL;
cf701cb7 1603 }
cf701cb7
HJYP
1604 }
1605 return panel;
8110399f 1606}
a52c2257 1607
0defe4b9 1608static void
a52c2257
HJYP
1609usage()
1610{
e7cb732b
HJYP
1611 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1612 g_print(_("Command line options:\n"));
1613 g_print(_(" --help -- print this help and exit\n"));
1614 g_print(_(" --version -- print version and exit\n"));
06e29ce1 1615// g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1616// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1617 g_print(_(" --profile name -- use specified profile\n"));
1618 g_print("\n");
1619 g_print(_(" -h -- same as --help\n"));
1620 g_print(_(" -p -- same as --profile\n"));
1621 g_print(_(" -v -- same as --version\n"));
2918994e 1622 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1623 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1624}
1625
e68b47dc
JH
1626/* Lightweight lock related functions - X clipboard hacks */
1627
1628#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1629
1630/*
1631 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1632 */
1633static void
1634clipboard_get_func(
1635 GtkClipboard *clipboard G_GNUC_UNUSED,
1636 GtkSelectionData *selection_data G_GNUC_UNUSED,
1637 guint info G_GNUC_UNUSED,
1638 gpointer user_data_or_owner G_GNUC_UNUSED)
1639{
1640}
1641
1642/*
1643 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1644 */
1645static void clipboard_clear_func(
1646 GtkClipboard *clipboard G_GNUC_UNUSED,
1647 gpointer user_data_or_owner G_GNUC_UNUSED)
1648{
1649}
1650
1651/*
1652 * Lightweight version for checking single instance.
1653 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1654 *
1655 * Returns TRUE if successfully retrieved and FALSE otherwise.
1656 */
16fb8c2e 1657static gboolean check_main_lock()
e68b47dc
JH
1658{
1659 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1660 gboolean retval = FALSE;
1661 GtkClipboard *clipboard;
1662 Atom atom;
09fa171b 1663 Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
e68b47dc
JH
1664
1665 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1666
09fa171b 1667 XGrabServer(xdisplay);
e68b47dc 1668
09fa171b 1669 if (XGetSelectionOwner(xdisplay, atom) != None)
e68b47dc
JH
1670 goto out;
1671
1672 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1673
1674 if (gtk_clipboard_set_with_data(clipboard, targets,
1675 G_N_ELEMENTS (targets),
1676 clipboard_get_func,
1677 clipboard_clear_func, NULL))
1678 retval = TRUE;
1679
1680out:
09fa171b 1681 XUngrabServer (xdisplay);
e68b47dc
JH
1682 gdk_flush ();
1683
1684 return retval;
1685}
1686#undef CLIPBOARD_NAME
1687
1f4bc3aa 1688static void _start_panels_from_dir(const char *panel_dir)
8110399f 1689{
1f4bc3aa
AG
1690 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1691 const gchar* name;
cf701cb7 1692
1f4bc3aa
AG
1693 if( ! dir )
1694 {
1695 return;
1696 }
cf701cb7 1697
1f4bc3aa
AG
1698 while((name = g_dir_read_name(dir)) != NULL)
1699 {
1700 char* panel_config = g_build_filename( panel_dir, name, NULL );
0bcf9045 1701 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
cf701cb7 1702 {
a7bd16a4 1703 LXPanel* panel = panel_new( panel_config, name );
1f4bc3aa
AG
1704 if( panel )
1705 all_panels = g_slist_prepend( all_panels, panel );
cf701cb7 1706 }
1f4bc3aa 1707 g_free( panel_config );
cf701cb7 1708 }
1f4bc3aa
AG
1709 g_dir_close( dir );
1710}
1711
1712static gboolean start_all_panels( )
1713{
1714 char *panel_dir;
8ea02629 1715 const gchar * const * dir;
1f4bc3aa
AG
1716
1717 /* try user panels */
1718 panel_dir = _user_config_file_name("panels", NULL);
1719 _start_panels_from_dir(panel_dir);
1720 g_free(panel_dir);
1721 if (all_panels != NULL)
1722 return TRUE;
8ea02629
AG
1723 /* else try XDG fallbacks */
1724 dir = g_get_system_config_dirs();
1725 if (dir) while (dir[0])
1726 {
1727 panel_dir = _system_config_file_name(dir[0], "panels");
1728 _start_panels_from_dir(panel_dir);
1729 g_free(panel_dir);
1730 if (all_panels != NULL)
1731 return TRUE;
1732 dir++;
1733 }
1f4bc3aa
AG
1734 /* last try at old fallback for compatibility reasons */
1735 panel_dir = _old_system_config_file_name("panels");
1736 _start_panels_from_dir(panel_dir);
1737 g_free(panel_dir);
cf701cb7 1738 return all_panels != NULL;
8110399f
HJYP
1739}
1740
cf701cb7
HJYP
1741void load_global_config();
1742void free_global_config();
1743
1f4bc3aa
AG
1744static void _ensure_user_config_dirs(void)
1745{
1746 char *dir = g_build_filename(g_get_user_config_dir(), "lxpanel", cprofile,
1747 "panels", NULL);
1748
1749 /* make sure the private profile and panels dir exists */
1750 g_mkdir_with_parents(dir, 0700);
1751 g_free(dir);
1752}
1753
8110399f 1754int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1755{
1756 int i;
9df6826f 1757 const char* desktop_name;
f3eacf81 1758 char *file;
f277dbb7 1759
a52c2257 1760 setlocale(LC_CTYPE, "");
f277dbb7 1761
0bcf9045
AG
1762 g_thread_init(NULL);
1763/* gdk_threads_init();
1764 gdk_threads_enter(); */
1d434622 1765
a52c2257
HJYP
1766 gtk_init(&argc, &argv);
1767
1768#ifdef ENABLE_NLS
1769 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1770 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1771 textdomain ( GETTEXT_PACKAGE );
1772#endif
1773
1774 XSetLocaleModifiers("");
2918994e 1775 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1776
a52c2257 1777 resolve_atoms();
8110399f 1778
9df6826f
HJYP
1779 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1780 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
95095259 1781
a52c2257
HJYP
1782 for (i = 1; i < argc; i++) {
1783 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1784 usage();
1785 exit(0);
1786 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1787 printf("lxpanel %s\n", version);
1788 exit(0);
1789 } else if (!strcmp(argv[i], "--log")) {
1790 i++;
1791 if (i == argc) {
06e29ce1 1792 g_critical( "lxpanel: missing log level");
a52c2257
HJYP
1793 usage();
1794 exit(1);
1795 } else {
06e29ce1 1796 /* deprecated */
a52c2257
HJYP
1797 }
1798 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1799 config = 1;
1800 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1801 i++;
1802 if (i == argc) {
06e29ce1 1803 g_critical( "lxpanel: missing profile name");
a52c2257
HJYP
1804 usage();
1805 exit(1);
1806 } else {
1807 cprofile = g_strdup(argv[i]);
1808 }
1809 } else {
1810 printf("lxpanel: unknown option - %s\n", argv[i]);
1811 usage();
1812 exit(1);
1813 }
1814 }
f277dbb7 1815
12eaee21
AG
1816 /* Add a gtkrc file to be parsed too. */
1817 file = _user_config_file_name("gtkrc", NULL);
1818 gtk_rc_parse(file);
1819 g_free(file);
1820
8110399f 1821 /* Check for duplicated lxpanel instances */
e68b47dc 1822 if (!check_main_lock() && !config) {
e7a42ecf 1823 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1824 exit(1);
1825 }
1826
1f4bc3aa
AG
1827 _ensure_user_config_dirs();
1828
730e3f7b 1829 /* Add our own icons to the search path of icon theme */
0b806437 1830 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/images" );
730e3f7b 1831
cf701cb7 1832 fbev = fb_ev_new();
e2957bd2 1833 win_grp = gtk_window_group_new();
22242ed4 1834
f7cb330e
HJYP
1835 is_restarting = FALSE;
1836
17fab6e5
AG
1837 /* init LibFM */
1838 fm_gtk_init(NULL);
1839
1840 /* prepare modules data */
1841 _prepare_modules();
1842
cf701cb7
HJYP
1843 load_global_config();
1844
0bcf9045
AG
1845 /* NOTE: StructureNotifyMask is required by XRandR
1846 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1847 */
0f83fb71 1848 gdk_window_set_events(gdk_get_default_root_window(), GDK_STRUCTURE_MASK |
1849 GDK_SUBSTRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK);
cf701cb7 1850 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1851
cf701cb7
HJYP
1852 if( G_UNLIKELY( ! start_all_panels() ) )
1853 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1854/*
1855 * FIXME: configure??
6a6ad54e
HJYP
1856 if (config)
1857 configure();
8110399f 1858*/
6a6ad54e 1859 gtk_main();
8110399f 1860
09fa171b 1861 XSelectInput (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), GDK_ROOT_WINDOW(), NoEventMask);
cf701cb7
HJYP
1862 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1863
8110399f 1864 /* destroy all panels */
a7bd16a4 1865 g_slist_foreach( all_panels, (GFunc) gtk_widget_destroy, NULL );
2a26c0d8
HJYP
1866 g_slist_free( all_panels );
1867 all_panels = NULL;
6a6ad54e 1868 g_free( cfgfile );
5541b8d2 1869
cf701cb7
HJYP
1870 free_global_config();
1871
17fab6e5
AG
1872 _unload_modules();
1873 fm_gtk_finalize();
1874
74bf5dbf 1875 /* gdk_threads_leave(); */
3e71f8af 1876
e2957bd2 1877 g_object_unref(win_grp);
22242ed4
HJYP
1878 g_object_unref(fbev);
1879
a7bd16a4
AG
1880 if (!is_restarting)
1881 return 0;
36f544e2 1882 if (strchr(argv[0], G_DIR_SEPARATOR))
a7bd16a4
AG
1883 execve(argv[0], argv, env);
1884 else
1885 execve(g_find_program_in_path(argv[0]), argv, env);
36f544e2 1886 return 1;
9c338caf
AG
1887}
1888
a7bd16a4 1889GtkOrientation panel_get_orientation(LXPanel *panel)
9c338caf 1890{
a7bd16a4 1891 return panel->priv->orientation;
9c338caf
AG
1892}
1893
a7bd16a4 1894gint panel_get_icon_size(LXPanel *panel)
9c338caf 1895{
a7bd16a4 1896 return panel->priv->icon_size;
9c338caf
AG
1897}
1898
a7bd16a4 1899gint panel_get_height(LXPanel *panel)
9c338caf 1900{
a7bd16a4 1901 return panel->priv->height;
9c338caf
AG
1902}
1903
a7bd16a4 1904Window panel_get_xwindow(LXPanel *panel)
4718ca02 1905{
a7bd16a4 1906 return panel->priv->topxwin;
4718ca02
AG
1907}
1908
a7bd16a4 1909gint panel_get_monitor(LXPanel *panel)
4718ca02 1910{
a7bd16a4 1911 return panel->priv->monitor;
4718ca02
AG
1912}
1913
a7bd16a4 1914GtkStyle *panel_get_defstyle(LXPanel *panel)
9c338caf 1915{
a7bd16a4 1916 return panel->priv->defstyle;
9c338caf
AG
1917}
1918
a7bd16a4 1919GtkIconTheme *panel_get_icon_theme(LXPanel *panel)
9c338caf 1920{
a7bd16a4 1921 return panel->priv->icon_theme;
9c338caf
AG
1922}
1923
a7bd16a4 1924gboolean panel_is_at_bottom(LXPanel *panel)
4718ca02 1925{
a7bd16a4 1926 return panel->priv->edge == EDGE_BOTTOM;
4718ca02
AG
1927}
1928
072944bf
AG
1929gboolean panel_is_dynamic(LXPanel *panel)
1930{
1931 return panel->priv->widthtype == WIDTH_REQUEST;
1932}
1933
a7bd16a4 1934GtkWidget *panel_box_new(LXPanel *panel, gboolean homogeneous, gint spacing)
9c338caf 1935{
a7bd16a4 1936 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5eaabb1b
AG
1937 return gtk_hbox_new(homogeneous, spacing);
1938 return gtk_vbox_new(homogeneous, spacing);
9c338caf
AG
1939}
1940
a7bd16a4 1941GtkWidget *panel_separator_new(LXPanel *panel)
9c338caf 1942{
a7bd16a4 1943 if (panel->priv->orientation == GTK_ORIENTATION_HORIZONTAL)
5eaabb1b
AG
1944 return gtk_vseparator_new();
1945 return gtk_hseparator_new();
9c338caf 1946}
10d93053 1947
191694fb 1948gboolean _class_is_present(const LXPanelPluginInit *init)
10d93053
AG
1949{
1950 GSList *sl;
1951
1952 for (sl = all_panels; sl; sl = sl->next )
1953 {
a7bd16a4 1954 LXPanel *panel = (LXPanel*)sl->data;
10d93053
AG
1955 GList *plugins, *p;
1956
a7bd16a4 1957 plugins = gtk_container_get_children(GTK_CONTAINER(panel->priv->box));
10d93053
AG
1958 for (p = plugins; p; p = p->next)
1959 if (PLUGIN_CLASS(p->data) == init)
1960 {
1961 g_list_free(plugins);
1962 return TRUE;
1963 }
1964 g_list_free(plugins);
1965 }
1966 return FALSE;
1967}