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