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