Correct the size request of menu items when icon is not available.
[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
HJYP
19#ifdef HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <stdlib.h>
24#include <stdio.h>
25#include <sys/types.h>
26#include <sys/stat.h>
27#include <unistd.h>
28#include <errno.h>
29#include <locale.h>
30#include <string.h>
e7cb732b 31#include <glib/gi18n.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"
38#include "gtkbgbox.h"
39
77886b88
HJYP
40#include "lxpanelctl.h"
41
a52c2257
HJYP
42static gchar *cfgfile = NULL;
43static gchar version[] = VERSION;
44gchar *cprofile = "default";
45
e68b47dc 46static int config = 0;
a52c2257
HJYP
47FbEv *fbev;
48
49//#define DEBUG
50#include "dbg.h"
51
52int log_level;
a52c2257
HJYP
53panel *p;
54
a52c2257
HJYP
55/****************************************************
56 * panel's handlers for WM events *
57 ****************************************************/
58/*
59static void
60panel_del_wm_strut(panel *p)
61{
62 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
63 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
64}
65*/
66
67
bee4c26e 68void panel_set_wm_strut(panel *p)
a52c2257
HJYP
69{
70 gulong data[12] = { 0 };
71 int i = 4;
72
73 ENTER;
74 if (!GTK_WIDGET_MAPPED (p->topgwin))
75 return;
bee4c26e
HJYP
76 if ( ! p->setstrut )
77 {
78 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
79 /* old spec, for wms that do not support STRUT_PARTIAL */
80 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
81 return;
82 }
83
a52c2257
HJYP
84 switch (p->edge) {
85 case EDGE_LEFT:
86 i = 0;
87 data[i] = p->aw;
88 data[4 + i*2] = p->ay;
89 data[5 + i*2] = p->ay + p->ah;
90 break;
91 case EDGE_RIGHT:
92 i = 1;
93 data[i] = p->aw;
94 data[4 + i*2] = p->ay;
95 data[5 + i*2] = p->ay + p->ah;
96 break;
97 case EDGE_TOP:
98 i = 2;
99 data[i] = p->ah;
100 data[4 + i*2] = p->ax;
101 data[5 + i*2] = p->ax + p->aw;
102 break;
103 case EDGE_BOTTOM:
104 i = 3;
105 data[i] = p->ah;
106 data[4 + i*2] = p->ax;
107 data[5 + i*2] = p->ax + p->aw;
108 break;
109 default:
110 ERR("wrong edge %d. strut won't be set\n", p->edge);
111 RET();
bee4c26e 112 }
a52c2257 113 DBG("type %d. width %d. from %d to %d\n", i, data[i], data[4 + i*2], data[5 + i*2]);
bee4c26e 114
a52c2257 115 /* if wm supports STRUT_PARTIAL it will ignore STRUT */
bee4c26e 116 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
a52c2257
HJYP
117 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 12);
118 /* old spec, for wms that do not support STRUT_PARTIAL */
bee4c26e
HJYP
119 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
120 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 4);
a52c2257
HJYP
121
122 RET();
123}
124
125static void
126print_wmdata(panel *p)
127{
128 int i;
129
130 ENTER;
131 RET();
132 DBG("desktop %d/%d\n", p->curdesk, p->desknum);
133 DBG("workarea\n");
134 for (i = 0; i < p->wa_len/4; i++)
135 DBG("(%d, %d) x (%d, %d)\n",
136 p->workarea[4*i + 0],
137 p->workarea[4*i + 1],
138 p->workarea[4*i + 2],
139 p->workarea[4*i + 3]);
140 RET();
141}
142
143
e996608e 144/* defined in plugins/menu.c */
8c44345a 145gboolean show_system_menu( gpointer system_menu );
e996608e
HJYP
146
147/* built-in commands, defined in configurator.c */
77886b88
HJYP
148void configure(void);
149void restart(void);
150void gtk_run(void);
151
8c44345a 152static void process_client_msg ( panel *p, XClientMessageEvent* ev )
77886b88 153{
8c44345a 154 int cmd = ev->data.b[0];
77886b88
HJYP
155 switch( cmd )
156 {
157 case LXPANEL_CMD_SYS_MENU:
5297da29 158 if( p->system_menus )
8c44345a 159 {
5297da29 160 /* show_system_menu( p->system_menus->data ); */
8c44345a
HJYP
161 /* FIXME: I've no idea why this doesn't work without timeout
162 under some WMs, like icewm. */
5297da29
HJYP
163 g_timeout_add( 200, (GSourceFunc)show_system_menu,
164 p->system_menus->data );
8c44345a 165 }
77886b88
HJYP
166 break;
167 case LXPANEL_CMD_RUN:
168 gtk_run();
169 break;
170 case LXPANEL_CMD_CONFIG:
171 configure();
172 break;
173 case LXPANEL_CMD_RESTART:
174 restart();
175 break;
176 case LXPANEL_CMD_EXIT:
177 gtk_main_quit();
178 break;
179 }
180}
181
a52c2257
HJYP
182static GdkFilterReturn
183panel_event_filter(GdkXEvent *xevent, GdkEvent *event, panel *p)
184{
185 Atom at;
186 Window win;
187 XEvent *ev = (XEvent *) xevent;
188
189 ENTER;
190 DBG("win = 0x%x\n", ev->xproperty.window);
77886b88
HJYP
191 if (ev->type != PropertyNotify ) {
192 /* private client message from lxpanelctl */
193 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
194 {
8c44345a 195 process_client_msg( p, (XClientMessageEvent*)ev );
77886b88 196 }
a52c2257 197 RET(GDK_FILTER_CONTINUE);
77886b88
HJYP
198 }
199
a52c2257
HJYP
200 at = ev->xproperty.atom;
201 win = ev->xproperty.window;
202 DBG("win=%x at=%d\n", win, at);
203 if (win == GDK_ROOT_WINDOW()) {
204 if (at == a_NET_CLIENT_LIST) {
205 DBG("A_NET_CLIENT_LIST\n");
206 fb_ev_trigger(fbev, EV_CLIENT_LIST);
207 } else if (at == a_NET_CURRENT_DESKTOP) {
208 DBG("A_NET_CURRENT_DESKTOP\n");
209 p->curdesk = get_net_current_desktop();
210 fb_ev_trigger(fbev, EV_CURRENT_DESKTOP);
211 } else if (at == a_NET_NUMBER_OF_DESKTOPS) {
212 DBG("A_NET_NUMBER_OF_DESKTOPS\n");
213 p->desknum = get_net_number_of_desktops();
214 fb_ev_trigger(fbev, EV_NUMBER_OF_DESKTOPS);
215 } else if (at == a_NET_DESKTOP_NAMES) {
216 DBG("A_NET_DESKTOP_NAMES\n");
217 fb_ev_trigger(fbev, EV_DESKTOP_NAMES);
218 } else if (at == a_NET_ACTIVE_WINDOW) {
219 DBG("A_NET_ACTIVE_WINDOW\n");
220 fb_ev_trigger(fbev, EV_ACTIVE_WINDOW);
221 }else if (at == a_NET_CLIENT_LIST_STACKING) {
222 DBG("A_NET_CLIENT_LIST_STACKING\n");
223 fb_ev_trigger(fbev, EV_CLIENT_LIST_STACKING);
224 } else if (at == a_XROOTPMAP_ID) {
225 DBG("a_XROOTPMAP_ID\n");
226 if (p->transparent) {
227 fb_bg_notify_changed_bg(p->bg);
228 }
229 } else if (at == a_NET_WORKAREA) {
230 DBG("A_NET_WORKAREA\n");
0dcb6bf5 231 g_free( p->workarea );
a52c2257
HJYP
232 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
233 print_wmdata(p);
234 } else
235 RET(GDK_FILTER_CONTINUE);
236 RET(GDK_FILTER_REMOVE);
237 }
238 DBG("non root %x\n", win);
239 RET(GDK_FILTER_CONTINUE);
240}
241
242/****************************************************
243 * panel's handlers for GTK events *
244 ****************************************************/
245
bee4c26e 246
a52c2257
HJYP
247static gint
248panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
249{
250 ENTER;
251 RET(FALSE);
252}
253
254static gint
255panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
256{
257 //panel *p = (panel *) data;
258
bee4c26e 259 ENTER;
a52c2257
HJYP
260 //if (!p->self_destroy)
261 gtk_main_quit();
262 RET(FALSE);
263}
264
265
266static void
267panel_realize(GtkWidget *widget, panel *p)
268{
269 ENTER;
270 RET();
271
272}
273static gint
274panel_size_req(GtkWidget *widget, GtkRequisition *req, panel *p)
275{
276 ENTER;
277 DBG("IN req=(%d, %d)\n", req->width, req->height);
278 if (p->widthtype == WIDTH_REQUEST)
279 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
280 if (p->heighttype == HEIGHT_REQUEST)
281 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
282 calculate_position(p);
283 req->width = p->aw;
284 req->height = p->ah;
285 DBG("OUT req=(%d, %d)\n", req->width, req->height);
286 RET( TRUE );
287}
288
289static gint
290panel_size_alloc(GtkWidget *widget, GtkAllocation *a, panel *p)
291{
292 ENTER;
293 DBG("installed alloc: size (%d, %d). pos (%d, %d)\n", aa->width, aa->height, aa->x, aa->y);
294 DBG("suggested alloc: size (%d, %d). pos (%d, %d)\n", a->width, a->height, a->x, a->y);
295 DBG("prev pref alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay);
296 if (p->widthtype == WIDTH_REQUEST)
297 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
298 if (p->heighttype == HEIGHT_REQUEST)
299 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
300 calculate_position(p);
301 DBG("curr pref alloc: size (%d, %d). pos (%d, %d)\n", p->aw, p->ah, p->ax, p->ay);
302 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
303 DBG("actual coords eq to preffered. just returning\n");
304 RET(TRUE);
305 }
306
307 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
308 DBG("moving to %d %d\n", p->ax, p->ay);
bee4c26e 309 panel_set_wm_strut(p);
a52c2257
HJYP
310 RET(TRUE);
311}
312
313
314static gboolean
315panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, panel *p)
316{
317 ENTER;
318 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
319 RET(TRUE);
320 p->cw = e->width;
321 p->ch = e->height;
322 p->cx = e->x;
323 p->cy = e->y;
324 DBG("here\n");
325 if (p->transparent)
326 fb_bg_notify_changed_bg(p->bg);
327 DBG("here\n");
328 DBG("geom: size (%d, %d). pos (%d, %d)\n", e->width, e->height, e->x, e->y);
329 RET(FALSE);
bee4c26e 330
a52c2257
HJYP
331}
332
333
334/****************************************************
335 * panel creation *
336 ****************************************************/
337static void
338make_round_corners(panel *p)
339{
a97d06a6
HJYP
340 /* FIXME: This should be re-written with shape extension of X11 */
341#if 0
a52c2257
HJYP
342 GtkWidget *b1, *b2, *img;
343 GtkWidget *(*box_new) (gboolean, gint);
344 void (*box_pack)(GtkBox *, GtkWidget *, gboolean, gboolean, guint);
345 gchar *s1, *s2;
346#define IMGPREFIX PACKAGE_DATA_DIR "/lxpanel/images/"
bee4c26e 347
a52c2257
HJYP
348 ENTER;
349 if (p->edge == EDGE_TOP) {
350 s1 = IMGPREFIX "top-left.xpm";
351 s2 = IMGPREFIX "top-right.xpm";
352 } else if (p->edge == EDGE_BOTTOM) {
353 s1 = IMGPREFIX "bottom-left.xpm";
354 s2 = IMGPREFIX "bottom-right.xpm";
355 } else if (p->edge == EDGE_LEFT) {
356 s1 = IMGPREFIX "top-left.xpm";
357 s2 = IMGPREFIX "bottom-left.xpm";
358 } else if (p->edge == EDGE_RIGHT) {
359 s1 = IMGPREFIX "top-right.xpm";
360 s2 = IMGPREFIX "bottom-right.xpm";
361 } else
362 RET();
bee4c26e 363
a52c2257
HJYP
364 box_new = (p->orientation == ORIENT_HORIZ) ? gtk_vbox_new : gtk_hbox_new;
365 b1 = box_new(0, FALSE);
366 gtk_widget_show(b1);
367 b2 = box_new(0, FALSE);
368 gtk_widget_show(b2);
369
370 box_pack = (p->edge == EDGE_TOP || p->edge == EDGE_LEFT) ?
371 gtk_box_pack_start : gtk_box_pack_end;
bee4c26e 372
a52c2257
HJYP
373 img = gtk_image_new_from_file(s1);
374 gtk_widget_show(img);
375 box_pack(GTK_BOX(b1), img, FALSE, FALSE, 0);
376 img = gtk_image_new_from_file(s2);
377 gtk_widget_show(img);
378 box_pack(GTK_BOX(b2), img, FALSE, FALSE, 0);
379 gtk_box_pack_start(GTK_BOX(p->lbox), b1, FALSE, FALSE, 0);
380 gtk_box_pack_end(GTK_BOX(p->lbox), b2, FALSE, FALSE, 0);
bee4c26e 381 RET();
a97d06a6 382#endif
bee4c26e
HJYP
383}
384
385void panel_set_dock_type(panel *p)
386{
387 if (p->setdocktype) {
388 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
389 XChangeProperty(GDK_DISPLAY(), p->topxwin,
390 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
391 PropModeReplace, (unsigned char *) &state, 1);
392 }
393 else {
394 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
395 }
a52c2257
HJYP
396}
397
0defe4b9 398static void
a52c2257
HJYP
399panel_start_gui(panel *p)
400{
401 Atom state[3];
402 XWMHints wmhints;
403 guint32 val;
6db11841 404
a52c2257
HJYP
405 ENTER;
406
407 // main toplevel window
408 p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
409 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
410 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
411 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
412 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
413 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
414 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 415
a52c2257
HJYP
416 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
417 G_CALLBACK(panel_delete_event), p);
418 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
419 G_CALLBACK(panel_destroy_event), p);
420 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
421 (GCallback) panel_size_req, p);
422 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
423 (GCallback) panel_size_alloc, p);
424 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
425 (GCallback) panel_configure_event, p);
426 g_signal_connect (G_OBJECT (p->topgwin), "realize",
427 (GCallback) panel_realize, p);
6db11841 428
a52c2257
HJYP
429 gtk_widget_realize(p->topgwin);
430 //gdk_window_set_decorations(p->topgwin->window, 0);
431 gtk_widget_set_app_paintable(p->topgwin, TRUE);
6db11841 432
a52c2257
HJYP
433 // background box all over toplevel
434 p->bbox = gtk_bgbox_new();
435 gtk_container_add(GTK_CONTAINER(p->topgwin), p->bbox);
436 gtk_widget_show(p->bbox);
437 gtk_container_set_border_width(GTK_CONTAINER(p->bbox), 0);
438 if (p->transparent) {
439 p->bg = fb_bg_get_for_display();
bee4c26e 440 gtk_bgbox_set_background(p->bbox, BG_ROOT, p->tintcolor, p->alpha);
a52c2257
HJYP
441 }
442
443 // main layout manager as a single child of background widget box
a97d06a6 444 p->box = p->my_box_new(FALSE, 0);
a52c2257 445 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
a97d06a6 446 gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
a52c2257 447 gtk_widget_show(p->box);
a97d06a6
HJYP
448 if (p->round_corners)
449 make_round_corners(p);
6db11841 450
a52c2257
HJYP
451 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
452 DBG("topxwin = %x\n", p->topxwin);
453
454 /* the settings that should be done before window is mapped */
455 wmhints.flags = InputHint;
456 wmhints.input = 0;
bee4c26e 457 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
a52c2257
HJYP
458#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
459 val = WIN_HINTS_SKIP_FOCUS;
460 XChangeProperty(GDK_DISPLAY(), p->topxwin,
461 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
462 PropModeReplace, (unsigned char *) &val, 1);
463
bee4c26e 464 panel_set_dock_type(p);
a52c2257
HJYP
465
466 /* window mapping point */
467 gtk_widget_show_all(p->topgwin);
468
469 /* the settings that should be done after window is mapped */
470
471 /* send it to running wm */
472 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
473 /* and assign it ourself just for case when wm is not running */
474 val = 0xFFFFFFFF;
475 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
476 PropModeReplace, (unsigned char *) &val, 1);
477
478 state[0] = a_NET_WM_STATE_SKIP_PAGER;
479 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
480 state[2] = a_NET_WM_STATE_STICKY;
481 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
482 32, PropModeReplace, (unsigned char *) state, 3);
483
77886b88 484 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), SubstructureNotifyMask|PropertyChangeMask);
a52c2257
HJYP
485 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, p);
486
487 calculate_position(p);
488 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 489 panel_set_wm_strut(p);
77886b88 490
a52c2257
HJYP
491 RET();
492}
493
a97d06a6
HJYP
494void panel_set_orientation(panel *p)
495{
496 GList* l;
497 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
498 ? ORIENT_HORIZ : ORIENT_VERT;
499 if (p->orientation == ORIENT_HORIZ) {
500 p->my_box_new = gtk_hbox_new;
501 p->my_separator_new = gtk_vseparator_new;
502 } else {
503 p->my_box_new = gtk_vbox_new;
504 p->my_separator_new = gtk_hseparator_new;
505 }
506
507 /* recreate the main layout box */
508 if( p->box ) {
5a343ad5
JH
509 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
510 if( GTK_WIDGET(newbox) != p->box ) {
511 p->box = GTK_WIDGET(newbox);
512 gtk_container_add( GTK_CONTAINER(p->bbox), GTK_WIDGET(newbox) );
a97d06a6
HJYP
513 }
514 }
515 /* NOTE: This loop won't be executed when panel started since
516 plugins are not loaded at that time.
517 This is used when the orientation of the panel is changed
518 from the config dialog, and plugins should be re-layout.
519 */
520 for( l = p->plugins; l; l = l->next ) {
521 plugin* pl = (plugin*)l->data;
522 if( pl->class->orientation ) {
523 pl->class->orientation( pl );
524 }
525 }
526}
527
a52c2257 528static int
db449f6e 529panel_parse_global(panel *p, char **fp)
a52c2257
HJYP
530{
531 line s;
532 s.len = 256;
6db11841 533
a52c2257 534 ENTER;
c69ac68e 535 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
536 if (s.type == LINE_VAR) {
537 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
538 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
539 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
540 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
541 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
542 p->margin = atoi(s.t[1]);
543 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
544 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
545 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
546 p->width = atoi(s.t[1]);
547 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
548 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
549 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
550 p->height = atoi(s.t[1]);
551 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
552 p->spacing = atoi(s.t[1]);
553 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
554 p->setdocktype = str2num(bool_pair, s.t[1], 0);
555 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
556 p->setstrut = str2num(bool_pair, s.t[1], 0);
557 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
558 p->round_corners = str2num(bool_pair, s.t[1], 0);
559 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
560 p->transparent = str2num(bool_pair, s.t[1], 0);
561 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
562 p->alpha = atoi(s.t[1]);
563 if (p->alpha > 255)
564 p->alpha = 255;
565 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
566 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
567 gdk_color_parse ("white", &p->gtintcolor);
568 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
569 DBG("tintcolor=%x\n", p->tintcolor);
389975e0
HJYP
570 } else if( !g_ascii_strcasecmp(s.t[0], "LogoutCommand") ) {
571 p->logout_command = g_strdup( s.t[1] );
a52c2257
HJYP
572 } else {
573 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
574 RET(0);
575 }
576 } else if (s.type == LINE_BLOCK_END) {
577 break;
578 } else {
579 ERR( "lxpanel: illegal in this context %s\n", s.str);
580 RET(0);
581 }
582 }
a97d06a6 583 panel_set_orientation( p );
a52c2257
HJYP
584 if (p->width < 0)
585 p->width = 100;
586 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
587 p->width = 100;
588 p->heighttype = HEIGHT_PIXEL;
589 if (p->heighttype == HEIGHT_PIXEL) {
590 if (p->height < PANEL_HEIGHT_MIN)
591 p->height = PANEL_HEIGHT_MIN;
592 else if (p->height > PANEL_HEIGHT_MAX)
593 p->height = PANEL_HEIGHT_MAX;
594 }
595 p->curdesk = get_net_current_desktop();
596 p->desknum = get_net_number_of_desktops();
597 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
598 print_wmdata(p);
599 panel_start_gui(p);
600 RET(1);
601}
602
603static int
db449f6e 604panel_parse_plugin(panel *p, char **fp)
a52c2257
HJYP
605{
606 line s;
607 plugin *plug = NULL;
608 gchar *type = NULL;
a52c2257 609 int expand , padding, border;
db449f6e
HJYP
610 char* pconfig = NULL;
611
a52c2257
HJYP
612 ENTER;
613 s.len = 256;
a52c2257 614 border = expand = padding = 0;
c69ac68e 615 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
616 if (s.type == LINE_NONE) {
617 ERR( "lxpanel: bad line %s\n", s.str);
618 goto error;
619 }
620 if (s.type == LINE_VAR) {
621 if (!g_ascii_strcasecmp(s.t[0], "type")) {
622 type = g_strdup(s.t[1]);
623 DBG("plug %s\n", type);
624 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
625 expand = str2num(bool_pair, s.t[1], 0);
626 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
627 padding = atoi(s.t[1]);
628 else if (!g_ascii_strcasecmp(s.t[0], "border"))
629 border = atoi(s.t[1]);
630 else {
631 ERR( "lxpanel: unknown var %s\n", s.t[0]);
632 goto error;
633 }
634 } else if (s.type == LINE_BLOCK_START) {
635 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 636 pconfig = *fp;
a52c2257
HJYP
637 int pno = 1;
638 while (pno) {
639 get_line_as_is(fp, &s);
640 if (s.type == LINE_NONE) {
641 ERR( "lxpanel: unexpected eof\n");
642 goto error;
643 } else if (s.type == LINE_BLOCK_START) {
644 pno++;
645 } else if (s.type == LINE_BLOCK_END) {
646 pno--;
bee4c26e 647 }
db449f6e 648 }
a52c2257
HJYP
649 } else {
650 ERR( "lxpanel: unknown block %s\n", s.t[0]);
651 goto error;
652 }
653 } else {
654 ERR( "lxpanel: illegal in this context %s\n", s.str);
655 goto error;
656 }
657 }
db449f6e 658
a52c2257
HJYP
659 if (!type || !(plug = plugin_load(type))) {
660 ERR( "lxpanel: can't load %s plugin\n", type);
661 goto error;
662 }
db449f6e 663
a52c2257 664 plug->panel = p;
a52c2257
HJYP
665 plug->expand = expand;
666 plug->padding = padding;
667 plug->border = border;
a52c2257 668 DBG("starting\n");
db449f6e 669 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
670 ERR( "lxpanel: can't start plugin %s\n", type);
671 goto error;
672 }
673 DBG("plug %s\n", type);
674 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
675
676 g_free( type );
a52c2257 677 RET(1);
db449f6e 678
a52c2257 679 error:
a52c2257
HJYP
680 g_free(type);
681 if (plug)
682 plugin_put(plug);
683 RET(0);
a52c2257
HJYP
684}
685
686
0defe4b9 687static int
db449f6e 688panel_start( panel *p, char **fp )
a52c2257
HJYP
689{
690 line s;
db449f6e 691
a52c2257
HJYP
692 /* parse global section */
693 ENTER;
694 s.len = 256;
695 memset(p, 0, sizeof(panel));
696 p->allign = ALLIGN_CENTER;
697 p->edge = EDGE_BOTTOM;
698 p->widthtype = WIDTH_PERCENT;
699 p->width = 100;
700 p->heighttype = HEIGHT_PIXEL;
701 p->height = PANEL_HEIGHT_DEFAULT;
702 p->setdocktype = 1;
703 p->setstrut = 1;
704 p->round_corners = 0;
705 p->transparent = 0;
706 p->alpha = 127;
707 p->tintcolor = 0xFFFFFFFF;
708 p->spacing = 0;
709 fbev = fb_ev_new();
c69ac68e 710 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
711 ERR( "lxpanel: config file must start from Global section\n");
712 RET(0);
713 }
714 if (!panel_parse_global(p, fp))
715 RET(0);
716
c69ac68e 717 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
718 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
719 ERR( "lxpanel: expecting Plugin section\n");
720 RET(0);
721 }
bee4c26e 722 if (!panel_parse_plugin(p, fp))
a52c2257
HJYP
723 RET(0);
724 }
725 gtk_widget_show_all(p->topgwin);
726 print_wmdata(p);
727 RET(1);
728}
729
730static void
731delete_plugin(gpointer data, gpointer udata)
732{
733 ENTER;
734 plugin_stop((plugin *)data);
735 plugin_put((plugin *)data);
736 RET();
a52c2257
HJYP
737}
738
739void panel_stop(panel *p)
740{
741 ENTER;
742
743 g_list_foreach(p->plugins, delete_plugin, NULL);
744 g_list_free(p->plugins);
745 p->plugins = NULL;
8c44345a 746
5297da29 747 if( p->system_menus ){
8c44345a 748 do{
5297da29 749 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
750 }
751
a52c2257
HJYP
752 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
753 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, p);
754 gtk_widget_destroy(p->topgwin);
755 g_object_unref(fbev);
756 g_free(p->workarea);
389975e0 757 g_free( p->logout_command );
5297da29 758 g_slist_free( p->system_menus );
a52c2257
HJYP
759 gdk_flush();
760 XFlush(GDK_DISPLAY());
761 XSync(GDK_DISPLAY(), True);
762 RET();
763}
764
765
0defe4b9 766static void
a52c2257
HJYP
767usage()
768{
769 ENTER;
e7cb732b
HJYP
770 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
771 g_print(_("Command line options:\n"));
772 g_print(_(" --help -- print this help and exit\n"));
773 g_print(_(" --version -- print version and exit\n"));
774 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
775 g_print(_(" --configure -- launch configuration utility\n"));
776 g_print(_(" --profile name -- use specified profile\n"));
777 g_print("\n");
778 g_print(_(" -h -- same as --help\n"));
779 g_print(_(" -p -- same as --profile\n"));
780 g_print(_(" -v -- same as --version\n"));
781 g_print(_(" -C -- same as --configure\n"));
782 g_print(_("\nVisit http://lxpanel.sourceforge.net/ for detailed documentation,\n\n"));
a52c2257
HJYP
783}
784
e68b47dc 785static char*
db449f6e 786load_profile(gchar *profile)
a52c2257
HJYP
787{
788 gchar *fname;
db449f6e 789 char* ret;
a52c2257
HJYP
790
791 ENTER;
792 LOG(LOG_INFO, "loading %s profile\n", profile);
793 fname = g_strdup_printf("%s/.lxpanel/%s", getenv("HOME"), profile);
db449f6e
HJYP
794 g_file_get_contents( fname, &ret, NULL, NULL );
795 if (ret) {
a52c2257 796 cfgfile = fname;
db449f6e 797 RET(ret);
a52c2257
HJYP
798 }
799 //ERR("Can't load %s\n", fname);
800 g_free(fname);
db449f6e 801
a52c2257
HJYP
802 /* check private configuration directory */
803 fname = g_strdup_printf(PACKAGE_DATA_DIR "/lxpanel/%s", profile);
db449f6e
HJYP
804 g_file_get_contents( fname, &ret, NULL, NULL );
805 if (ret) {
a52c2257 806 cfgfile = fname;
db449f6e 807 RET(ret);
a52c2257
HJYP
808 }
809 //ERR("Can't load %s\n", fname);
810 g_free(fname);
811 LOG(LOG_ERR, "Can't open '%s' profile\n", profile);
812 RET(NULL);
813}
814
e68b47dc 815static void
a52c2257
HJYP
816handle_error(Display * d, XErrorEvent * ev)
817{
818 char buf[256];
819
820 ENTER;
821 if (log_level >= LOG_WARN) {
822 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
823 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
824 }
825 RET();
826}
827
e68b47dc
JH
828/* Lightweight lock related functions - X clipboard hacks */
829
830#define CLIPBOARD_NAME "LXPANEL_SELECTION"
831
832/*
833 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
834 */
835static void
836clipboard_get_func(
837 GtkClipboard *clipboard G_GNUC_UNUSED,
838 GtkSelectionData *selection_data G_GNUC_UNUSED,
839 guint info G_GNUC_UNUSED,
840 gpointer user_data_or_owner G_GNUC_UNUSED)
841{
842}
843
844/*
845 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
846 */
847static void clipboard_clear_func(
848 GtkClipboard *clipboard G_GNUC_UNUSED,
849 gpointer user_data_or_owner G_GNUC_UNUSED)
850{
851}
852
853/*
854 * Lightweight version for checking single instance.
855 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
856 *
857 * Returns TRUE if successfully retrieved and FALSE otherwise.
858 */
16fb8c2e 859static gboolean check_main_lock()
e68b47dc
JH
860{
861 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
862 gboolean retval = FALSE;
863 GtkClipboard *clipboard;
864 Atom atom;
865
866 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
867
868 XGrabServer(GDK_DISPLAY());
869
870 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
871 goto out;
872
873 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
874
875 if (gtk_clipboard_set_with_data(clipboard, targets,
876 G_N_ELEMENTS (targets),
877 clipboard_get_func,
878 clipboard_clear_func, NULL))
879 retval = TRUE;
880
881out:
882 XUngrabServer (GDK_DISPLAY ());
883 gdk_flush ();
884
885 return retval;
886}
887#undef CLIPBOARD_NAME
888
a52c2257
HJYP
889int
890main(int argc, char *argv[], char *env[])
891{
892 int i;
a52c2257 893 void configure();
db449f6e 894 char *fp, *pfp; /* point to current position of profile data in memory */
f277dbb7 895
a52c2257
HJYP
896 ENTER;
897 //printf("sizeof(gulong)=%d\n", sizeof(gulong));
898 setlocale(LC_CTYPE, "");
f277dbb7 899
a52c2257
HJYP
900 gtk_init(&argc, &argv);
901
902#ifdef ENABLE_NLS
903 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
904 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
905 textdomain ( GETTEXT_PACKAGE );
906#endif
907
908 XSetLocaleModifiers("");
909 XSetErrorHandler((XErrorHandler) handle_error);
910 resolve_atoms();
911 for (i = 1; i < argc; i++) {
912 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
913 usage();
914 exit(0);
915 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
916 printf("lxpanel %s\n", version);
917 exit(0);
918 } else if (!strcmp(argv[i], "--log")) {
919 i++;
920 if (i == argc) {
921 ERR( "lxpanel: missing log level\n");
922 usage();
923 exit(1);
924 } else {
925 log_level = atoi(argv[i]);
926 }
927 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
928 config = 1;
929 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
930 i++;
931 if (i == argc) {
932 ERR( "lxpanel: missing profile name\n");
933 usage();
934 exit(1);
935 } else {
936 cprofile = g_strdup(argv[i]);
937 }
938 } else {
939 printf("lxpanel: unknown option - %s\n", argv[i]);
940 usage();
941 exit(1);
942 }
943 }
f277dbb7 944
e68b47dc
JH
945 /* Check for duplicated panel instances */
946 if (!check_main_lock() && !config) {
947 printf("There is alreay an instance of LXPanel. Now to exit\n");
948 exit(1);
949 }
950
f277dbb7
HJYP
951 /* Add our own icons to the search path of icon theme */
952 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
953 PACKAGE_DATA_DIR "/lxpanel/images" );
954
6a6ad54e
HJYP
955 if (!(fp = pfp = load_profile(cprofile)))
956 exit(1);
957 p = g_new0(panel, 1);
958 g_return_val_if_fail (p != NULL, 1);
959 if (!panel_start(p, &pfp)) {
960 ERR( "lxpanel: can't start panel\n");
961 exit(1);
5541b8d2 962 }
6a6ad54e
HJYP
963 g_free( fp );
964 if (config)
965 configure();
966
967 gtk_main();
968 panel_stop(p);
969 g_free( cfgfile );
970 g_free(p);
5541b8d2
JH
971
972 return 0;
a52c2257
HJYP
973}
974