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