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