22 Sep 2008 Updates
[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
HJYP
24#include <stdlib.h>
25#include <stdio.h>
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
8110399f 39#include "glib-mem.h"
77886b88 40#include "lxpanelctl.h"
cf701cb7 41#include "dbg.h"
77886b88 42
a52c2257
HJYP
43static gchar *cfgfile = NULL;
44static gchar version[] = VERSION;
45gchar *cprofile = "default";
46
e68b47dc 47static int config = 0;
22242ed4 48FbEv *fbev = NULL;
a52c2257 49
a52c2257 50int log_level;
8110399f 51
cf701cb7 52GSList* all_panels = NULL; /* a single-linked list storing all panels */
a52c2257 53
f7cb330e
HJYP
54gboolean is_restarting = FALSE;
55
3e7b8eb7 56static int panel_start( Panel *p, char **fp );
4b93d81e
HJYP
57void panel_config_save(Panel* panel); /* defined in configurator.c */
58
a52c2257
HJYP
59/****************************************************
60 * panel's handlers for WM events *
61 ****************************************************/
62/*
63static void
22242ed4 64panel_del_wm_strut(Panel *p)
a52c2257
HJYP
65{
66 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
67 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
68}
69*/
70
71
22242ed4 72void panel_set_wm_strut(Panel *p)
a52c2257
HJYP
73{
74 gulong data[12] = { 0 };
75 int i = 4;
76
a52c2257
HJYP
77 if (!GTK_WIDGET_MAPPED (p->topgwin))
78 return;
bee4c26e
HJYP
79 if ( ! p->setstrut )
80 {
81 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
82 /* old spec, for wms that do not support STRUT_PARTIAL */
83 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
84 return;
85 }
86
a52c2257
HJYP
87 switch (p->edge) {
88 case EDGE_LEFT:
89 i = 0;
90 data[i] = p->aw;
91 data[4 + i*2] = p->ay;
92 data[5 + i*2] = p->ay + p->ah;
93 break;
94 case EDGE_RIGHT:
95 i = 1;
96 data[i] = p->aw;
97 data[4 + i*2] = p->ay;
98 data[5 + i*2] = p->ay + p->ah;
99 break;
100 case EDGE_TOP:
101 i = 2;
102 data[i] = p->ah;
103 data[4 + i*2] = p->ax;
104 data[5 + i*2] = p->ax + p->aw;
105 break;
106 case EDGE_BOTTOM:
107 i = 3;
108 data[i] = p->ah;
109 data[4 + i*2] = p->ax;
110 data[5 + i*2] = p->ax + p->aw;
111 break;
112 default:
113 ERR("wrong edge %d. strut won't be set\n", p->edge);
114 RET();
bee4c26e 115 }
a52c2257 116 DBG("type %d. width %d. from %d to %d\n", i, data[i], data[4 + i*2], data[5 + i*2]);
bee4c26e 117
a52c2257 118 /* if wm supports STRUT_PARTIAL it will ignore STRUT */
bee4c26e 119 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
a52c2257
HJYP
120 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 12);
121 /* old spec, for wms that do not support STRUT_PARTIAL */
bee4c26e
HJYP
122 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
123 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) data, 4);
a52c2257
HJYP
124
125 RET();
126}
127
1d9dc649 128#if 0
a52c2257 129static void
22242ed4 130print_wmdata(Panel *p)
a52c2257
HJYP
131{
132 int i;
133
a52c2257
HJYP
134 RET();
135 DBG("desktop %d/%d\n", p->curdesk, p->desknum);
136 DBG("workarea\n");
137 for (i = 0; i < p->wa_len/4; i++)
138 DBG("(%d, %d) x (%d, %d)\n",
139 p->workarea[4*i + 0],
140 p->workarea[4*i + 1],
141 p->workarea[4*i + 2],
142 p->workarea[4*i + 3]);
143 RET();
144}
1d9dc649 145#endif
a52c2257 146
e996608e 147/* defined in plugins/menu.c */
8c44345a 148gboolean show_system_menu( gpointer system_menu );
e996608e 149
3e7b8eb7
HJYP
150/* defined in configurator.c */
151void panel_configure(Panel* p, int sel_page );
152
e996608e 153/* built-in commands, defined in configurator.c */
77886b88
HJYP
154void restart(void);
155void gtk_run(void);
156
d18c9409 157static void process_client_msg ( XClientMessageEvent* ev )
77886b88 158{
8c44345a 159 int cmd = ev->data.b[0];
77886b88
HJYP
160 switch( cmd )
161 {
162 case LXPANEL_CMD_SYS_MENU:
d18c9409
HJYP
163 {
164 GSList* l;
165 for( l = all_panels; l; l = l->next )
8c44345a 166 {
d18c9409
HJYP
167 Panel* p = (Panel*)l->data;
168 if( p->system_menus )
169 {
170 /* show_system_menu( p->system_menus->data ); */
171 /* FIXME: I've no idea why this doesn't work without timeout
172 under some WMs, like icewm. */
173 g_timeout_add( 200, (GSourceFunc)show_system_menu,
174 p->system_menus->data );
175 }
8c44345a 176 }
77886b88 177 break;
d18c9409 178 }
77886b88
HJYP
179 case LXPANEL_CMD_RUN:
180 gtk_run();
181 break;
182 case LXPANEL_CMD_CONFIG:
8110399f 183 //FIXME: configure();
77886b88
HJYP
184 break;
185 case LXPANEL_CMD_RESTART:
186 restart();
187 break;
188 case LXPANEL_CMD_EXIT:
189 gtk_main_quit();
190 break;
191 }
192}
193
a52c2257 194static GdkFilterReturn
cf701cb7 195panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
a52c2257
HJYP
196{
197 Atom at;
198 Window win;
199 XEvent *ev = (XEvent *) xevent;
200
201 ENTER;
202 DBG("win = 0x%x\n", ev->xproperty.window);
77886b88
HJYP
203 if (ev->type != PropertyNotify ) {
204 /* private client message from lxpanelctl */
205 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
206 {
d18c9409 207 process_client_msg( (XClientMessageEvent*)ev );
77886b88 208 }
22242ed4
HJYP
209 else if( ev->type == DestroyNotify )
210 {
cf701cb7 211 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
22242ed4 212 }
a52c2257 213 RET(GDK_FILTER_CONTINUE);
24053345 214 }
77886b88 215
a52c2257
HJYP
216 at = ev->xproperty.atom;
217 win = ev->xproperty.window;
a52c2257 218 if (win == GDK_ROOT_WINDOW()) {
cf701cb7
HJYP
219 if (at == a_NET_CLIENT_LIST) {
220 fb_ev_emit(fbev, EV_CLIENT_LIST);
221 } else if (at == a_NET_CURRENT_DESKTOP) {
222 GSList* l;
223 for( l = all_panels; l; l = l->next )
224 ((Panel*)l->data)->curdesk = get_net_current_desktop();
22242ed4 225 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
cf701cb7
HJYP
226 } else if (at == a_NET_NUMBER_OF_DESKTOPS) {
227 GSList* l;
228 for( l = all_panels; l; l = l->next )
229 ((Panel*)l->data)->desknum = get_net_number_of_desktops();
22242ed4 230 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
cf701cb7 231 } else if (at == a_NET_DESKTOP_NAMES) {
22242ed4 232 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
a52c2257 233 } else if (at == a_NET_ACTIVE_WINDOW) {
22242ed4 234 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
cf701cb7 235 } else if (at == a_NET_CLIENT_LIST_STACKING) {
22242ed4 236 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
a52c2257 237 } else if (at == a_XROOTPMAP_ID) {
cf701cb7
HJYP
238 GSList* l;
239 for( l = all_panels; l; l = l->next )
240 {
241 Panel* p = (Panel*)l->data;
242 if (p->transparent) {
243 fb_bg_notify_changed_bg(p->bg);
244 }
245 }
246 } else if (at == a_NET_WORKAREA) {
247 GSList* l;
248 for( l = all_panels; l; l = l->next )
249 {
250 Panel* p = (Panel*)l->data;
251 g_free( p->workarea );
252 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1d9dc649 253 /* print_wmdata(p); */
a52c2257 254 }
a52c2257 255 } else
cf701cb7
HJYP
256 return GDK_FILTER_CONTINUE;
257
258 return GDK_FILTER_REMOVE;
a52c2257 259 }
cf701cb7 260 return GDK_FILTER_CONTINUE;
a52c2257
HJYP
261}
262
263/****************************************************
264 * panel's handlers for GTK events *
265 ****************************************************/
266
bee4c26e 267
a52c2257
HJYP
268static gint
269panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
270{
271 ENTER;
272 RET(FALSE);
273}
274
275static gint
276panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
277{
22242ed4 278 //Panel *p = (Panel *) data;
a52c2257
HJYP
279 //if (!p->self_destroy)
280 gtk_main_quit();
281 RET(FALSE);
282}
283
4542c20d 284static void
22242ed4 285on_root_bg_changed(FbBg *bg, Panel* p)
4542c20d
HJYP
286{
287 panel_update_background( p );
288}
289
290/* This function should only be called after the panel has been realized */
22242ed4 291void panel_update_background( Panel* p )
4542c20d
HJYP
292{
293 GList* l;
294 GdkPixmap* pixmap = NULL;
295
296 /* handle background image of panel */
297 gtk_widget_set_app_paintable(p->topgwin, TRUE);
298
299 if (p->background) {
300 pixmap = fb_bg_get_pix_from_file(p->topgwin, p->background_file);
301 if( p->bg )
302 {
303 g_object_unref( p->bg );
304 p->bg = NULL;
305 }
306 } else if (p->transparent) {
307 if( ! p->bg )
308 {
309 p->bg = fb_bg_get_for_display();
310 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), p);
311 }
312 pixmap = fb_bg_get_xroot_pix_for_win( p->bg, p->topgwin );
313
314 if (pixmap && pixmap != GDK_NO_BG) {
315 if (p->alpha)
316 fb_bg_composite( pixmap, p->topgwin->style->black_gc, p->tintcolor, p->alpha );
317 }
318 }
319 else
320 {
321 if( p->bg )
322 {
323 g_object_unref( p->bg );
324 p->bg = NULL;
325 }
326 }
327
328 if( pixmap )
329 {
330 gtk_widget_set_app_paintable( p->topgwin, TRUE );
331 gdk_window_set_back_pixmap( p->topgwin->window, pixmap, FALSE );
332 g_object_unref( pixmap );
333 }
334 else
335 {
336// gdk_window_set_back_pixmap( p->topgwin->window, p->topgwin->style->bg_pixmap[0], FALSE );
337 gtk_widget_set_app_paintable( p->topgwin, FALSE );
338// gdk_window_set_background( p->topgwin->window, &p->topgwin->style->bg[0] );
339 }
340
341 for( l = p->plugins; l; l = l->next )
342 {
22242ed4 343 Plugin* pl = (Plugin*)l->data;
4542c20d
HJYP
344 plugin_set_background( pl, p );
345 }
346
347 gdk_window_clear( p->topgwin->window );
348 gtk_widget_queue_draw( p->topgwin );
349}
a52c2257 350
84fc1d55
HJYP
351static gboolean delay_update_background( Panel* p )
352{
cbf65dac
HJYP
353 /* FIXME: can this work? */
354 gdk_display_sync( gtk_widget_get_display(p->topgwin) );
cf701cb7
HJYP
355 panel_update_background( p );
356 return FALSE;
84fc1d55
HJYP
357}
358
359static void
f2d54481
HJYP
360panel_realize(GtkWidget *widget, Panel *p)
361{
362 g_idle_add_full( G_PRIORITY_LOW, delay_update_background, p, NULL );
363}
364
365static void
84fc1d55
HJYP
366panel_style_set(GtkWidget *widget, GtkStyle* prev, Panel *p)
367{
0987f648 368 /* FIXME: This dirty hack is used to fix the background of systray... */
cf701cb7 369 if( GTK_WIDGET_REALIZED( widget ) )
f2d54481 370 g_idle_add_full( G_PRIORITY_LOW, delay_update_background, p, NULL );
84fc1d55 371}
4542c20d 372
a52c2257 373static gint
22242ed4 374panel_size_req(GtkWidget *widget, GtkRequisition *req, Panel *p)
a52c2257
HJYP
375{
376 ENTER;
4542c20d 377
a52c2257
HJYP
378 if (p->widthtype == WIDTH_REQUEST)
379 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
380 if (p->heighttype == HEIGHT_REQUEST)
381 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
382 calculate_position(p);
383 req->width = p->aw;
384 req->height = p->ah;
4542c20d 385
a52c2257
HJYP
386 RET( TRUE );
387}
388
389static gint
22242ed4 390panel_size_alloc(GtkWidget *widget, GtkAllocation *a, Panel *p)
a52c2257
HJYP
391{
392 ENTER;
a52c2257
HJYP
393 if (p->widthtype == WIDTH_REQUEST)
394 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
395 if (p->heighttype == HEIGHT_REQUEST)
396 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
397 calculate_position(p);
4542c20d 398
a52c2257 399 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
a52c2257
HJYP
400 RET(TRUE);
401 }
402
403 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
bee4c26e 404 panel_set_wm_strut(p);
a52c2257
HJYP
405 RET(TRUE);
406}
407
a52c2257 408static gboolean
22242ed4 409panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, Panel *p)
a52c2257
HJYP
410{
411 ENTER;
412 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
413 RET(TRUE);
414 p->cw = e->width;
415 p->ch = e->height;
416 p->cx = e->x;
417 p->cy = e->y;
4542c20d 418
a52c2257
HJYP
419 if (p->transparent)
420 fb_bg_notify_changed_bg(p->bg);
bee4c26e 421
4542c20d 422 RET(FALSE);
a52c2257
HJYP
423}
424
fddae119
FC
425static gint
426panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
427{
4b93d81e
HJYP
428 panel_configure( (Panel*)user_data, 0 );
429 return TRUE;
fddae119
FC
430}
431
432static gint
433panel_press_button_event(GtkWidget *widget, GdkEvent *event, gpointer user_data)
434{
435 GdkEventButton *event_button;
cf701cb7 436 GtkWidget* img;
fddae119
FC
437
438 g_return_val_if_fail (event != NULL, FALSE);
439 event_button = (GdkEventButton *)event;
440 if (event_button->button == 3) {
441 GtkWidget *menu;
cf701cb7 442 Panel* panel = (Panel*)user_data;
fddae119 443 /* create menu */
3e7b8eb7 444 menu = lxpanel_get_panel_menu( panel, NULL, FALSE );
fddae119
FC
445 gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event_button->button, event_button->time);
446 return TRUE;
447 }
448
449 return FALSE;
450}
451
cf701cb7
HJYP
452static void panel_popupmenu_config_plugin( GtkMenuItem* item, Plugin* plugin )
453{
454 plugin->class->config( plugin, plugin->panel->topgwin );
930af9fd
HJYP
455
456 /* FIXME: this should be more elegant */
457 plugin->panel->config_changed = TRUE;
cf701cb7
HJYP
458}
459
4b93d81e
HJYP
460#if 0
461static void on_add_plugin_response( GtkDialog* dlg,
462 int response,
463 Panel* p )
cf701cb7 464{
4b93d81e
HJYP
465 if( response == GTK_RESPONSE_OK )
466 {
467 GtkTreeView* view;
468 GtkTreeSelection* tree_sel;
469 GtkTreeIter it;
470 GtkTreeModel* model;
471
472 view = (GtkTreeView*)g_object_get_data( G_OBJECT(dlg), "avail-plugins" );
473 tree_sel = gtk_tree_view_get_selection( view );
474 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
475 {
476 char* type = NULL;
477 Plugin* pl;
478 gtk_tree_model_get( model, &it, 1, &type, -1 );
479 if( pl = plugin_load( type ) )
480 {
481 GtkTreePath* tree_path;
cf701cb7 482
4b93d81e
HJYP
483 pl->panel = p;
484 plugin_start( pl, NULL );
485 p->plugins = g_list_append(p->plugins, pl);
486 /* FIXME: will show all cause problems? */
487 gtk_widget_show_all( pl->pwid );
488
489 /* update background of the newly added plugin */
490 plugin_widget_set_background( pl->pwid, pl->panel );
491 }
492 g_free( type );
493 }
494 }
495 gtk_widget_destroy( (GtkWidget*)dlg );
496}
497
498void panel_add_plugin( Panel* panel, GtkWindow* parent_win )
499{
500 GtkWidget* dlg, *scroll;
501 GList* classes;
502 GList* tmp;
503 GtkTreeViewColumn* col;
504 GtkCellRenderer* render;
505 GtkTreeView* view;
506 GtkListStore* list;
507 GtkTreeSelection* tree_sel;
508
509 classes = plugin_get_available_classes();
510
511 dlg = gtk_dialog_new_with_buttons( _("Add plugin to panel"),
512 GTK_WINDOW(parent_win), 0,
513 GTK_STOCK_CANCEL,
514 GTK_RESPONSE_CANCEL,
515 GTK_STOCK_ADD,
516 GTK_RESPONSE_OK, NULL );
517
518 /* gtk_widget_set_sensitive( parent_win, FALSE ); */
519 scroll = gtk_scrolled_window_new( NULL, NULL );
520 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)scroll,
521 GTK_SHADOW_IN );
522 gtk_scrolled_window_set_policy((GtkScrolledWindow*)scroll,
523 GTK_POLICY_AUTOMATIC,
524 GTK_POLICY_AUTOMATIC );
525 gtk_box_pack_start( (GtkBox*)GTK_DIALOG(dlg)->vbox, scroll,
526 TRUE, TRUE, 4 );
527 view = (GtkTreeView*)gtk_tree_view_new();
528 gtk_container_add( (GtkContainer*)scroll, view );
529 tree_sel = gtk_tree_view_get_selection( view );
530 gtk_tree_selection_set_mode( tree_sel, GTK_SELECTION_BROWSE );
531
532 render = gtk_cell_renderer_text_new();
533 col = gtk_tree_view_column_new_with_attributes(
534 _("Available plugins"),
535 render, "text", 0, NULL );
536 gtk_tree_view_append_column( view, col );
537
538 list = gtk_list_store_new( 2,
539 G_TYPE_STRING,
540 G_TYPE_STRING );
541
542 for( tmp = classes; tmp; tmp = tmp->next ) {
543 PluginClass* pc = (PluginClass*)tmp->data;
544 if( ! pc->invisible ) {
545 /* FIXME: should we display invisible plugins? */
546 GtkTreeIter it;
547 gtk_list_store_append( list, &it );
548 gtk_list_store_set( list, &it,
549 0, _(pc->name),
550 1, pc->type, -1 );
551 /* g_debug( "%s (%s)", pc->type, _(pc->name) ); */
552 }
553 }
554
555 gtk_tree_view_set_model( view, GTK_TREE_MODEL(list) );
556 g_object_unref( list );
557
558 g_signal_connect( dlg, "response",
559 on_add_plugin_response, panel );
560 g_object_set_data( dlg, "avail-plugins", view );
561 g_object_weak_ref( dlg, plugin_class_list_free, classes );
562
563 gtk_window_set_default_size( (GtkWindow*)dlg, 320, 400 );
564 gtk_widget_show_all( dlg );
565}
566#endif
567
568static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
569{
570 /* panel_add_plugin( panel, panel->topgwin ); */
571 panel_configure( panel, 1 );
cf701cb7
HJYP
572}
573
574static void panel_popupmenu_remove_item( GtkMenuItem* item, Plugin* plugin )
575{
576 Panel* panel = plugin->panel;
577 panel->plugins = g_list_remove( panel->plugins, plugin );
578 plugin_stop( plugin ); /* free the plugin widget & its data */
579 plugin_put( plugin ); /* free the lib if necessary */
4b93d81e
HJYP
580
581 panel_config_save( plugin->panel );
cf701cb7
HJYP
582}
583
3e7b8eb7
HJYP
584/* FIXME: Potentially we can support multiple panels at the same edge,
585 * but currently this cannot be done due to some positioning problems. */
586static char* gen_panel_name( int edge )
587{
588 const char* edge_str = num2str( edge_pair, edge, "" );
589 char* name = NULL;
590 char* dir = get_config_file( cprofile, "panels", FALSE );
591 int i;
592 for( i = 0; i < G_MAXINT; ++i )
593 {
594 char* f;
595 if( G_LIKELY( i > 0 ) )
596 name = g_strdup_printf( "%s%d", edge_str, i );
597 else
598 name = g_strdup( edge_str );
599 f = g_build_filename( dir, name, NULL );
600 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
601 {
602 g_free( f );
603 break;
604 }
605 g_free( name );
606 g_free( f );
607 }
608 g_free( dir );
609 return name;
610}
611
612/* FIXME: Potentially we can support multiple panels at the same edge,
613 * but currently this cannot be done due to some positioning problems. */
cf701cb7
HJYP
614static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
615{
3e7b8eb7
HJYP
616 int i;
617 GSList* group = NULL;
618 GtkWidget* btns[ 4 ], *box, *frame;
619 const char* edges[]={N_("Left"), N_("Right"), N_("Top"), N_("Bottom")};
7414a73f
HJYP
620 GtkWidget* dlg = gtk_dialog_new_with_buttons(
621 _("Create New Panel"),
622 panel->topgwin,
623 GTK_DIALOG_MODAL,
624 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
625 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL );
3e7b8eb7
HJYP
626 gtk_container_set_border_width( (GtkContainer*)dlg, 8 );
627 frame = gtk_frame_new( _("Where to put the panel?"));
628 gtk_box_pack_start( (GtkBox*)((GtkDialog*)dlg)->vbox, frame, TRUE, TRUE, 0 );
629 box = gtk_vbox_new( FALSE, 2 );
630 gtk_container_add( (GtkContainer*)frame, box );
631 for( i = 0; i < 4; ++i )
632 {
633 GSList* l;
634 btns[ i ] = gtk_radio_button_new_with_label( group, _(edges[i]) );
635 group = gtk_radio_button_get_group( (GtkRadioButton*)btns[ i ] );
636 gtk_box_pack_start( box, btns[ i ], FALSE, TRUE, 2 );
637 for( l = all_panels; l; l = l->next )
638 {
639 Panel* p = (Panel*)l->data;
640 /* If there is already a panel on this edge */
641 if( p->edge == (i + 1) )
642 gtk_widget_set_sensitive( btns[i], FALSE );
643 /* FIXME: multiple panel at the same edge should be supported in the future. */
644 }
645 }
646 gtk_widget_show_all( dlg );
647
648 if( gtk_dialog_run( dlg ) == GTK_RESPONSE_OK )
649 {
650 char* pfp;
651 char default_conf[128];
652 for( i = 0; i < 4; ++i )
653 {
654 if( gtk_toggle_button_get_active( (GtkToggleButton*)btns[i] ) )
655 break;
656 }
657 ++i; /* 0 is EDGE_NONE, all edge values start from 1 */
658 g_snprintf( default_conf, 128,
659 "global{\n"
660 "edge=%s\n"
661 "}\n",
662 num2str( edge_pair, i, "bottom" ) );
663 panel = g_slice_new0( Panel );
664 panel->name = gen_panel_name(i);
665 pfp = default_conf;
666 if ( panel_start( panel, &pfp )) {
667 panel_config_save( panel );
668 all_panels = g_slist_prepend( all_panels, panel );
669 }
670 else {
671 panel_destroy( panel );
672 }
673 }
7414a73f 674 gtk_widget_destroy( dlg );
cf701cb7
HJYP
675}
676
677static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
678{
679 GtkWidget* dlg;
680 gboolean ok;
681 dlg = gtk_message_dialog_new_with_markup( panel->topgwin,
682 GTK_DIALOG_MODAL,
683 GTK_MESSAGE_QUESTION,
684 GTK_BUTTONS_OK_CANCEL,
685 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
686 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
687 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
688 gtk_widget_destroy( dlg );
689 if( ok )
690 {
4b93d81e 691 gchar *fname, *dir;
cf701cb7 692 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
693
694 /* delete the config file of this panel */
695 dir = get_config_file( cprofile, "panels", FALSE );
696 fname = g_build_filename( dir, panel->name, NULL );
697 g_free( dir );
698 g_unlink( fname );
930af9fd 699 panel->config_changed = 0;
cf701cb7
HJYP
700 panel_destroy( panel );
701 }
702}
703
704extern GtkWidget* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
705{
706 GtkWidget *ret,*menu, *menu_item, *img;
707 char* tmp;
708 ret = menu = gtk_menu_new();
709
4b93d81e 710/*
cf701cb7
HJYP
711 img = gtk_image_new_from_stock( GTK_STOCK_ADD, GTK_ICON_SIZE_MENU );
712 menu_item = gtk_image_menu_item_new_with_label(_("Add Item To Panel"));
713 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
714 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
715 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
4b93d81e
HJYP
716*/
717
718 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
719 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
720 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
721 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
722 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
723
724 if( plugin )
725 {
726 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
727 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
728 menu_item = gtk_image_menu_item_new_with_label( tmp );
729 g_free( tmp );
730 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
731 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
732 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
733 }
734
735 menu_item = gtk_separator_menu_item_new();
736 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
737
4b93d81e
HJYP
738 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
739 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
740 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
741 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
742 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
743
cf701cb7
HJYP
744 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
745 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
746 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
747 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
748 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
3e7b8eb7
HJYP
749 /* FIXME: Potentially we can support multiple panels at the same edge,
750 * but currently this cannot be done due to some positioning problems. */
751 /* currently, disable the option when there are already four panels */
752 if( g_slist_length( all_panels ) >= 4 )
753 gtk_widget_set_sensitive( menu_item, FALSE );
cf701cb7
HJYP
754
755 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
756 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
757 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
758 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
759 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
760 if( ! all_panels->next ) /* if this is the only panel */
761 gtk_widget_set_sensitive( menu_item, FALSE );
762
cf701cb7
HJYP
763 if( use_sub_menu )
764 {
765 ret = gtk_menu_new();
766 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
767 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
768 gtk_menu_item_set_submenu( menu_item, menu );
769
770 gtk_widget_show_all(ret);
771 }
772
3e7b8eb7
HJYP
773 if( plugin )
774 {
775 menu_item = gtk_separator_menu_item_new();
776 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
777
778 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
779 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
780 menu_item = gtk_image_menu_item_new_with_label( tmp );
781 g_free( tmp );
782 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
783 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
784 if( plugin->class->config )
785 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
786 else
787 gtk_widget_set_sensitive( menu_item, FALSE );
788 }
789
790 gtk_widget_show_all(menu);
791
cf701cb7
HJYP
792 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
793 return ret;
794}
795
fddae119 796
a52c2257
HJYP
797
798/****************************************************
799 * panel creation *
800 ****************************************************/
801static void
22242ed4 802make_round_corners(Panel *p)
a52c2257 803{
a97d06a6 804 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 805 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
806}
807
22242ed4 808void panel_set_dock_type(Panel *p)
bee4c26e
HJYP
809{
810 if (p->setdocktype) {
811 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
812 XChangeProperty(GDK_DISPLAY(), p->topxwin,
813 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
814 PropModeReplace, (unsigned char *) &state, 1);
815 }
816 else {
817 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
818 }
a52c2257
HJYP
819}
820
0defe4b9 821static void
22242ed4 822panel_start_gui(Panel *p)
a52c2257
HJYP
823{
824 Atom state[3];
825 XWMHints wmhints;
826 guint32 val;
6db11841 827
a52c2257
HJYP
828 ENTER;
829
830 // main toplevel window
831 p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
832 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
833 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
834 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
835 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
836 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
837 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 838
a52c2257
HJYP
839 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
840 G_CALLBACK(panel_delete_event), p);
841 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
842 G_CALLBACK(panel_destroy_event), p);
843 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
844 (GCallback) panel_size_req, p);
845 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
846 (GCallback) panel_size_alloc, p);
847 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
848 (GCallback) panel_configure_event, p);
3e7b8eb7
HJYP
849
850 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
fddae119 851 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
8110399f 852 (GCallback) panel_press_button_event, p);
f2d54481 853
a52c2257
HJYP
854 g_signal_connect (G_OBJECT (p->topgwin), "realize",
855 (GCallback) panel_realize, p);
f2d54481 856
84fc1d55
HJYP
857 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
858 (GCallback)panel_style_set, p);
a52c2257
HJYP
859 gtk_widget_realize(p->topgwin);
860 //gdk_window_set_decorations(p->topgwin->window, 0);
2de71c90 861
4542c20d 862 // main layout manager as a single child of panel
a97d06a6 863 p->box = p->my_box_new(FALSE, 0);
a52c2257 864 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
4542c20d
HJYP
865// gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
866 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
a52c2257 867 gtk_widget_show(p->box);
a97d06a6
HJYP
868 if (p->round_corners)
869 make_round_corners(p);
6db11841 870
a52c2257
HJYP
871 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
872 DBG("topxwin = %x\n", p->topxwin);
873
874 /* the settings that should be done before window is mapped */
875 wmhints.flags = InputHint;
876 wmhints.input = 0;
bee4c26e 877 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
24053345 878#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257
HJYP
879 val = WIN_HINTS_SKIP_FOCUS;
880 XChangeProperty(GDK_DISPLAY(), p->topxwin,
881 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
882 PropModeReplace, (unsigned char *) &val, 1);
883
bee4c26e 884 panel_set_dock_type(p);
a52c2257
HJYP
885
886 /* window mapping point */
887 gtk_widget_show_all(p->topgwin);
888
889 /* the settings that should be done after window is mapped */
890
891 /* send it to running wm */
892 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
893 /* and assign it ourself just for case when wm is not running */
894 val = 0xFFFFFFFF;
895 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
896 PropModeReplace, (unsigned char *) &val, 1);
897
898 state[0] = a_NET_WM_STATE_SKIP_PAGER;
899 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
900 state[2] = a_NET_WM_STATE_STICKY;
901 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
902 32, PropModeReplace, (unsigned char *) state, 3);
903
a52c2257
HJYP
904 calculate_position(p);
905 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 906 panel_set_wm_strut(p);
77886b88 907
239cb032
HJYP
908 p->tooltips = gtk_tooltips_new();
909#if GLIB_CHECK_VERSION( 2, 10, 0 )
910 g_object_ref_sink( p->tooltips );
911#else
912 g_object_ref( p->tooltips );
913 gtk_object_sink( p->tooltips );
914#endif
915
a52c2257
HJYP
916 RET();
917}
918
22242ed4 919void panel_set_orientation(Panel *p)
a97d06a6
HJYP
920{
921 GList* l;
922 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
923 ? ORIENT_HORIZ : ORIENT_VERT;
924 if (p->orientation == ORIENT_HORIZ) {
925 p->my_box_new = gtk_hbox_new;
926 p->my_separator_new = gtk_vseparator_new;
927 } else {
928 p->my_box_new = gtk_vbox_new;
929 p->my_separator_new = gtk_hseparator_new;
930 }
931
932 /* recreate the main layout box */
933 if( p->box ) {
5a343ad5
JH
934 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
935 if( GTK_WIDGET(newbox) != p->box ) {
936 p->box = GTK_WIDGET(newbox);
4542c20d 937 gtk_container_add( GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox) );
a97d06a6
HJYP
938 }
939 }
940 /* NOTE: This loop won't be executed when panel started since
941 plugins are not loaded at that time.
942 This is used when the orientation of the panel is changed
943 from the config dialog, and plugins should be re-layout.
944 */
945 for( l = p->plugins; l; l = l->next ) {
22242ed4 946 Plugin* pl = (Plugin*)l->data;
a97d06a6
HJYP
947 if( pl->class->orientation ) {
948 pl->class->orientation( pl );
949 }
950 }
951}
952
a52c2257 953static int
22242ed4 954panel_parse_global(Panel *p, char **fp)
a52c2257
HJYP
955{
956 line s;
957 s.len = 256;
6db11841 958
3e7b8eb7
HJYP
959 if( G_LIKELY( fp ) )
960 {
961 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
962 if (s.type == LINE_VAR) {
963 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
964 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
965 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
966 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
967 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
968 p->margin = atoi(s.t[1]);
969 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
970 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
971 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
972 p->width = atoi(s.t[1]);
973 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
974 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
975 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
976 p->height = atoi(s.t[1]);
977 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
978 p->spacing = atoi(s.t[1]);
979 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
980 p->setdocktype = str2num(bool_pair, s.t[1], 0);
981 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
982 p->setstrut = str2num(bool_pair, s.t[1], 0);
983 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
984 p->round_corners = str2num(bool_pair, s.t[1], 0);
985 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
986 p->transparent = str2num(bool_pair, s.t[1], 0);
987 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
988 p->alpha = atoi(s.t[1]);
989 if (p->alpha > 255)
990 p->alpha = 255;
991 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
992 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
993 gdk_color_parse ("white", &p->gtintcolor);
994 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
995 DBG("tintcolor=%x\n", p->tintcolor);
996 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
997 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
998 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
999 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1000 gdk_color_parse ("black", &p->gfontcolor);
1001 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1002 DBG("fontcolor=%x\n", p->fontcolor);
1003 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1004 p->background = str2num(bool_pair, s.t[1], 0);
1005 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1006 p->background_file = g_strdup( s.t[1] );
1007 } else {
1008 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
1009 RET(0);
1010 }
1011 } else if (s.type == LINE_BLOCK_END) {
1012 break;
a52c2257 1013 } else {
3e7b8eb7 1014 ERR( "lxpanel: illegal in this context %s\n", s.str);
a52c2257
HJYP
1015 RET(0);
1016 }
a52c2257
HJYP
1017 }
1018 }
a97d06a6 1019 panel_set_orientation( p );
4542c20d 1020
a52c2257
HJYP
1021 if (p->width < 0)
1022 p->width = 100;
1023 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
1024 p->width = 100;
1025 p->heighttype = HEIGHT_PIXEL;
1026 if (p->heighttype == HEIGHT_PIXEL) {
1027 if (p->height < PANEL_HEIGHT_MIN)
1028 p->height = PANEL_HEIGHT_MIN;
1029 else if (p->height > PANEL_HEIGHT_MAX)
1030 p->height = PANEL_HEIGHT_MAX;
1031 }
2de71c90
FC
1032
1033 if (p->background)
1034 p->transparent = 0;
1035
a52c2257
HJYP
1036 p->curdesk = get_net_current_desktop();
1037 p->desknum = get_net_number_of_desktops();
1038 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1d9dc649 1039 /* print_wmdata(p); */
239cb032 1040
a52c2257
HJYP
1041 panel_start_gui(p);
1042 RET(1);
1043}
1044
1045static int
22242ed4 1046panel_parse_plugin(Panel *p, char **fp)
a52c2257
HJYP
1047{
1048 line s;
22242ed4 1049 Plugin *plug = NULL;
a52c2257 1050 gchar *type = NULL;
a52c2257 1051 int expand , padding, border;
db449f6e
HJYP
1052 char* pconfig = NULL;
1053
a52c2257
HJYP
1054 ENTER;
1055 s.len = 256;
a52c2257 1056 border = expand = padding = 0;
c69ac68e 1057 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
1058 if (s.type == LINE_NONE) {
1059 ERR( "lxpanel: bad line %s\n", s.str);
1060 goto error;
1061 }
1062 if (s.type == LINE_VAR) {
1063 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1064 type = g_strdup(s.t[1]);
1065 DBG("plug %s\n", type);
1066 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1067 expand = str2num(bool_pair, s.t[1], 0);
1068 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1069 padding = atoi(s.t[1]);
1070 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1071 border = atoi(s.t[1]);
1072 else {
1073 ERR( "lxpanel: unknown var %s\n", s.t[0]);
1074 goto error;
1075 }
1076 } else if (s.type == LINE_BLOCK_START) {
1077 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 1078 pconfig = *fp;
a52c2257
HJYP
1079 int pno = 1;
1080 while (pno) {
1081 get_line_as_is(fp, &s);
1082 if (s.type == LINE_NONE) {
1083 ERR( "lxpanel: unexpected eof\n");
1084 goto error;
1085 } else if (s.type == LINE_BLOCK_START) {
1086 pno++;
1087 } else if (s.type == LINE_BLOCK_END) {
1088 pno--;
bee4c26e 1089 }
db449f6e 1090 }
a52c2257
HJYP
1091 } else {
1092 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1093 goto error;
1094 }
1095 } else {
1096 ERR( "lxpanel: illegal in this context %s\n", s.str);
1097 goto error;
1098 }
1099 }
db449f6e 1100
a52c2257
HJYP
1101 if (!type || !(plug = plugin_load(type))) {
1102 ERR( "lxpanel: can't load %s plugin\n", type);
1103 goto error;
1104 }
db449f6e 1105
a52c2257 1106 plug->panel = p;
a52c2257
HJYP
1107 plug->expand = expand;
1108 plug->padding = padding;
1109 plug->border = border;
a52c2257 1110 DBG("starting\n");
db449f6e 1111 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
1112 ERR( "lxpanel: can't start plugin %s\n", type);
1113 goto error;
1114 }
1115 DBG("plug %s\n", type);
1116 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
1117
1118 g_free( type );
a52c2257 1119 RET(1);
db449f6e 1120
a52c2257 1121 error:
a52c2257
HJYP
1122 g_free(type);
1123 if (plug)
1124 plugin_put(plug);
1125 RET(0);
a52c2257
HJYP
1126}
1127
3e7b8eb7 1128int panel_start( Panel *p, char **fp )
a52c2257
HJYP
1129{
1130 line s;
db449f6e 1131
a52c2257
HJYP
1132 /* parse global section */
1133 ENTER;
1134 s.len = 256;
8110399f 1135
a52c2257
HJYP
1136 p->allign = ALLIGN_CENTER;
1137 p->edge = EDGE_BOTTOM;
1138 p->widthtype = WIDTH_PERCENT;
1139 p->width = 100;
1140 p->heighttype = HEIGHT_PIXEL;
1141 p->height = PANEL_HEIGHT_DEFAULT;
1142 p->setdocktype = 1;
1143 p->setstrut = 1;
1144 p->round_corners = 0;
1145 p->transparent = 0;
1146 p->alpha = 127;
1147 p->tintcolor = 0xFFFFFFFF;
2de71c90
FC
1148 p->usefontcolor = 0;
1149 p->fontcolor = 0x00000000;
a52c2257 1150 p->spacing = 0;
22242ed4 1151
c69ac68e 1152 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
1153 ERR( "lxpanel: config file must start from Global section\n");
1154 RET(0);
1155 }
1156 if (!panel_parse_global(p, fp))
1157 RET(0);
1158
c69ac68e 1159 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
1160 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1161 ERR( "lxpanel: expecting Plugin section\n");
1162 RET(0);
1163 }
9939506e 1164 panel_parse_plugin(p, fp);
a52c2257
HJYP
1165 }
1166 gtk_widget_show_all(p->topgwin);
4542c20d
HJYP
1167
1168 /* update backgrond of panel and all plugins */
1169 panel_update_background( p );
1170
1d9dc649 1171 /* print_wmdata(p); */
a52c2257
HJYP
1172 RET(1);
1173}
1174
1175static void
1176delete_plugin(gpointer data, gpointer udata)
1177{
1178 ENTER;
22242ed4
HJYP
1179 plugin_stop((Plugin *)data);
1180 plugin_put((Plugin *)data);
a52c2257 1181 RET();
a52c2257
HJYP
1182}
1183
8110399f 1184void panel_destroy(Panel *p)
a52c2257
HJYP
1185{
1186 ENTER;
1187
930af9fd
HJYP
1188 if( p->config_changed )
1189 panel_config_save( p );
1190
a52c2257
HJYP
1191 g_list_foreach(p->plugins, delete_plugin, NULL);
1192 g_list_free(p->plugins);
1193 p->plugins = NULL;
8c44345a 1194
5297da29 1195 if( p->system_menus ){
8c44345a 1196 do{
5297da29 1197 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
1198 }
1199
4b93d81e
HJYP
1200 if( p->tooltips )
1201 g_object_unref( p->tooltips );
239cb032 1202
4b93d81e
HJYP
1203 if( p->topgwin )
1204 gtk_widget_destroy(p->topgwin);
a52c2257 1205 g_free(p->workarea);
2de71c90 1206 g_free( p->background_file );
5297da29 1207 g_slist_free( p->system_menus );
a52c2257
HJYP
1208 gdk_flush();
1209 XFlush(GDK_DISPLAY());
1210 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1211
1212 g_free( p->name );
cf701cb7 1213 g_slice_free( Panel, p );
a52c2257
HJYP
1214 RET();
1215}
1216
8110399f
HJYP
1217Panel* panel_new( const char* config_file, const char* config_name )
1218{
1219 char *fp, *pfp; /* point to current position of profile data in memory */
cf701cb7 1220 Panel* panel = NULL;
8110399f 1221 char* ret;
3e7b8eb7 1222 if( G_LIKELY(config_file) )
cf701cb7 1223 {
3e7b8eb7
HJYP
1224 g_file_get_contents( config_file, &fp, NULL, NULL );
1225 if( fp )
1226 {
1227 panel = g_slice_new0( Panel );
1228 panel->name = g_strdup( config_name );
1229 pfp = fp;
1230
1231 if (! panel_start( panel, &pfp )) {
1232 ERR( "lxpanel: can't start panel\n");
1233 panel_destroy( panel );
1234 panel = NULL;
1235 }
cf701cb7 1236
3e7b8eb7 1237 g_free( fp );
cf701cb7 1238 }
cf701cb7
HJYP
1239 }
1240 return panel;
8110399f 1241}
a52c2257 1242
0defe4b9 1243static void
a52c2257
HJYP
1244usage()
1245{
e7cb732b
HJYP
1246 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1247 g_print(_("Command line options:\n"));
1248 g_print(_(" --help -- print this help and exit\n"));
1249 g_print(_(" --version -- print version and exit\n"));
1250 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
1251 g_print(_(" --configure -- launch configuration utility\n"));
1252 g_print(_(" --profile name -- use specified profile\n"));
1253 g_print("\n");
1254 g_print(_(" -h -- same as --help\n"));
1255 g_print(_(" -p -- same as --profile\n"));
1256 g_print(_(" -v -- same as --version\n"));
1257 g_print(_(" -C -- same as --configure\n"));
b37c9aae 1258 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1259}
1260
e68b47dc 1261static void
a52c2257
HJYP
1262handle_error(Display * d, XErrorEvent * ev)
1263{
1264 char buf[256];
1265
1266 ENTER;
1267 if (log_level >= LOG_WARN) {
1268 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1269 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1270 }
1271 RET();
1272}
1273
e68b47dc
JH
1274/* Lightweight lock related functions - X clipboard hacks */
1275
1276#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1277
1278/*
1279 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1280 */
1281static void
1282clipboard_get_func(
1283 GtkClipboard *clipboard G_GNUC_UNUSED,
1284 GtkSelectionData *selection_data G_GNUC_UNUSED,
1285 guint info G_GNUC_UNUSED,
1286 gpointer user_data_or_owner G_GNUC_UNUSED)
1287{
1288}
1289
1290/*
1291 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1292 */
1293static void clipboard_clear_func(
1294 GtkClipboard *clipboard G_GNUC_UNUSED,
1295 gpointer user_data_or_owner G_GNUC_UNUSED)
1296{
1297}
1298
1299/*
1300 * Lightweight version for checking single instance.
1301 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1302 *
1303 * Returns TRUE if successfully retrieved and FALSE otherwise.
1304 */
16fb8c2e 1305static gboolean check_main_lock()
e68b47dc
JH
1306{
1307 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1308 gboolean retval = FALSE;
1309 GtkClipboard *clipboard;
1310 Atom atom;
1311
1312 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1313
1314 XGrabServer(GDK_DISPLAY());
1315
1316 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1317 goto out;
1318
1319 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1320
1321 if (gtk_clipboard_set_with_data(clipboard, targets,
1322 G_N_ELEMENTS (targets),
1323 clipboard_get_func,
1324 clipboard_clear_func, NULL))
1325 retval = TRUE;
1326
1327out:
1328 XUngrabServer (GDK_DISPLAY ());
1329 gdk_flush ();
1330
1331 return retval;
1332}
1333#undef CLIPBOARD_NAME
1334
8110399f
HJYP
1335static gboolean start_all_panels( )
1336{
cf701cb7 1337 gboolean is_global;
4b93d81e 1338 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
cf701cb7
HJYP
1339 {
1340 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1341 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1342 char* name;
1343
1344 if( ! dir )
1345 {
1346 g_free( panel_dir );
1347 continue;
1348 }
1349
1350 while( name = g_dir_read_name( dir ) )
1351 {
1352 char* panel_config = g_build_filename( panel_dir, name, NULL );
1353 Panel* panel = panel_new( panel_config, name );
1354 if( panel )
1355 all_panels = g_slist_prepend( all_panels, panel );
1356 g_free( panel_config );
1357 }
1358 g_dir_close( dir );
1359 g_free( panel_dir );
1360 }
1361 return all_panels != NULL;
8110399f
HJYP
1362}
1363
cf701cb7
HJYP
1364void load_global_config();
1365void free_global_config();
1366
8110399f 1367int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1368{
1369 int i;
f277dbb7 1370
a52c2257 1371 setlocale(LC_CTYPE, "");
f277dbb7 1372
a52c2257
HJYP
1373 gtk_init(&argc, &argv);
1374
1375#ifdef ENABLE_NLS
1376 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1377 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1378 textdomain ( GETTEXT_PACKAGE );
1379#endif
1380
1381 XSetLocaleModifiers("");
1382 XSetErrorHandler((XErrorHandler) handle_error);
8110399f 1383
a52c2257 1384 resolve_atoms();
8110399f 1385
a52c2257
HJYP
1386 for (i = 1; i < argc; i++) {
1387 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1388 usage();
1389 exit(0);
1390 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1391 printf("lxpanel %s\n", version);
1392 exit(0);
1393 } else if (!strcmp(argv[i], "--log")) {
1394 i++;
1395 if (i == argc) {
1396 ERR( "lxpanel: missing log level\n");
1397 usage();
1398 exit(1);
1399 } else {
1400 log_level = atoi(argv[i]);
1401 }
1402 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1403 config = 1;
1404 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1405 i++;
1406 if (i == argc) {
1407 ERR( "lxpanel: missing profile name\n");
1408 usage();
1409 exit(1);
1410 } else {
1411 cprofile = g_strdup(argv[i]);
1412 }
1413 } else {
1414 printf("lxpanel: unknown option - %s\n", argv[i]);
1415 usage();
1416 exit(1);
1417 }
1418 }
f277dbb7 1419
8110399f 1420 /* Check for duplicated lxpanel instances */
e68b47dc
JH
1421 if (!check_main_lock() && !config) {
1422 printf("There is alreay an instance of LXPanel. Now to exit\n");
1423 exit(1);
1424 }
1425
f277dbb7
HJYP
1426 /* Add our own icons to the search path of icon theme */
1427 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
1428 PACKAGE_DATA_DIR "/lxpanel/images" );
1429
cf701cb7 1430 fbev = fb_ev_new();
22242ed4 1431
f7cb330e
HJYP
1432restart:
1433 is_restarting = FALSE;
1434
cf701cb7
HJYP
1435 load_global_config();
1436
1d9dc649
HJYP
1437 /* NOTE: StructureNotifyMask is required by XRandR
1438 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1439 */
1440 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
cf701cb7 1441 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1442
cf701cb7
HJYP
1443 if( G_UNLIKELY( ! start_all_panels() ) )
1444 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1445/*
1446 * FIXME: configure??
6a6ad54e
HJYP
1447 if (config)
1448 configure();
8110399f 1449*/
6a6ad54e 1450 gtk_main();
8110399f 1451
cf701cb7
HJYP
1452 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1453 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1454
8110399f
HJYP
1455 /* destroy all panels */
1456 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1457 g_slist_free( all_panels );
1458 all_panels = NULL;
6a6ad54e 1459 g_free( cfgfile );
5541b8d2 1460
cf701cb7
HJYP
1461 free_global_config();
1462
f7cb330e
HJYP
1463 if( is_restarting )
1464 goto restart;
1465
22242ed4
HJYP
1466 g_object_unref(fbev);
1467
5541b8d2 1468 return 0;
a52c2257 1469}