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