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