allow custom font scaling in panel_draw_label_text(), use in xkb
[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"));
4c91be3e
JL
699
700 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
701 {
702 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
703 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
704 }
705 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
706 {
707 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
708 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
709 }
710 else
711 {
712 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
713 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL));
714 }
715
815e1027 716 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2011"));
e7a42ecf 717 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
718 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.");
719 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
720 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
721 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
722 gtk_dialog_run(GTK_DIALOG(about));
723 gtk_widget_destroy(about);
724}
725
726void panel_apply_icon( GtkWindow *w )
727{
d60c9372
MR
728 GdkPixbuf* window_icon;
729
4c91be3e
JL
730 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
731 {
3ad38785 732 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
d60c9372 733 }
4c91be3e
JL
734 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
735 {
736 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
89496346
JL
737 }
738 else
4c91be3e 739 {
d60c9372
MR
740 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL);
741 }
21f91705 742 gtk_window_set_icon(w, window_icon);
e7a42ecf 743}
744
9dd114c4 745GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
cf701cb7 746{
e3b89f43 747 GtkWidget *menu_item, *img;
748 GtkMenu *ret,*menu;
749
cf701cb7 750 char* tmp;
e3b89f43 751 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 752
4b93d81e
HJYP
753 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
754 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
755 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
756 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
757 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
758
759 if( plugin )
760 {
761 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
762 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
763 menu_item = gtk_image_menu_item_new_with_label( tmp );
764 g_free( tmp );
765 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
766 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
767 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
768 }
769
770 menu_item = gtk_separator_menu_item_new();
771 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
772
4b93d81e
HJYP
773 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
774 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
775 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
776 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
777 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
778
cf701cb7
HJYP
779 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
780 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
781 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
782 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
783 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
3e7b8eb7
HJYP
784 /* FIXME: Potentially we can support multiple panels at the same edge,
785 * but currently this cannot be done due to some positioning problems. */
786 /* currently, disable the option when there are already four panels */
787 if( g_slist_length( all_panels ) >= 4 )
788 gtk_widget_set_sensitive( menu_item, FALSE );
cf701cb7
HJYP
789
790 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
791 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
792 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
793 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
794 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
795 if( ! all_panels->next ) /* if this is the only panel */
796 gtk_widget_set_sensitive( menu_item, FALSE );
797
e7a42ecf 798 menu_item = gtk_separator_menu_item_new();
799 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
800
801 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
802 menu_item = gtk_image_menu_item_new_with_label(_("About"));
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_about), panel );
806
cf701cb7
HJYP
807 if( use_sub_menu )
808 {
e3b89f43 809 ret = GTK_MENU(gtk_menu_new());
cf701cb7
HJYP
810 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
811 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 812 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
cf701cb7 813
e3b89f43 814 gtk_widget_show_all(GTK_WIDGET(ret));
cf701cb7
HJYP
815 }
816
3e7b8eb7
HJYP
817 if( plugin )
818 {
819 menu_item = gtk_separator_menu_item_new();
820 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
821
822 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
823 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
824 menu_item = gtk_image_menu_item_new_with_label( tmp );
825 g_free( tmp );
826 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
827 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
828 if( plugin->class->config )
829 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
830 else
831 gtk_widget_set_sensitive( menu_item, FALSE );
832 }
833
e3b89f43 834 gtk_widget_show_all(GTK_WIDGET(menu));
3e7b8eb7 835
cf701cb7
HJYP
836 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
837 return ret;
838}
839
a52c2257
HJYP
840/****************************************************
841 * panel creation *
842 ****************************************************/
176fb687 843
a52c2257 844static void
22242ed4 845make_round_corners(Panel *p)
a52c2257 846{
a97d06a6 847 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 848 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
849}
850
22242ed4 851void panel_set_dock_type(Panel *p)
bee4c26e
HJYP
852{
853 if (p->setdocktype) {
854 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
855 XChangeProperty(GDK_DISPLAY(), p->topxwin,
856 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
857 PropModeReplace, (unsigned char *) &state, 1);
858 }
859 else {
860 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
861 }
a52c2257
HJYP
862}
863
176fb687 864static void panel_set_visibility(Panel *p, gboolean visible)
865{
866 if ( ! visible) gtk_widget_hide(p->box);
867 p->visible = visible;
868 calculate_position(p);
869 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
870 gdk_window_move(p->topgwin->window, p->ax, p->ay);
871 if (visible) gtk_widget_show(p->box);
872 panel_set_wm_strut(p);
873}
874
875static gboolean panel_leave_real(Panel *p)
876{
fab48ed0 877 /* If the pointer is grabbed by this application, leave the panel displayed.
878 * There is no way to determine if it is grabbed by another application, such as an application that has a systray icon. */
176fb687 879 if (gdk_display_pointer_is_grabbed(p->display))
880 return TRUE;
881
fab48ed0 882 /* If the pointer is inside the panel, leave the panel displayed. */
176fb687 883 gint x, y;
884 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
885 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
886 return TRUE;
887
fab48ed0 888 /* If the panel is configured to autohide and if it is visible, hide the panel. */
176fb687 889 if ((p->autohide) && (p->visible))
890 panel_set_visibility(p, FALSE);
891
fab48ed0 892 /* Clear the timer. */
176fb687 893 p->hide_timeout = 0;
894 return FALSE;
895}
896
897static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
898{
fab48ed0 899 /* We may receive multiple enter-notify events when the pointer crosses into the panel.
900 * Do extra tests to make sure this does not cause misoperation such as blinking.
901 * If the pointer is inside the panel, unhide it. */
902 gint x, y;
903 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
904 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
905 {
906 /* If the pointer is inside the panel and we have not already unhidden it, do so and
907 * set a timer to recheck it in a half second. */
908 if (p->hide_timeout == 0)
909 {
910 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
911 panel_set_visibility(p, TRUE);
912 }
913 }
914 else
915 {
916 /* If the pointer is not inside the panel, simulate a timer expiration. */
917 panel_leave_real(p);
918 }
176fb687 919 return TRUE;
920}
921
922static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
923 gint y, guint time, Panel *p)
924{
925 panel_enter(NULL, NULL, p);
926 return TRUE;
927}
928
929void panel_establish_autohide(Panel *p)
930{
931 if (p->autohide)
932 {
933 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
934 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
935 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
936 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
937 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
938 panel_enter(NULL, NULL, p);
939 }
940 else if ( ! p->visible)
941 {
942 gtk_widget_show(p->box);
943 p->visible = TRUE;
944 }
945}
946
8f9e6256 947/* Set an image from a file with scaling to the panel icon size. */
fcb35553 948void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
8f9e6256 949{
950 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
951 if (pixbuf != NULL)
952 {
953 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
954 g_object_unref(pixbuf);
955 }
956}
957
c14620f2
MJ
958/* Set an image from a icon theme with scaling to the panel icon size. */
959gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
960{
1b532bce 961 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
c14620f2 962 {
1b532bce 963 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
c14620f2
MJ
964 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
965 g_object_unref(pixbuf);
966 return TRUE;
967 }
968 return FALSE;
969}
970
0defe4b9 971static void
22242ed4 972panel_start_gui(Panel *p)
a52c2257
HJYP
973{
974 Atom state[3];
975 XWMHints wmhints;
976 guint32 val;
6db11841 977
a52c2257
HJYP
978 ENTER;
979
9dd114c4 980 p->curdesk = get_net_current_desktop();
981 p->desknum = get_net_number_of_desktops();
982 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
983
cb22c87c
HJYP
984 /* main toplevel window */
985 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
986 p->topgwin = (GtkWidget*)g_object_new(PANEL_TOPLEVEL_TYPE, NULL);
987 gtk_widget_set_name(p->topgwin, "PanelToplevel");
176fb687 988 p->display = gdk_display_get_default();
a52c2257
HJYP
989 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
990 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
991 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
992 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
993 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
994 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 995
e2957bd2
HJYP
996 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
997
a52c2257
HJYP
998 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
999 G_CALLBACK(panel_delete_event), p);
1000 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
1001 G_CALLBACK(panel_destroy_event), p);
1002 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
1003 (GCallback) panel_size_req, p);
1004 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
1005 (GCallback) panel_size_alloc, p);
1006 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
1007 (GCallback) panel_configure_event, p);
3e7b8eb7
HJYP
1008
1009 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
fddae119 1010 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
9dd114c4 1011 (GCallback) panel_button_press_event_with_panel, p);
f2d54481 1012
a52c2257
HJYP
1013 g_signal_connect (G_OBJECT (p->topgwin), "realize",
1014 (GCallback) panel_realize, p);
f2d54481 1015
84fc1d55
HJYP
1016 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
1017 (GCallback)panel_style_set, p);
a52c2257
HJYP
1018 gtk_widget_realize(p->topgwin);
1019 //gdk_window_set_decorations(p->topgwin->window, 0);
2de71c90 1020
4542c20d 1021 // main layout manager as a single child of panel
a97d06a6 1022 p->box = p->my_box_new(FALSE, 0);
a52c2257 1023 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
4542c20d
HJYP
1024// gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
1025 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
a52c2257 1026 gtk_widget_show(p->box);
a97d06a6
HJYP
1027 if (p->round_corners)
1028 make_round_corners(p);
6db11841 1029
a52c2257
HJYP
1030 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
1031 DBG("topxwin = %x\n", p->topxwin);
1032
1033 /* the settings that should be done before window is mapped */
1034 wmhints.flags = InputHint;
1035 wmhints.input = 0;
bee4c26e 1036 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
24053345 1037#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257
HJYP
1038 val = WIN_HINTS_SKIP_FOCUS;
1039 XChangeProperty(GDK_DISPLAY(), p->topxwin,
1040 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
1041 PropModeReplace, (unsigned char *) &val, 1);
1042
bee4c26e 1043 panel_set_dock_type(p);
a52c2257
HJYP
1044
1045 /* window mapping point */
1046 gtk_widget_show_all(p->topgwin);
1047
1048 /* the settings that should be done after window is mapped */
176fb687 1049 panel_establish_autohide(p);
a52c2257
HJYP
1050
1051 /* send it to running wm */
1052 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
1053 /* and assign it ourself just for case when wm is not running */
1054 val = 0xFFFFFFFF;
1055 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1056 PropModeReplace, (unsigned char *) &val, 1);
1057
1058 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1059 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1060 state[2] = a_NET_WM_STATE_STICKY;
1061 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
1062 32, PropModeReplace, (unsigned char *) state, 3);
1063
a52c2257
HJYP
1064 calculate_position(p);
1065 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 1066 panel_set_wm_strut(p);
77886b88 1067
a52c2257
HJYP
1068 RET();
1069}
1070
2918994e 1071/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1072void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 1073{
8ed3b3d4 1074 if ((p->height_label != NULL) && (p->width_label != NULL)
1075 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
9dd114c4 1076 {
1077 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1078 {
1079 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1080 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 1081 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1082 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 1083 }
1084 else
1085 {
1086 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1087 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 1088 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1089 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 1090 }
1091 }
1092}
1093
2918994e 1094/* Draw text into a label, with the user preference color and optionally bold. */
3fd2186c
HG
1095void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold,
1096 float custom_size_factor, gboolean custom_color)
2918994e 1097{
2918994e 1098 if (text == NULL)
1099 {
1100 /* Null string. */
1101 gtk_label_set_text(GTK_LABEL(label), NULL);
1102 }
1103
f669ce5e 1104 else
2918994e 1105 {
f669ce5e 1106 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1107 int font_desc;
1869ef90
LK
1108 if (p->usefontsize)
1109 font_desc = p->fontsize;
1110 else
1111 {
1112 if (p->icon_size < 20)
1113 font_desc = 9;
1114 else if (p->icon_size >= 20 && p->icon_size < 36)
1115 font_desc = 10;
1116 else
1117 font_desc = 12;
1118 }
3fd2186c 1119 font_desc *= custom_size_factor;
2918994e 1120
1121 /* Check the string for characters that need to be escaped.
1122 * If any are found, create the properly escaped string and use it instead. */
f669ce5e 1123 char * valid_markup = text;
1124 char * escaped_text = NULL;
2918994e 1125 char * q;
1126 for (q = text; *q != '\0'; q += 1)
1127 {
1128 if ((*q == '<') || (*q == '>') || (*q == '&'))
1129 {
1130 escaped_text = g_markup_escape_text(text, -1);
1131 valid_markup = escaped_text;
1132 break;
1133 }
1134 }
1135
6835a23e 1136 if ((custom_color) && (p->usefontcolor))
2918994e 1137 {
1138 /* Color, optionally bold. */
cd93c810 1139 gchar * text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
f669ce5e 1140 font_desc,
2918994e 1141 gcolor2rgb24(&p->gfontcolor),
1142 ((bold) ? "<b>" : ""),
1143 valid_markup,
1144 ((bold) ? "</b>" : ""));
cd93c810
MJ
1145 gtk_label_set_markup(GTK_LABEL(label), text);
1146 g_free(text);
2918994e 1147 }
f669ce5e 1148 else
2918994e 1149 {
f669ce5e 1150 /* No color, optionally bold. */
cd93c810 1151 gchar * text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
f669ce5e 1152 font_desc,
1153 ((bold) ? "<b>" : ""),
1154 valid_markup,
1155 ((bold) ? "</b>" : ""));
cd93c810
MJ
1156 gtk_label_set_markup(GTK_LABEL(label), text);
1157 g_free(text);
2918994e 1158 }
1159 g_free(escaped_text);
1160 }
2918994e 1161}
1162
1163void panel_set_panel_configuration_changed(Panel *p)
a97d06a6
HJYP
1164{
1165 GList* l;
9dd114c4 1166
1167 int previous_orientation = p->orientation;
a97d06a6
HJYP
1168 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1169 ? ORIENT_HORIZ : ORIENT_VERT;
9dd114c4 1170
1171 if (previous_orientation != p->orientation)
1172 {
1173 panel_adjust_geometry_terminology(p);
1b0b2df6 1174 if (previous_orientation != ORIENT_NONE)
1175 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
9dd114c4 1176 if (p->height_control != NULL)
1177 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1178 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1179 {
1180 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1181 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1182 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1183 }
1184
9dd114c4 1185 }
1186
a97d06a6
HJYP
1187 if (p->orientation == ORIENT_HORIZ) {
1188 p->my_box_new = gtk_hbox_new;
1189 p->my_separator_new = gtk_vseparator_new;
1190 } else {
1191 p->my_box_new = gtk_vbox_new;
1192 p->my_separator_new = gtk_hseparator_new;
1193 }
1194
1195 /* recreate the main layout box */
964b8b7e 1196 if (p->box != NULL)
1197 {
1198#if GTK_CHECK_VERSION(2,16,0)
1199 GtkOrientation bo = (p->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1200 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), bo);
1201#else
1202 GtkBox * newbox = GTK_BOX(recreate_box(GTK_BOX(p->box), p->orientation));
1203 if (GTK_WIDGET(newbox) != p->box)
1204 {
5a343ad5 1205 p->box = GTK_WIDGET(newbox);
964b8b7e 1206 gtk_container_add(GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox));
a97d06a6 1207 }
964b8b7e 1208#endif
a97d06a6 1209 }
9dd114c4 1210
a97d06a6
HJYP
1211 /* NOTE: This loop won't be executed when panel started since
1212 plugins are not loaded at that time.
1213 This is used when the orientation of the panel is changed
1214 from the config dialog, and plugins should be re-layout.
1215 */
1216 for( l = p->plugins; l; l = l->next ) {
22242ed4 1217 Plugin* pl = (Plugin*)l->data;
2918994e 1218 if( pl->class->panel_configuration_changed ) {
1219 pl->class->panel_configuration_changed( pl );
a97d06a6
HJYP
1220 }
1221 }
1222}
1223
a52c2257 1224static int
22242ed4 1225panel_parse_global(Panel *p, char **fp)
a52c2257
HJYP
1226{
1227 line s;
1228 s.len = 256;
6db11841 1229
3e7b8eb7
HJYP
1230 if( G_LIKELY( fp ) )
1231 {
1232 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1233 if (s.type == LINE_VAR) {
1234 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1235 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1236 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1237 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1238 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1239 p->margin = atoi(s.t[1]);
1240 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1241 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1242 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1243 p->width = atoi(s.t[1]);
1244 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1245 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1246 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1247 p->height = atoi(s.t[1]);
1248 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1249 p->spacing = atoi(s.t[1]);
1250 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1251 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1252 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1253 p->setstrut = str2num(bool_pair, s.t[1], 0);
1254 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1255 p->round_corners = str2num(bool_pair, s.t[1], 0);
1256 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1257 p->transparent = str2num(bool_pair, s.t[1], 0);
1258 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1259 p->alpha = atoi(s.t[1]);
1260 if (p->alpha > 255)
1261 p->alpha = 255;
176fb687 1262 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1263 p->autohide = str2num(bool_pair, s.t[1], 0);
1264 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1265 p->height_when_hidden = atoi(s.t[1]);
3e7b8eb7
HJYP
1266 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1267 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1268 gdk_color_parse ("white", &p->gtintcolor);
1269 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1270 DBG("tintcolor=%x\n", p->tintcolor);
1271 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1272 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1273 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1274 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1275 gdk_color_parse ("black", &p->gfontcolor);
1276 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1277 DBG("fontcolor=%x\n", p->fontcolor);
1869ef90
LK
1278 } else if (!g_ascii_strcasecmp(s.t[0], "useFontSize")) {
1279 p->usefontsize = str2num(bool_pair, s.t[1], 0);
1280 } else if (!g_ascii_strcasecmp(s.t[0], "FontSize")) {
1281 p->fontsize = atoi(s.t[1]);
3e7b8eb7
HJYP
1282 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1283 p->background = str2num(bool_pair, s.t[1], 0);
1284 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1285 p->background_file = g_strdup( s.t[1] );
8f9e6256 1286 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1287 p->icon_size = atoi(s.t[1]);
82ca42af
HG
1288 } else if (!g_ascii_strcasecmp(s.t[0], "LogLevel")) {
1289 configured_log_level = atoi(s.t[1]);
1290 if (!log_level_set_on_commandline)
1291 log_level = configured_log_level;
3e7b8eb7
HJYP
1292 } else {
1293 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
3e7b8eb7
HJYP
1294 }
1295 } else if (s.type == LINE_BLOCK_END) {
1296 break;
a52c2257 1297 } else {
3e7b8eb7 1298 ERR( "lxpanel: illegal in this context %s\n", s.str);
a52c2257
HJYP
1299 RET(0);
1300 }
a52c2257
HJYP
1301 }
1302 }
4542c20d 1303
9dd114c4 1304 panel_normalize_configuration(p);
239cb032 1305
9dd114c4 1306 return 1;
a52c2257
HJYP
1307}
1308
1309static int
22242ed4 1310panel_parse_plugin(Panel *p, char **fp)
a52c2257
HJYP
1311{
1312 line s;
22242ed4 1313 Plugin *plug = NULL;
a52c2257 1314 gchar *type = NULL;
a52c2257 1315 int expand , padding, border;
db449f6e
HJYP
1316 char* pconfig = NULL;
1317
a52c2257
HJYP
1318 ENTER;
1319 s.len = 256;
a52c2257 1320 border = expand = padding = 0;
c69ac68e 1321 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
1322 if (s.type == LINE_NONE) {
1323 ERR( "lxpanel: bad line %s\n", s.str);
1324 goto error;
1325 }
1326 if (s.type == LINE_VAR) {
1327 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1328 type = g_strdup(s.t[1]);
1329 DBG("plug %s\n", type);
1330 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1331 expand = str2num(bool_pair, s.t[1], 0);
1332 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1333 padding = atoi(s.t[1]);
1334 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1335 border = atoi(s.t[1]);
1336 else {
1337 ERR( "lxpanel: unknown var %s\n", s.t[0]);
a52c2257
HJYP
1338 }
1339 } else if (s.type == LINE_BLOCK_START) {
1340 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 1341 pconfig = *fp;
a52c2257
HJYP
1342 int pno = 1;
1343 while (pno) {
1344 get_line_as_is(fp, &s);
1345 if (s.type == LINE_NONE) {
1346 ERR( "lxpanel: unexpected eof\n");
1347 goto error;
1348 } else if (s.type == LINE_BLOCK_START) {
1349 pno++;
1350 } else if (s.type == LINE_BLOCK_END) {
1351 pno--;
bee4c26e 1352 }
db449f6e 1353 }
a52c2257
HJYP
1354 } else {
1355 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1356 goto error;
1357 }
1358 } else {
1359 ERR( "lxpanel: illegal in this context %s\n", s.str);
1360 goto error;
1361 }
1362 }
db449f6e 1363
a52c2257
HJYP
1364 if (!type || !(plug = plugin_load(type))) {
1365 ERR( "lxpanel: can't load %s plugin\n", type);
1366 goto error;
1367 }
db449f6e 1368
a52c2257 1369 plug->panel = p;
2918994e 1370 if (plug->class->expand_available) plug->expand = expand;
a52c2257
HJYP
1371 plug->padding = padding;
1372 plug->border = border;
a52c2257 1373 DBG("starting\n");
db449f6e 1374 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
1375 ERR( "lxpanel: can't start plugin %s\n", type);
1376 goto error;
1377 }
1378 DBG("plug %s\n", type);
1379 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
1380
1381 g_free( type );
a52c2257 1382 RET(1);
db449f6e 1383
a52c2257 1384 error:
2918994e 1385 if (plug != NULL)
1386 plugin_unload(plug);
a52c2257 1387 g_free(type);
a52c2257 1388 RET(0);
a52c2257
HJYP
1389}
1390
3e7b8eb7 1391int panel_start( Panel *p, char **fp )
a52c2257
HJYP
1392{
1393 line s;
db449f6e 1394
a52c2257
HJYP
1395 /* parse global section */
1396 ENTER;
1397 s.len = 256;
8110399f 1398
c69ac68e 1399 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
1400 ERR( "lxpanel: config file must start from Global section\n");
1401 RET(0);
1402 }
1403 if (!panel_parse_global(p, fp))
1404 RET(0);
1405
9dd114c4 1406 panel_start_gui(p);
1407
c69ac68e 1408 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
1409 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1410 ERR( "lxpanel: expecting Plugin section\n");
1411 RET(0);
1412 }
9939506e 1413 panel_parse_plugin(p, fp);
a52c2257 1414 }
4542c20d
HJYP
1415
1416 /* update backgrond of panel and all plugins */
1417 panel_update_background( p );
9dd114c4 1418 return 1;
a52c2257
HJYP
1419}
1420
1421static void
1422delete_plugin(gpointer data, gpointer udata)
1423{
2918994e 1424 plugin_delete((Plugin *)data);
a52c2257
HJYP
1425}
1426
8110399f 1427void panel_destroy(Panel *p)
a52c2257
HJYP
1428{
1429 ENTER;
1430
2918994e 1431 if (p->pref_dialog != NULL)
1432 gtk_widget_destroy(p->pref_dialog);
1433 if (p->plugin_pref_dialog != NULL)
1434 {
1435 gtk_widget_destroy(p->plugin_pref_dialog);
1436 p->plugin_pref_dialog = NULL;
1437 }
1438
1439 if (p->bg != NULL)
1440 {
1441 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1442 g_object_unref(p->bg);
1443 }
1444
930af9fd
HJYP
1445 if( p->config_changed )
1446 panel_config_save( p );
1447
a52c2257
HJYP
1448 g_list_foreach(p->plugins, delete_plugin, NULL);
1449 g_list_free(p->plugins);
1450 p->plugins = NULL;
8c44345a 1451
5297da29 1452 if( p->system_menus ){
8c44345a 1453 do{
5297da29 1454 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
1455 }
1456
16fbda14 1457 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
e2957bd2 1458
4b93d81e
HJYP
1459 if( p->topgwin )
1460 gtk_widget_destroy(p->topgwin);
a52c2257 1461 g_free(p->workarea);
2de71c90 1462 g_free( p->background_file );
5297da29 1463 g_slist_free( p->system_menus );
a52c2257
HJYP
1464 gdk_flush();
1465 XFlush(GDK_DISPLAY());
1466 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1467
1468 g_free( p->name );
2918994e 1469 g_free(p);
a52c2257
HJYP
1470 RET();
1471}
1472
8110399f
HJYP
1473Panel* panel_new( const char* config_file, const char* config_name )
1474{
1475 char *fp, *pfp; /* point to current position of profile data in memory */
cf701cb7 1476 Panel* panel = NULL;
16fbda14 1477
3e7b8eb7 1478 if( G_LIKELY(config_file) )
cf701cb7 1479 {
3e7b8eb7
HJYP
1480 g_file_get_contents( config_file, &fp, NULL, NULL );
1481 if( fp )
1482 {
9dd114c4 1483 panel = panel_allocate();
8ed3b3d4 1484 panel->orientation = ORIENT_NONE;
3e7b8eb7
HJYP
1485 panel->name = g_strdup( config_name );
1486 pfp = fp;
1487
1488 if (! panel_start( panel, &pfp )) {
1489 ERR( "lxpanel: can't start panel\n");
1490 panel_destroy( panel );
1491 panel = NULL;
1492 }
cf701cb7 1493
3e7b8eb7 1494 g_free( fp );
cf701cb7 1495 }
cf701cb7
HJYP
1496 }
1497 return panel;
8110399f 1498}
a52c2257 1499
0defe4b9 1500static void
a52c2257
HJYP
1501usage()
1502{
e7cb732b
HJYP
1503 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1504 g_print(_("Command line options:\n"));
1505 g_print(_(" --help -- print this help and exit\n"));
1506 g_print(_(" --version -- print version and exit\n"));
1507 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1508// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1509 g_print(_(" --profile name -- use specified profile\n"));
1510 g_print("\n");
1511 g_print(_(" -h -- same as --help\n"));
1512 g_print(_(" -p -- same as --profile\n"));
1513 g_print(_(" -v -- same as --version\n"));
2918994e 1514 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1515 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1516}
1517
2918994e 1518int panel_handle_x_error(Display * d, XErrorEvent * ev)
a52c2257
HJYP
1519{
1520 char buf[256];
1521
a52c2257
HJYP
1522 if (log_level >= LOG_WARN) {
1523 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1524 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1525 }
2918994e 1526 return 0; /* Ignored */
1527}
1528
1529int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1530{
1531 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1532 panel_handle_x_error(d, ev);
1533 return 0; /* Ignored */
a52c2257
HJYP
1534}
1535
e68b47dc
JH
1536/* Lightweight lock related functions - X clipboard hacks */
1537
1538#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1539
1540/*
1541 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1542 */
1543static void
1544clipboard_get_func(
1545 GtkClipboard *clipboard G_GNUC_UNUSED,
1546 GtkSelectionData *selection_data G_GNUC_UNUSED,
1547 guint info G_GNUC_UNUSED,
1548 gpointer user_data_or_owner G_GNUC_UNUSED)
1549{
1550}
1551
1552/*
1553 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1554 */
1555static void clipboard_clear_func(
1556 GtkClipboard *clipboard G_GNUC_UNUSED,
1557 gpointer user_data_or_owner G_GNUC_UNUSED)
1558{
1559}
1560
1561/*
1562 * Lightweight version for checking single instance.
1563 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1564 *
1565 * Returns TRUE if successfully retrieved and FALSE otherwise.
1566 */
16fb8c2e 1567static gboolean check_main_lock()
e68b47dc
JH
1568{
1569 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1570 gboolean retval = FALSE;
1571 GtkClipboard *clipboard;
1572 Atom atom;
1573
1574 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1575
1576 XGrabServer(GDK_DISPLAY());
1577
1578 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1579 goto out;
1580
1581 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1582
1583 if (gtk_clipboard_set_with_data(clipboard, targets,
1584 G_N_ELEMENTS (targets),
1585 clipboard_get_func,
1586 clipboard_clear_func, NULL))
1587 retval = TRUE;
1588
1589out:
1590 XUngrabServer (GDK_DISPLAY ());
1591 gdk_flush ();
1592
1593 return retval;
1594}
1595#undef CLIPBOARD_NAME
1596
8110399f
HJYP
1597static gboolean start_all_panels( )
1598{
cf701cb7 1599 gboolean is_global;
4b93d81e 1600 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
cf701cb7
HJYP
1601 {
1602 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1603 GDir* dir = g_dir_open( panel_dir, 0, NULL );
e3b89f43 1604 const gchar* name;
cf701cb7
HJYP
1605
1606 if( ! dir )
1607 {
1608 g_free( panel_dir );
1609 continue;
1610 }
1611
547ece89 1612 while((name = g_dir_read_name(dir)) != NULL)
cf701cb7
HJYP
1613 {
1614 char* panel_config = g_build_filename( panel_dir, name, NULL );
2918994e 1615 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1616 {
1617 Panel* panel = panel_new( panel_config, name );
1618 if( panel )
1619 all_panels = g_slist_prepend( all_panels, panel );
1620 }
cf701cb7
HJYP
1621 g_free( panel_config );
1622 }
1623 g_dir_close( dir );
1624 g_free( panel_dir );
1625 }
1626 return all_panels != NULL;
8110399f
HJYP
1627}
1628
cf701cb7
HJYP
1629void load_global_config();
1630void free_global_config();
1631
8110399f 1632int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1633{
1634 int i;
9df6826f 1635 const char* desktop_name;
f277dbb7 1636
a52c2257 1637 setlocale(LC_CTYPE, "");
f277dbb7 1638
1d434622
HJYP
1639 g_thread_init(NULL);
1640 gdk_threads_init();
3e71f8af 1641 gdk_threads_enter();
1d434622 1642
a52c2257
HJYP
1643 gtk_init(&argc, &argv);
1644
1645#ifdef ENABLE_NLS
1646 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1647 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1648 textdomain ( GETTEXT_PACKAGE );
1649#endif
1650
1651 XSetLocaleModifiers("");
2918994e 1652 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1653
a52c2257 1654 resolve_atoms();
8110399f 1655
9df6826f
HJYP
1656 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1657 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
95095259 1658
a52c2257
HJYP
1659 for (i = 1; i < argc; i++) {
1660 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1661 usage();
1662 exit(0);
1663 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1664 printf("lxpanel %s\n", version);
1665 exit(0);
1666 } else if (!strcmp(argv[i], "--log")) {
1667 i++;
1668 if (i == argc) {
1669 ERR( "lxpanel: missing log level\n");
1670 usage();
1671 exit(1);
1672 } else {
1673 log_level = atoi(argv[i]);
82ca42af 1674 log_level_set_on_commandline = true;
a52c2257
HJYP
1675 }
1676 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1677 config = 1;
1678 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1679 i++;
1680 if (i == argc) {
1681 ERR( "lxpanel: missing profile name\n");
1682 usage();
1683 exit(1);
1684 } else {
1685 cprofile = g_strdup(argv[i]);
1686 }
1687 } else {
1688 printf("lxpanel: unknown option - %s\n", argv[i]);
1689 usage();
1690 exit(1);
1691 }
1692 }
f277dbb7 1693
8110399f 1694 /* Check for duplicated lxpanel instances */
e68b47dc 1695 if (!check_main_lock() && !config) {
e7a42ecf 1696 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1697 exit(1);
1698 }
1699
730e3f7b
JL
1700 /* Add our own icons to the search path of icon theme */
1701 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/lxpanel/images" );
1702
cf701cb7 1703 fbev = fb_ev_new();
e2957bd2 1704 win_grp = gtk_window_group_new();
22242ed4 1705
f7cb330e
HJYP
1706restart:
1707 is_restarting = FALSE;
1708
cf701cb7
HJYP
1709 load_global_config();
1710
1d9dc649
HJYP
1711 /* NOTE: StructureNotifyMask is required by XRandR
1712 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1713 */
1714 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
cf701cb7 1715 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1716
cf701cb7
HJYP
1717 if( G_UNLIKELY( ! start_all_panels() ) )
1718 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1719/*
1720 * FIXME: configure??
6a6ad54e
HJYP
1721 if (config)
1722 configure();
8110399f 1723*/
6a6ad54e 1724 gtk_main();
8110399f 1725
cf701cb7
HJYP
1726 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1727 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1728
8110399f
HJYP
1729 /* destroy all panels */
1730 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1731 g_slist_free( all_panels );
1732 all_panels = NULL;
6a6ad54e 1733 g_free( cfgfile );
5541b8d2 1734
cf701cb7
HJYP
1735 free_global_config();
1736
f7cb330e
HJYP
1737 if( is_restarting )
1738 goto restart;
1739
3e71f8af
HG
1740 gdk_threads_leave();
1741
e2957bd2 1742 g_object_unref(win_grp);
22242ed4
HJYP
1743 g_object_unref(fbev);
1744
5541b8d2 1745 return 0;
a52c2257 1746}