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