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