Fix background of systray icons with *very dirty but working* hacks.
[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 );
455}
456
4b93d81e
HJYP
457#if 0
458static void on_add_plugin_response( GtkDialog* dlg,
459 int response,
460 Panel* p )
cf701cb7 461{
4b93d81e
HJYP
462 if( response == GTK_RESPONSE_OK )
463 {
464 GtkTreeView* view;
465 GtkTreeSelection* tree_sel;
466 GtkTreeIter it;
467 GtkTreeModel* model;
468
469 view = (GtkTreeView*)g_object_get_data( G_OBJECT(dlg), "avail-plugins" );
470 tree_sel = gtk_tree_view_get_selection( view );
471 if( gtk_tree_selection_get_selected( tree_sel, &model, &it ) )
472 {
473 char* type = NULL;
474 Plugin* pl;
475 gtk_tree_model_get( model, &it, 1, &type, -1 );
476 if( pl = plugin_load( type ) )
477 {
478 GtkTreePath* tree_path;
cf701cb7 479
4b93d81e
HJYP
480 pl->panel = p;
481 plugin_start( pl, NULL );
482 p->plugins = g_list_append(p->plugins, pl);
483 /* FIXME: will show all cause problems? */
484 gtk_widget_show_all( pl->pwid );
485
486 /* update background of the newly added plugin */
487 plugin_widget_set_background( pl->pwid, pl->panel );
488 }
489 g_free( type );
490 }
491 }
492 gtk_widget_destroy( (GtkWidget*)dlg );
493}
494
495void panel_add_plugin( Panel* panel, GtkWindow* parent_win )
496{
497 GtkWidget* dlg, *scroll;
498 GList* classes;
499 GList* tmp;
500 GtkTreeViewColumn* col;
501 GtkCellRenderer* render;
502 GtkTreeView* view;
503 GtkListStore* list;
504 GtkTreeSelection* tree_sel;
505
506 classes = plugin_get_available_classes();
507
508 dlg = gtk_dialog_new_with_buttons( _("Add plugin to panel"),
509 GTK_WINDOW(parent_win), 0,
510 GTK_STOCK_CANCEL,
511 GTK_RESPONSE_CANCEL,
512 GTK_STOCK_ADD,
513 GTK_RESPONSE_OK, NULL );
514
515 /* gtk_widget_set_sensitive( parent_win, FALSE ); */
516 scroll = gtk_scrolled_window_new( NULL, NULL );
517 gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)scroll,
518 GTK_SHADOW_IN );
519 gtk_scrolled_window_set_policy((GtkScrolledWindow*)scroll,
520 GTK_POLICY_AUTOMATIC,
521 GTK_POLICY_AUTOMATIC );
522 gtk_box_pack_start( (GtkBox*)GTK_DIALOG(dlg)->vbox, scroll,
523 TRUE, TRUE, 4 );
524 view = (GtkTreeView*)gtk_tree_view_new();
525 gtk_container_add( (GtkContainer*)scroll, view );
526 tree_sel = gtk_tree_view_get_selection( view );
527 gtk_tree_selection_set_mode( tree_sel, GTK_SELECTION_BROWSE );
528
529 render = gtk_cell_renderer_text_new();
530 col = gtk_tree_view_column_new_with_attributes(
531 _("Available plugins"),
532 render, "text", 0, NULL );
533 gtk_tree_view_append_column( view, col );
534
535 list = gtk_list_store_new( 2,
536 G_TYPE_STRING,
537 G_TYPE_STRING );
538
539 for( tmp = classes; tmp; tmp = tmp->next ) {
540 PluginClass* pc = (PluginClass*)tmp->data;
541 if( ! pc->invisible ) {
542 /* FIXME: should we display invisible plugins? */
543 GtkTreeIter it;
544 gtk_list_store_append( list, &it );
545 gtk_list_store_set( list, &it,
546 0, _(pc->name),
547 1, pc->type, -1 );
548 /* g_debug( "%s (%s)", pc->type, _(pc->name) ); */
549 }
550 }
551
552 gtk_tree_view_set_model( view, GTK_TREE_MODEL(list) );
553 g_object_unref( list );
554
555 g_signal_connect( dlg, "response",
556 on_add_plugin_response, panel );
557 g_object_set_data( dlg, "avail-plugins", view );
558 g_object_weak_ref( dlg, plugin_class_list_free, classes );
559
560 gtk_window_set_default_size( (GtkWindow*)dlg, 320, 400 );
561 gtk_widget_show_all( dlg );
562}
563#endif
564
565static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
566{
567 /* panel_add_plugin( panel, panel->topgwin ); */
568 panel_configure( panel, 1 );
cf701cb7
HJYP
569}
570
571static void panel_popupmenu_remove_item( GtkMenuItem* item, Plugin* plugin )
572{
573 Panel* panel = plugin->panel;
574 panel->plugins = g_list_remove( panel->plugins, plugin );
575 plugin_stop( plugin ); /* free the plugin widget & its data */
576 plugin_put( plugin ); /* free the lib if necessary */
4b93d81e
HJYP
577
578 panel_config_save( plugin->panel );
cf701cb7
HJYP
579}
580
3e7b8eb7
HJYP
581/* FIXME: Potentially we can support multiple panels at the same edge,
582 * but currently this cannot be done due to some positioning problems. */
583static char* gen_panel_name( int edge )
584{
585 const char* edge_str = num2str( edge_pair, edge, "" );
586 char* name = NULL;
587 char* dir = get_config_file( cprofile, "panels", FALSE );
588 int i;
589 for( i = 0; i < G_MAXINT; ++i )
590 {
591 char* f;
592 if( G_LIKELY( i > 0 ) )
593 name = g_strdup_printf( "%s%d", edge_str, i );
594 else
595 name = g_strdup( edge_str );
596 f = g_build_filename( dir, name, NULL );
597 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
598 {
599 g_free( f );
600 break;
601 }
602 g_free( name );
603 g_free( f );
604 }
605 g_free( dir );
606 return name;
607}
608
609/* FIXME: Potentially we can support multiple panels at the same edge,
610 * but currently this cannot be done due to some positioning problems. */
cf701cb7
HJYP
611static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
612{
3e7b8eb7
HJYP
613 int i;
614 GSList* group = NULL;
615 GtkWidget* btns[ 4 ], *box, *frame;
616 const char* edges[]={N_("Left"), N_("Right"), N_("Top"), N_("Bottom")};
7414a73f
HJYP
617 GtkWidget* dlg = gtk_dialog_new_with_buttons(
618 _("Create New Panel"),
619 panel->topgwin,
620 GTK_DIALOG_MODAL,
621 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
622 GTK_STOCK_OK, GTK_RESPONSE_OK, NULL );
3e7b8eb7
HJYP
623 gtk_container_set_border_width( (GtkContainer*)dlg, 8 );
624 frame = gtk_frame_new( _("Where to put the panel?"));
625 gtk_box_pack_start( (GtkBox*)((GtkDialog*)dlg)->vbox, frame, TRUE, TRUE, 0 );
626 box = gtk_vbox_new( FALSE, 2 );
627 gtk_container_add( (GtkContainer*)frame, box );
628 for( i = 0; i < 4; ++i )
629 {
630 GSList* l;
631 btns[ i ] = gtk_radio_button_new_with_label( group, _(edges[i]) );
632 group = gtk_radio_button_get_group( (GtkRadioButton*)btns[ i ] );
633 gtk_box_pack_start( box, btns[ i ], FALSE, TRUE, 2 );
634 for( l = all_panels; l; l = l->next )
635 {
636 Panel* p = (Panel*)l->data;
637 /* If there is already a panel on this edge */
638 if( p->edge == (i + 1) )
639 gtk_widget_set_sensitive( btns[i], FALSE );
640 /* FIXME: multiple panel at the same edge should be supported in the future. */
641 }
642 }
643 gtk_widget_show_all( dlg );
644
645 if( gtk_dialog_run( dlg ) == GTK_RESPONSE_OK )
646 {
647 char* pfp;
648 char default_conf[128];
649 for( i = 0; i < 4; ++i )
650 {
651 if( gtk_toggle_button_get_active( (GtkToggleButton*)btns[i] ) )
652 break;
653 }
654 ++i; /* 0 is EDGE_NONE, all edge values start from 1 */
655 g_snprintf( default_conf, 128,
656 "global{\n"
657 "edge=%s\n"
658 "}\n",
659 num2str( edge_pair, i, "bottom" ) );
660 panel = g_slice_new0( Panel );
661 panel->name = gen_panel_name(i);
662 pfp = default_conf;
663 if ( panel_start( panel, &pfp )) {
664 panel_config_save( panel );
665 all_panels = g_slist_prepend( all_panels, panel );
666 }
667 else {
668 panel_destroy( panel );
669 }
670 }
7414a73f 671 gtk_widget_destroy( dlg );
cf701cb7
HJYP
672}
673
674static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
675{
676 GtkWidget* dlg;
677 gboolean ok;
678 dlg = gtk_message_dialog_new_with_markup( panel->topgwin,
679 GTK_DIALOG_MODAL,
680 GTK_MESSAGE_QUESTION,
681 GTK_BUTTONS_OK_CANCEL,
682 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
683 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
684 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
685 gtk_widget_destroy( dlg );
686 if( ok )
687 {
4b93d81e 688 gchar *fname, *dir;
cf701cb7 689 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
690
691 /* delete the config file of this panel */
692 dir = get_config_file( cprofile, "panels", FALSE );
693 fname = g_build_filename( dir, panel->name, NULL );
694 g_free( dir );
695 g_unlink( fname );
696
cf701cb7
HJYP
697 panel_destroy( panel );
698 }
699}
700
701extern GtkWidget* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
702{
703 GtkWidget *ret,*menu, *menu_item, *img;
704 char* tmp;
705 ret = menu = gtk_menu_new();
706
4b93d81e 707/*
cf701cb7
HJYP
708 img = gtk_image_new_from_stock( GTK_STOCK_ADD, GTK_ICON_SIZE_MENU );
709 menu_item = gtk_image_menu_item_new_with_label(_("Add Item To Panel"));
710 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
711 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
712 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
4b93d81e
HJYP
713*/
714
715 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
716 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
717 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
718 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
719 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
720
721 if( plugin )
722 {
723 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
724 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
725 menu_item = gtk_image_menu_item_new_with_label( tmp );
726 g_free( tmp );
727 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
728 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
729 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
730 }
731
732 menu_item = gtk_separator_menu_item_new();
733 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
734
4b93d81e
HJYP
735 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
736 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
737 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
738 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
739 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
740
cf701cb7
HJYP
741 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
742 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
743 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
744 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
745 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
3e7b8eb7
HJYP
746 /* FIXME: Potentially we can support multiple panels at the same edge,
747 * but currently this cannot be done due to some positioning problems. */
748 /* currently, disable the option when there are already four panels */
749 if( g_slist_length( all_panels ) >= 4 )
750 gtk_widget_set_sensitive( menu_item, FALSE );
cf701cb7
HJYP
751
752 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
753 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
754 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
755 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
756 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
757 if( ! all_panels->next ) /* if this is the only panel */
758 gtk_widget_set_sensitive( menu_item, FALSE );
759
cf701cb7
HJYP
760 if( use_sub_menu )
761 {
762 ret = gtk_menu_new();
763 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
764 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
765 gtk_menu_item_set_submenu( menu_item, menu );
766
767 gtk_widget_show_all(ret);
768 }
769
3e7b8eb7
HJYP
770 if( plugin )
771 {
772 menu_item = gtk_separator_menu_item_new();
773 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
774
775 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
776 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
777 menu_item = gtk_image_menu_item_new_with_label( tmp );
778 g_free( tmp );
779 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
780 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
781 if( plugin->class->config )
782 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
783 else
784 gtk_widget_set_sensitive( menu_item, FALSE );
785 }
786
787 gtk_widget_show_all(menu);
788
cf701cb7
HJYP
789 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
790 return ret;
791}
792
fddae119 793
a52c2257
HJYP
794
795/****************************************************
796 * panel creation *
797 ****************************************************/
798static void
22242ed4 799make_round_corners(Panel *p)
a52c2257 800{
a97d06a6 801 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 802 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
803}
804
22242ed4 805void panel_set_dock_type(Panel *p)
bee4c26e
HJYP
806{
807 if (p->setdocktype) {
808 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
809 XChangeProperty(GDK_DISPLAY(), p->topxwin,
810 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
811 PropModeReplace, (unsigned char *) &state, 1);
812 }
813 else {
814 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
815 }
a52c2257
HJYP
816}
817
0defe4b9 818static void
22242ed4 819panel_start_gui(Panel *p)
a52c2257
HJYP
820{
821 Atom state[3];
822 XWMHints wmhints;
823 guint32 val;
6db11841 824
a52c2257
HJYP
825 ENTER;
826
827 // main toplevel window
828 p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
829 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
830 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
831 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
832 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
833 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
834 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 835
a52c2257
HJYP
836 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
837 G_CALLBACK(panel_delete_event), p);
838 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
839 G_CALLBACK(panel_destroy_event), p);
840 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
841 (GCallback) panel_size_req, p);
842 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
843 (GCallback) panel_size_alloc, p);
844 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
845 (GCallback) panel_configure_event, p);
3e7b8eb7
HJYP
846
847 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
fddae119 848 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
8110399f 849 (GCallback) panel_press_button_event, p);
f2d54481 850
a52c2257
HJYP
851 g_signal_connect (G_OBJECT (p->topgwin), "realize",
852 (GCallback) panel_realize, p);
f2d54481 853
84fc1d55
HJYP
854 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
855 (GCallback)panel_style_set, p);
a52c2257
HJYP
856 gtk_widget_realize(p->topgwin);
857 //gdk_window_set_decorations(p->topgwin->window, 0);
2de71c90 858
4542c20d 859 // main layout manager as a single child of panel
a97d06a6 860 p->box = p->my_box_new(FALSE, 0);
a52c2257 861 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
4542c20d
HJYP
862// gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
863 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
a52c2257 864 gtk_widget_show(p->box);
a97d06a6
HJYP
865 if (p->round_corners)
866 make_round_corners(p);
6db11841 867
a52c2257
HJYP
868 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
869 DBG("topxwin = %x\n", p->topxwin);
870
871 /* the settings that should be done before window is mapped */
872 wmhints.flags = InputHint;
873 wmhints.input = 0;
bee4c26e 874 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
24053345 875#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257
HJYP
876 val = WIN_HINTS_SKIP_FOCUS;
877 XChangeProperty(GDK_DISPLAY(), p->topxwin,
878 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
879 PropModeReplace, (unsigned char *) &val, 1);
880
bee4c26e 881 panel_set_dock_type(p);
a52c2257
HJYP
882
883 /* window mapping point */
884 gtk_widget_show_all(p->topgwin);
885
886 /* the settings that should be done after window is mapped */
887
888 /* send it to running wm */
889 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
890 /* and assign it ourself just for case when wm is not running */
891 val = 0xFFFFFFFF;
892 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
893 PropModeReplace, (unsigned char *) &val, 1);
894
895 state[0] = a_NET_WM_STATE_SKIP_PAGER;
896 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
897 state[2] = a_NET_WM_STATE_STICKY;
898 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
899 32, PropModeReplace, (unsigned char *) state, 3);
900
a52c2257
HJYP
901 calculate_position(p);
902 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 903 panel_set_wm_strut(p);
77886b88 904
239cb032
HJYP
905 p->tooltips = gtk_tooltips_new();
906#if GLIB_CHECK_VERSION( 2, 10, 0 )
907 g_object_ref_sink( p->tooltips );
908#else
909 g_object_ref( p->tooltips );
910 gtk_object_sink( p->tooltips );
911#endif
912
a52c2257
HJYP
913 RET();
914}
915
22242ed4 916void panel_set_orientation(Panel *p)
a97d06a6
HJYP
917{
918 GList* l;
919 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
920 ? ORIENT_HORIZ : ORIENT_VERT;
921 if (p->orientation == ORIENT_HORIZ) {
922 p->my_box_new = gtk_hbox_new;
923 p->my_separator_new = gtk_vseparator_new;
924 } else {
925 p->my_box_new = gtk_vbox_new;
926 p->my_separator_new = gtk_hseparator_new;
927 }
928
929 /* recreate the main layout box */
930 if( p->box ) {
5a343ad5
JH
931 GtkBox* newbox = GTK_BOX(recreate_box( GTK_BOX(p->box), p->orientation ));
932 if( GTK_WIDGET(newbox) != p->box ) {
933 p->box = GTK_WIDGET(newbox);
4542c20d 934 gtk_container_add( GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox) );
a97d06a6
HJYP
935 }
936 }
937 /* NOTE: This loop won't be executed when panel started since
938 plugins are not loaded at that time.
939 This is used when the orientation of the panel is changed
940 from the config dialog, and plugins should be re-layout.
941 */
942 for( l = p->plugins; l; l = l->next ) {
22242ed4 943 Plugin* pl = (Plugin*)l->data;
a97d06a6
HJYP
944 if( pl->class->orientation ) {
945 pl->class->orientation( pl );
946 }
947 }
948}
949
a52c2257 950static int
22242ed4 951panel_parse_global(Panel *p, char **fp)
a52c2257
HJYP
952{
953 line s;
954 s.len = 256;
6db11841 955
3e7b8eb7
HJYP
956 if( G_LIKELY( fp ) )
957 {
958 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
959 if (s.type == LINE_VAR) {
960 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
961 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
962 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
963 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
964 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
965 p->margin = atoi(s.t[1]);
966 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
967 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
968 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
969 p->width = atoi(s.t[1]);
970 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
971 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
972 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
973 p->height = atoi(s.t[1]);
974 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
975 p->spacing = atoi(s.t[1]);
976 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
977 p->setdocktype = str2num(bool_pair, s.t[1], 0);
978 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
979 p->setstrut = str2num(bool_pair, s.t[1], 0);
980 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
981 p->round_corners = str2num(bool_pair, s.t[1], 0);
982 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
983 p->transparent = str2num(bool_pair, s.t[1], 0);
984 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
985 p->alpha = atoi(s.t[1]);
986 if (p->alpha > 255)
987 p->alpha = 255;
988 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
989 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
990 gdk_color_parse ("white", &p->gtintcolor);
991 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
992 DBG("tintcolor=%x\n", p->tintcolor);
993 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
994 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
995 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
996 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
997 gdk_color_parse ("black", &p->gfontcolor);
998 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
999 DBG("fontcolor=%x\n", p->fontcolor);
1000 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1001 p->background = str2num(bool_pair, s.t[1], 0);
1002 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1003 p->background_file = g_strdup( s.t[1] );
1004 } else {
1005 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
1006 RET(0);
1007 }
1008 } else if (s.type == LINE_BLOCK_END) {
1009 break;
a52c2257 1010 } else {
3e7b8eb7 1011 ERR( "lxpanel: illegal in this context %s\n", s.str);
a52c2257
HJYP
1012 RET(0);
1013 }
a52c2257
HJYP
1014 }
1015 }
a97d06a6 1016 panel_set_orientation( p );
4542c20d 1017
a52c2257
HJYP
1018 if (p->width < 0)
1019 p->width = 100;
1020 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
1021 p->width = 100;
1022 p->heighttype = HEIGHT_PIXEL;
1023 if (p->heighttype == HEIGHT_PIXEL) {
1024 if (p->height < PANEL_HEIGHT_MIN)
1025 p->height = PANEL_HEIGHT_MIN;
1026 else if (p->height > PANEL_HEIGHT_MAX)
1027 p->height = PANEL_HEIGHT_MAX;
1028 }
2de71c90
FC
1029
1030 if (p->background)
1031 p->transparent = 0;
1032
a52c2257
HJYP
1033 p->curdesk = get_net_current_desktop();
1034 p->desknum = get_net_number_of_desktops();
1035 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1d9dc649 1036 /* print_wmdata(p); */
239cb032 1037
a52c2257
HJYP
1038 panel_start_gui(p);
1039 RET(1);
1040}
1041
1042static int
22242ed4 1043panel_parse_plugin(Panel *p, char **fp)
a52c2257
HJYP
1044{
1045 line s;
22242ed4 1046 Plugin *plug = NULL;
a52c2257 1047 gchar *type = NULL;
a52c2257 1048 int expand , padding, border;
db449f6e
HJYP
1049 char* pconfig = NULL;
1050
a52c2257
HJYP
1051 ENTER;
1052 s.len = 256;
a52c2257 1053 border = expand = padding = 0;
c69ac68e 1054 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
1055 if (s.type == LINE_NONE) {
1056 ERR( "lxpanel: bad line %s\n", s.str);
1057 goto error;
1058 }
1059 if (s.type == LINE_VAR) {
1060 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1061 type = g_strdup(s.t[1]);
1062 DBG("plug %s\n", type);
1063 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1064 expand = str2num(bool_pair, s.t[1], 0);
1065 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1066 padding = atoi(s.t[1]);
1067 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1068 border = atoi(s.t[1]);
1069 else {
1070 ERR( "lxpanel: unknown var %s\n", s.t[0]);
1071 goto error;
1072 }
1073 } else if (s.type == LINE_BLOCK_START) {
1074 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 1075 pconfig = *fp;
a52c2257
HJYP
1076 int pno = 1;
1077 while (pno) {
1078 get_line_as_is(fp, &s);
1079 if (s.type == LINE_NONE) {
1080 ERR( "lxpanel: unexpected eof\n");
1081 goto error;
1082 } else if (s.type == LINE_BLOCK_START) {
1083 pno++;
1084 } else if (s.type == LINE_BLOCK_END) {
1085 pno--;
bee4c26e 1086 }
db449f6e 1087 }
a52c2257
HJYP
1088 } else {
1089 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1090 goto error;
1091 }
1092 } else {
1093 ERR( "lxpanel: illegal in this context %s\n", s.str);
1094 goto error;
1095 }
1096 }
db449f6e 1097
a52c2257
HJYP
1098 if (!type || !(plug = plugin_load(type))) {
1099 ERR( "lxpanel: can't load %s plugin\n", type);
1100 goto error;
1101 }
db449f6e 1102
a52c2257 1103 plug->panel = p;
a52c2257
HJYP
1104 plug->expand = expand;
1105 plug->padding = padding;
1106 plug->border = border;
a52c2257 1107 DBG("starting\n");
db449f6e 1108 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
1109 ERR( "lxpanel: can't start plugin %s\n", type);
1110 goto error;
1111 }
1112 DBG("plug %s\n", type);
1113 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
1114
1115 g_free( type );
a52c2257 1116 RET(1);
db449f6e 1117
a52c2257 1118 error:
a52c2257
HJYP
1119 g_free(type);
1120 if (plug)
1121 plugin_put(plug);
1122 RET(0);
a52c2257
HJYP
1123}
1124
3e7b8eb7 1125int panel_start( Panel *p, char **fp )
a52c2257
HJYP
1126{
1127 line s;
db449f6e 1128
a52c2257
HJYP
1129 /* parse global section */
1130 ENTER;
1131 s.len = 256;
8110399f 1132
a52c2257
HJYP
1133 p->allign = ALLIGN_CENTER;
1134 p->edge = EDGE_BOTTOM;
1135 p->widthtype = WIDTH_PERCENT;
1136 p->width = 100;
1137 p->heighttype = HEIGHT_PIXEL;
1138 p->height = PANEL_HEIGHT_DEFAULT;
1139 p->setdocktype = 1;
1140 p->setstrut = 1;
1141 p->round_corners = 0;
1142 p->transparent = 0;
1143 p->alpha = 127;
1144 p->tintcolor = 0xFFFFFFFF;
2de71c90
FC
1145 p->usefontcolor = 0;
1146 p->fontcolor = 0x00000000;
a52c2257 1147 p->spacing = 0;
22242ed4 1148
c69ac68e 1149 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
1150 ERR( "lxpanel: config file must start from Global section\n");
1151 RET(0);
1152 }
1153 if (!panel_parse_global(p, fp))
1154 RET(0);
1155
c69ac68e 1156 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
1157 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1158 ERR( "lxpanel: expecting Plugin section\n");
1159 RET(0);
1160 }
9939506e 1161 panel_parse_plugin(p, fp);
a52c2257
HJYP
1162 }
1163 gtk_widget_show_all(p->topgwin);
4542c20d
HJYP
1164
1165 /* update backgrond of panel and all plugins */
1166 panel_update_background( p );
1167
1d9dc649 1168 /* print_wmdata(p); */
a52c2257
HJYP
1169 RET(1);
1170}
1171
1172static void
1173delete_plugin(gpointer data, gpointer udata)
1174{
1175 ENTER;
22242ed4
HJYP
1176 plugin_stop((Plugin *)data);
1177 plugin_put((Plugin *)data);
a52c2257 1178 RET();
a52c2257
HJYP
1179}
1180
8110399f 1181void panel_destroy(Panel *p)
a52c2257
HJYP
1182{
1183 ENTER;
1184
1185 g_list_foreach(p->plugins, delete_plugin, NULL);
1186 g_list_free(p->plugins);
1187 p->plugins = NULL;
8c44345a 1188
5297da29 1189 if( p->system_menus ){
8c44345a 1190 do{
5297da29 1191 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
1192 }
1193
4b93d81e
HJYP
1194 if( p->tooltips )
1195 g_object_unref( p->tooltips );
239cb032 1196
4b93d81e
HJYP
1197 if( p->topgwin )
1198 gtk_widget_destroy(p->topgwin);
a52c2257 1199 g_free(p->workarea);
2de71c90 1200 g_free( p->background_file );
5297da29 1201 g_slist_free( p->system_menus );
a52c2257
HJYP
1202 gdk_flush();
1203 XFlush(GDK_DISPLAY());
1204 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1205
1206 g_free( p->name );
cf701cb7 1207 g_slice_free( Panel, p );
a52c2257
HJYP
1208 RET();
1209}
1210
8110399f
HJYP
1211Panel* panel_new( const char* config_file, const char* config_name )
1212{
1213 char *fp, *pfp; /* point to current position of profile data in memory */
cf701cb7 1214 Panel* panel = NULL;
8110399f 1215 char* ret;
3e7b8eb7 1216 if( G_LIKELY(config_file) )
cf701cb7 1217 {
3e7b8eb7
HJYP
1218 g_file_get_contents( config_file, &fp, NULL, NULL );
1219 if( fp )
1220 {
1221 panel = g_slice_new0( Panel );
1222 panel->name = g_strdup( config_name );
1223 pfp = fp;
1224
1225 if (! panel_start( panel, &pfp )) {
1226 ERR( "lxpanel: can't start panel\n");
1227 panel_destroy( panel );
1228 panel = NULL;
1229 }
cf701cb7 1230
3e7b8eb7 1231 g_free( fp );
cf701cb7 1232 }
cf701cb7
HJYP
1233 }
1234 return panel;
8110399f 1235}
a52c2257 1236
0defe4b9 1237static void
a52c2257
HJYP
1238usage()
1239{
e7cb732b
HJYP
1240 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1241 g_print(_("Command line options:\n"));
1242 g_print(_(" --help -- print this help and exit\n"));
1243 g_print(_(" --version -- print version and exit\n"));
1244 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
1245 g_print(_(" --configure -- launch configuration utility\n"));
1246 g_print(_(" --profile name -- use specified profile\n"));
1247 g_print("\n");
1248 g_print(_(" -h -- same as --help\n"));
1249 g_print(_(" -p -- same as --profile\n"));
1250 g_print(_(" -v -- same as --version\n"));
1251 g_print(_(" -C -- same as --configure\n"));
b37c9aae 1252 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1253}
1254
e68b47dc 1255static void
a52c2257
HJYP
1256handle_error(Display * d, XErrorEvent * ev)
1257{
1258 char buf[256];
1259
1260 ENTER;
1261 if (log_level >= LOG_WARN) {
1262 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1263 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1264 }
1265 RET();
1266}
1267
e68b47dc
JH
1268/* Lightweight lock related functions - X clipboard hacks */
1269
1270#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1271
1272/*
1273 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1274 */
1275static void
1276clipboard_get_func(
1277 GtkClipboard *clipboard G_GNUC_UNUSED,
1278 GtkSelectionData *selection_data G_GNUC_UNUSED,
1279 guint info G_GNUC_UNUSED,
1280 gpointer user_data_or_owner G_GNUC_UNUSED)
1281{
1282}
1283
1284/*
1285 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1286 */
1287static void clipboard_clear_func(
1288 GtkClipboard *clipboard G_GNUC_UNUSED,
1289 gpointer user_data_or_owner G_GNUC_UNUSED)
1290{
1291}
1292
1293/*
1294 * Lightweight version for checking single instance.
1295 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1296 *
1297 * Returns TRUE if successfully retrieved and FALSE otherwise.
1298 */
16fb8c2e 1299static gboolean check_main_lock()
e68b47dc
JH
1300{
1301 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1302 gboolean retval = FALSE;
1303 GtkClipboard *clipboard;
1304 Atom atom;
1305
1306 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1307
1308 XGrabServer(GDK_DISPLAY());
1309
1310 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1311 goto out;
1312
1313 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1314
1315 if (gtk_clipboard_set_with_data(clipboard, targets,
1316 G_N_ELEMENTS (targets),
1317 clipboard_get_func,
1318 clipboard_clear_func, NULL))
1319 retval = TRUE;
1320
1321out:
1322 XUngrabServer (GDK_DISPLAY ());
1323 gdk_flush ();
1324
1325 return retval;
1326}
1327#undef CLIPBOARD_NAME
1328
8110399f
HJYP
1329static gboolean start_all_panels( )
1330{
cf701cb7 1331 gboolean is_global;
4b93d81e 1332 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
cf701cb7
HJYP
1333 {
1334 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1335 GDir* dir = g_dir_open( panel_dir, 0, NULL );
1336 char* name;
1337
1338 if( ! dir )
1339 {
1340 g_free( panel_dir );
1341 continue;
1342 }
1343
1344 while( name = g_dir_read_name( dir ) )
1345 {
1346 char* panel_config = g_build_filename( panel_dir, name, NULL );
1347 Panel* panel = panel_new( panel_config, name );
1348 if( panel )
1349 all_panels = g_slist_prepend( all_panels, panel );
1350 g_free( panel_config );
1351 }
1352 g_dir_close( dir );
1353 g_free( panel_dir );
1354 }
1355 return all_panels != NULL;
8110399f
HJYP
1356}
1357
cf701cb7
HJYP
1358void load_global_config();
1359void free_global_config();
1360
8110399f 1361int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1362{
1363 int i;
f277dbb7 1364
a52c2257 1365 setlocale(LC_CTYPE, "");
f277dbb7 1366
a52c2257
HJYP
1367 gtk_init(&argc, &argv);
1368
1369#ifdef ENABLE_NLS
1370 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1371 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1372 textdomain ( GETTEXT_PACKAGE );
1373#endif
1374
1375 XSetLocaleModifiers("");
1376 XSetErrorHandler((XErrorHandler) handle_error);
8110399f 1377
a52c2257 1378 resolve_atoms();
8110399f 1379
a52c2257
HJYP
1380 for (i = 1; i < argc; i++) {
1381 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1382 usage();
1383 exit(0);
1384 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1385 printf("lxpanel %s\n", version);
1386 exit(0);
1387 } else if (!strcmp(argv[i], "--log")) {
1388 i++;
1389 if (i == argc) {
1390 ERR( "lxpanel: missing log level\n");
1391 usage();
1392 exit(1);
1393 } else {
1394 log_level = atoi(argv[i]);
1395 }
1396 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1397 config = 1;
1398 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1399 i++;
1400 if (i == argc) {
1401 ERR( "lxpanel: missing profile name\n");
1402 usage();
1403 exit(1);
1404 } else {
1405 cprofile = g_strdup(argv[i]);
1406 }
1407 } else {
1408 printf("lxpanel: unknown option - %s\n", argv[i]);
1409 usage();
1410 exit(1);
1411 }
1412 }
f277dbb7 1413
8110399f 1414 /* Check for duplicated lxpanel instances */
e68b47dc
JH
1415 if (!check_main_lock() && !config) {
1416 printf("There is alreay an instance of LXPanel. Now to exit\n");
1417 exit(1);
1418 }
1419
f277dbb7
HJYP
1420 /* Add our own icons to the search path of icon theme */
1421 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(),
1422 PACKAGE_DATA_DIR "/lxpanel/images" );
1423
cf701cb7 1424 fbev = fb_ev_new();
22242ed4 1425
f7cb330e
HJYP
1426restart:
1427 is_restarting = FALSE;
1428
cf701cb7
HJYP
1429 load_global_config();
1430
1d9dc649
HJYP
1431 /* NOTE: StructureNotifyMask is required by XRandR
1432 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1433 */
1434 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
cf701cb7 1435 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1436
cf701cb7
HJYP
1437 if( G_UNLIKELY( ! start_all_panels() ) )
1438 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1439/*
1440 * FIXME: configure??
6a6ad54e
HJYP
1441 if (config)
1442 configure();
8110399f 1443*/
6a6ad54e 1444 gtk_main();
8110399f 1445
cf701cb7
HJYP
1446 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1447 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1448
8110399f
HJYP
1449 /* destroy all panels */
1450 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1451 g_slist_free( all_panels );
1452 all_panels = NULL;
6a6ad54e 1453 g_free( cfgfile );
5541b8d2 1454
cf701cb7
HJYP
1455 free_global_config();
1456
f7cb330e
HJYP
1457 if( is_restarting )
1458 goto restart;
1459
22242ed4
HJYP
1460 g_object_unref(fbev);
1461
5541b8d2 1462 return 0;
a52c2257 1463}