plugins: assign PluginClass in a C99 compatible fashion
[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
77886b88 39#include "lxpanelctl.h"
cf701cb7 40#include "dbg.h"
77886b88 41
a52c2257
HJYP
42static gchar *cfgfile = NULL;
43static gchar version[] = VERSION;
44gchar *cprofile = "default";
45
e2957bd2
HJYP
46static GtkWindowGroup* win_grp; /* window group used to limit the scope of model dialog. */
47
e68b47dc 48static int config = 0;
22242ed4 49FbEv *fbev = NULL;
a52c2257 50
cf701cb7 51GSList* all_panels = NULL; /* a single-linked list storing all panels */
a52c2257 52
f7cb330e
HJYP
53gboolean is_restarting = FALSE;
54
3e7b8eb7 55static int panel_start( Panel *p, char **fp );
9dd114c4 56static void panel_start_gui(Panel *p);
4b93d81e
HJYP
57void panel_config_save(Panel* panel); /* defined in configurator.c */
58
95095259
HJYP
59gboolean is_in_lxde = FALSE;
60
cb22c87c
HJYP
61/* A hack used to be compatible with Gnome panel for gtk+ themes.
62 * Some gtk+ themes define special styles for desktop panels.
63 * http://live.gnome.org/GnomeArt/Tutorials/GtkThemes/GnomePanel
64 * So we make a derived class from GtkWindow named PanelToplevel
65 * and create the panels with it to be compatible with Gnome themes.
66 */
67#define PANEL_TOPLEVEL_TYPE (panel_toplevel_get_type())
68
69typedef struct _PanelToplevel PanelToplevel;
70typedef struct _PanelToplevelClass PanelToplevelClass;
71struct _PanelToplevel
72{
73 GtkWindow parent;
74};
75struct _PanelToplevelClass
76{
77 GtkWindowClass parent_class;
78};
79G_DEFINE_TYPE(PanelToplevel, panel_toplevel, GTK_TYPE_WINDOW);
80static void panel_toplevel_class_init(PanelToplevelClass *klass)
81{
82}
83static void panel_toplevel_init(PanelToplevel *self)
84{
85}
86
9dd114c4 87/* Allocate and initialize new Panel structure. */
88static Panel* panel_allocate(void)
89{
2918994e 90 Panel* p = g_new0(Panel, 1);
9dd114c4 91 p->allign = ALLIGN_CENTER;
92 p->edge = EDGE_NONE;
93 p->widthtype = WIDTH_PERCENT;
94 p->width = 100;
95 p->heighttype = HEIGHT_PIXEL;
96 p->height = PANEL_HEIGHT_DEFAULT;
97 p->setdocktype = 1;
98 p->setstrut = 1;
99 p->round_corners = 0;
100 p->autohide = 0;
101 p->visible = TRUE;
102 p->height_when_hidden = 2;
103 p->transparent = 0;
bf2140f8
MJ
104 p->alpha = 255;
105 gdk_color_parse("white", &p->gtintcolor);
106 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
9dd114c4 107 p->usefontcolor = 0;
108 p->fontcolor = 0x00000000;
1869ef90
LK
109 p->usefontsize = 0;
110 p->fontsize = 10;
9dd114c4 111 p->spacing = 0;
8f9e6256 112 p->icon_size = PANEL_ICON_SIZE;
21f91705 113 p->icon_theme = gtk_icon_theme_get_default();
9dd114c4 114 return p;
115}
116
117/* Normalize panel configuration after load from file or reconfiguration. */
118static void panel_normalize_configuration(Panel* p)
119{
2918994e 120 panel_set_panel_configuration_changed( p );
9dd114c4 121 if (p->width < 0)
122 p->width = 100;
123 if (p->widthtype == WIDTH_PERCENT && p->width > 100)
124 p->width = 100;
125 p->heighttype = HEIGHT_PIXEL;
126 if (p->heighttype == HEIGHT_PIXEL) {
127 if (p->height < PANEL_HEIGHT_MIN)
128 p->height = PANEL_HEIGHT_MIN;
129 else if (p->height > PANEL_HEIGHT_MAX)
130 p->height = PANEL_HEIGHT_MAX;
131 }
132 if (p->background)
133 p->transparent = 0;
134}
135
a52c2257
HJYP
136/****************************************************
137 * panel's handlers for WM events *
138 ****************************************************/
a52c2257 139
22242ed4 140void panel_set_wm_strut(Panel *p)
a52c2257 141{
d1d43629 142 int index;
143 gulong strut_size;
144 gulong strut_lower;
145 gulong strut_upper;
a52c2257 146
d1d43629 147 /* Dispatch on edge to set up strut parameters. */
148 switch (p->edge)
bee4c26e 149 {
d1d43629 150 case EDGE_LEFT:
151 index = 0;
152 strut_size = p->aw;
153 strut_lower = p->ay;
154 strut_upper = p->ay + p->ah;
155 break;
156 case EDGE_RIGHT:
157 index = 1;
158 strut_size = p->aw;
159 strut_lower = p->ay;
160 strut_upper = p->ay + p->ah;
161 break;
162 case EDGE_TOP:
163 index = 2;
164 strut_size = p->ah;
165 strut_lower = p->ax;
166 strut_upper = p->ax + p->aw;
167 break;
168 case EDGE_BOTTOM:
169 index = 3;
170 strut_size = p->ah;
171 strut_lower = p->ax;
172 strut_upper = p->ax + p->aw;
173 break;
174 default:
175 return;
bee4c26e
HJYP
176 }
177
176fb687 178 /* Handle autohide case. EWMH recommends having the strut be the minimized size. */
92955ae5 179 if (p->autohide)
176fb687 180 strut_size = p->height_when_hidden;
181
d1d43629 182 /* Set up strut value in property format. */
183 gulong desired_strut[12];
184 memset(desired_strut, 0, sizeof(desired_strut));
185 if (p->setstrut)
186 {
187 desired_strut[index] = strut_size;
188 desired_strut[4 + index * 2] = strut_lower;
189 desired_strut[5 + index * 2] = strut_upper;
190 }
191 else
192 {
193 strut_size = 0;
194 strut_lower = 0;
195 strut_upper = 0;
bee4c26e 196 }
bee4c26e 197
d1d43629 198 /* If strut value changed, set the property value on the panel window.
199 * This avoids property change traffic when the panel layout is recalculated but strut geometry hasn't changed. */
200 if ((GTK_WIDGET_MAPPED(p->topgwin))
547ece89 201 && ((p->strut_size != strut_size) || (p->strut_lower != strut_lower) || (p->strut_upper != strut_upper) || (p->strut_edge != p->edge)))
d1d43629 202 {
203 p->strut_size = strut_size;
204 p->strut_lower = strut_lower;
205 p->strut_upper = strut_upper;
547ece89 206 p->strut_edge = p->edge;
a52c2257 207
d1d43629 208 /* If window manager supports STRUT_PARTIAL, it will ignore STRUT.
209 * Set STRUT also for window managers that do not support STRUT_PARTIAL. */
210 if (strut_size != 0)
211 {
212 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL,
213 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 12);
214 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT,
215 XA_CARDINAL, 32, PropModeReplace, (unsigned char *) desired_strut, 4);
216 }
217 else
218 {
219 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT);
220 XDeleteProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STRUT_PARTIAL);
221 }
222 }
a52c2257
HJYP
223}
224
e996608e 225/* defined in plugins/menu.c */
8c44345a 226gboolean show_system_menu( gpointer system_menu );
e996608e 227
3e7b8eb7
HJYP
228/* defined in configurator.c */
229void panel_configure(Panel* p, int sel_page );
9dd114c4 230gboolean panel_edge_available(Panel* p, int edge);
3e7b8eb7 231
e996608e 232/* built-in commands, defined in configurator.c */
77886b88
HJYP
233void restart(void);
234void gtk_run(void);
e3b89f43 235void panel_destroy(Panel *p);
77886b88 236
d18c9409 237static void process_client_msg ( XClientMessageEvent* ev )
77886b88 238{
8c44345a 239 int cmd = ev->data.b[0];
77886b88
HJYP
240 switch( cmd )
241 {
ace2a572 242#ifndef DISABLE_MENU
77886b88 243 case LXPANEL_CMD_SYS_MENU:
d18c9409
HJYP
244 {
245 GSList* l;
246 for( l = all_panels; l; l = l->next )
8c44345a 247 {
d18c9409
HJYP
248 Panel* p = (Panel*)l->data;
249 if( p->system_menus )
250 {
251 /* show_system_menu( p->system_menus->data ); */
252 /* FIXME: I've no idea why this doesn't work without timeout
253 under some WMs, like icewm. */
254 g_timeout_add( 200, (GSourceFunc)show_system_menu,
255 p->system_menus->data );
256 }
8c44345a 257 }
77886b88 258 break;
d18c9409 259 }
ace2a572
JH
260#endif
261#ifndef DISABLE_MENU
77886b88
HJYP
262 case LXPANEL_CMD_RUN:
263 gtk_run();
264 break;
ace2a572 265#endif
77886b88 266 case LXPANEL_CMD_CONFIG:
0c5f948f
MJ
267 {
268 Panel * p = ((all_panels != NULL) ? all_panels->data : NULL);
269 if (p != NULL)
270 panel_configure(p, 0);
271 }
77886b88
HJYP
272 break;
273 case LXPANEL_CMD_RESTART:
274 restart();
275 break;
276 case LXPANEL_CMD_EXIT:
277 gtk_main_quit();
278 break;
279 }
280}
281
a52c2257 282static GdkFilterReturn
cf701cb7 283panel_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer not_used)
a52c2257
HJYP
284{
285 Atom at;
286 Window win;
287 XEvent *ev = (XEvent *) xevent;
288
289 ENTER;
290 DBG("win = 0x%x\n", ev->xproperty.window);
53b37ef2
HJYP
291 if (ev->type != PropertyNotify )
292 {
77886b88
HJYP
293 /* private client message from lxpanelctl */
294 if( ev->type == ClientMessage && ev->xproperty.atom == a_LXPANEL_CMD )
295 {
d18c9409 296 process_client_msg( (XClientMessageEvent*)ev );
77886b88 297 }
22242ed4
HJYP
298 else if( ev->type == DestroyNotify )
299 {
cf701cb7 300 fb_ev_emit_destroy( fbev, ((XDestroyWindowEvent*)ev)->window );
22242ed4 301 }
a52c2257 302 RET(GDK_FILTER_CONTINUE);
24053345 303 }
77886b88 304
a52c2257
HJYP
305 at = ev->xproperty.atom;
306 win = ev->xproperty.window;
53b37ef2
HJYP
307 if (win == GDK_ROOT_WINDOW())
308 {
309 if (at == a_NET_CLIENT_LIST)
310 {
311 fb_ev_emit(fbev, EV_CLIENT_LIST);
312 }
313 else if (at == a_NET_CURRENT_DESKTOP)
314 {
cf701cb7
HJYP
315 GSList* l;
316 for( l = all_panels; l; l = l->next )
317 ((Panel*)l->data)->curdesk = get_net_current_desktop();
22242ed4 318 fb_ev_emit(fbev, EV_CURRENT_DESKTOP);
53b37ef2
HJYP
319 }
320 else if (at == a_NET_NUMBER_OF_DESKTOPS)
321 {
cf701cb7
HJYP
322 GSList* l;
323 for( l = all_panels; l; l = l->next )
324 ((Panel*)l->data)->desknum = get_net_number_of_desktops();
22242ed4 325 fb_ev_emit(fbev, EV_NUMBER_OF_DESKTOPS);
53b37ef2
HJYP
326 }
327 else if (at == a_NET_DESKTOP_NAMES)
328 {
22242ed4 329 fb_ev_emit(fbev, EV_DESKTOP_NAMES);
53b37ef2
HJYP
330 }
331 else if (at == a_NET_ACTIVE_WINDOW)
332 {
22242ed4 333 fb_ev_emit(fbev, EV_ACTIVE_WINDOW );
53b37ef2
HJYP
334 }
335 else if (at == a_NET_CLIENT_LIST_STACKING)
336 {
22242ed4 337 fb_ev_emit(fbev, EV_CLIENT_LIST_STACKING);
53b37ef2
HJYP
338 }
339 else if (at == a_XROOTPMAP_ID)
340 {
cf701cb7
HJYP
341 GSList* l;
342 for( l = all_panels; l; l = l->next )
343 {
344 Panel* p = (Panel*)l->data;
345 if (p->transparent) {
346 fb_bg_notify_changed_bg(p->bg);
347 }
348 }
53b37ef2
HJYP
349 }
350 else if (at == a_NET_WORKAREA)
351 {
cf701cb7
HJYP
352 GSList* l;
353 for( l = all_panels; l; l = l->next )
354 {
355 Panel* p = (Panel*)l->data;
356 g_free( p->workarea );
357 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
1d9dc649 358 /* print_wmdata(p); */
a52c2257 359 }
53b37ef2
HJYP
360 }
361 else
cf701cb7
HJYP
362 return GDK_FILTER_CONTINUE;
363
364 return GDK_FILTER_REMOVE;
a52c2257 365 }
cf701cb7 366 return GDK_FILTER_CONTINUE;
a52c2257
HJYP
367}
368
369/****************************************************
370 * panel's handlers for GTK events *
371 ****************************************************/
372
bee4c26e 373
a52c2257
HJYP
374static gint
375panel_delete_event(GtkWidget * widget, GdkEvent * event, gpointer data)
376{
377 ENTER;
378 RET(FALSE);
379}
380
381static gint
382panel_destroy_event(GtkWidget * widget, GdkEvent * event, gpointer data)
383{
22242ed4 384 //Panel *p = (Panel *) data;
a52c2257
HJYP
385 //if (!p->self_destroy)
386 gtk_main_quit();
387 RET(FALSE);
388}
389
4542c20d 390static void
22242ed4 391on_root_bg_changed(FbBg *bg, Panel* p)
4542c20d
HJYP
392{
393 panel_update_background( p );
394}
395
2918994e 396void panel_determine_background_pixmap(Panel * p, GtkWidget * widget, GdkWindow * window)
4542c20d 397{
2918994e 398 GdkPixmap * pixmap = NULL;
4542c20d 399
2918994e 400 /* Free p->bg if it is not going to be used. */
401 if (( ! p->transparent) && (p->bg != NULL))
402 {
403 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
404 g_object_unref(p->bg);
405 p->bg = NULL;
406 }
4542c20d 407
2918994e 408 if (p->background)
409 {
410 /* User specified background pixmap. */
411 if (p->background_file != NULL)
412 pixmap = fb_bg_get_pix_from_file(widget, p->background_file);
4542c20d 413 }
2918994e 414
415 else if (p->transparent)
4542c20d 416 {
2918994e 417 /* Transparent. Determine the appropriate value from the root pixmap. */
418 if (p->bg == NULL)
4542c20d 419 {
2918994e 420 p->bg = fb_bg_get_for_display();
421 g_signal_connect(G_OBJECT(p->bg), "changed", G_CALLBACK(on_root_bg_changed), p);
4542c20d 422 }
2918994e 423 pixmap = fb_bg_get_xroot_pix_for_win(p->bg, widget);
424 if ((pixmap != NULL) && (pixmap != GDK_NO_BG) && (p->alpha != 0))
938806f9 425 fb_bg_composite(pixmap, &p->gtintcolor, p->alpha);
4542c20d
HJYP
426 }
427
2918994e 428 if (pixmap != NULL)
4542c20d 429 {
2918994e 430 gtk_widget_set_app_paintable(widget, TRUE );
431 gdk_window_set_back_pixmap(window, pixmap, FALSE);
432 g_object_unref(pixmap);
4542c20d
HJYP
433 }
434 else
2918994e 435 gtk_widget_set_app_paintable(widget, FALSE);
436}
4542c20d 437
2918994e 438/* Update the background of the entire panel.
439 * This function should only be called after the panel has been realized. */
440void panel_update_background(Panel * p)
441{
442 /* Redraw the top level widget. */
443 panel_determine_background_pixmap(p, p->topgwin, p->topgwin->window);
444 gdk_window_clear(p->topgwin->window);
445 gtk_widget_queue_draw(p->topgwin);
2918994e 446
447 /* Loop over all plugins redrawing each plugin. */
448 GList * l;
449 for (l = p->plugins; l != NULL; l = l->next)
4542c20d 450 {
2918994e 451 Plugin * pl = (Plugin *) l->data;
964b8b7e 452 if (pl->pwid != NULL)
453 plugin_widget_set_background(pl->pwid, p);
4542c20d
HJYP
454 }
455
4542c20d 456}
a52c2257 457
84fc1d55
HJYP
458static gboolean delay_update_background( Panel* p )
459{
6589616e
JH
460 /* Panel could be destroyed while background update scheduled */
461 if ( p->topgwin && GTK_WIDGET_REALIZED ( p->topgwin ) ) {
462 gdk_display_sync( gtk_widget_get_display(p->topgwin) );
463 panel_update_background( p );
464 }
465
cf701cb7 466 return FALSE;
84fc1d55
HJYP
467}
468
469static void
f2d54481
HJYP
470panel_realize(GtkWidget *widget, Panel *p)
471{
e3b89f43 472 g_idle_add_full( G_PRIORITY_LOW,
473 (GSourceFunc)delay_update_background, p, NULL );
f2d54481
HJYP
474}
475
476static void
84fc1d55
HJYP
477panel_style_set(GtkWidget *widget, GtkStyle* prev, Panel *p)
478{
0987f648 479 /* FIXME: This dirty hack is used to fix the background of systray... */
cf701cb7 480 if( GTK_WIDGET_REALIZED( widget ) )
e3b89f43 481 g_idle_add_full( G_PRIORITY_LOW,
482 (GSourceFunc)delay_update_background, p, NULL );
84fc1d55 483}
4542c20d 484
a52c2257 485static gint
22242ed4 486panel_size_req(GtkWidget *widget, GtkRequisition *req, Panel *p)
a52c2257
HJYP
487{
488 ENTER;
4542c20d 489
b957082c
JC
490 if (p->autohide && !p->visible)
491 /* When the panel is in invisible state, the content box also got hidden, thus always
492 * report 0 size. Ask the content box instead for its size. */
493 gtk_widget_size_request(p->box, req);
494
a52c2257
HJYP
495 if (p->widthtype == WIDTH_REQUEST)
496 p->width = (p->orientation == ORIENT_HORIZ) ? req->width : req->height;
497 if (p->heighttype == HEIGHT_REQUEST)
498 p->height = (p->orientation == ORIENT_HORIZ) ? req->height : req->width;
b957082c 499
a52c2257 500 calculate_position(p);
b957082c
JC
501
502 gtk_widget_set_size_request( widget, p->aw, p->ah );
4542c20d 503
a52c2257
HJYP
504 RET( TRUE );
505}
506
507static gint
22242ed4 508panel_size_alloc(GtkWidget *widget, GtkAllocation *a, Panel *p)
a52c2257
HJYP
509{
510 ENTER;
a52c2257
HJYP
511 if (p->widthtype == WIDTH_REQUEST)
512 p->width = (p->orientation == ORIENT_HORIZ) ? a->width : a->height;
513 if (p->heighttype == HEIGHT_REQUEST)
514 p->height = (p->orientation == ORIENT_HORIZ) ? a->height : a->width;
515 calculate_position(p);
4542c20d 516
a52c2257 517 if (a->width == p->aw && a->height == p->ah && a->x == p->ax && a->y == p ->ay) {
a52c2257
HJYP
518 RET(TRUE);
519 }
520
521 gtk_window_move(GTK_WINDOW(p->topgwin), p->ax, p->ay);
bee4c26e 522 panel_set_wm_strut(p);
a52c2257
HJYP
523 RET(TRUE);
524}
525
a52c2257 526static gboolean
22242ed4 527panel_configure_event (GtkWidget *widget, GdkEventConfigure *e, Panel *p)
a52c2257
HJYP
528{
529 ENTER;
530 if (e->width == p->cw && e->height == p->ch && e->x == p->cx && e->y == p->cy)
531 RET(TRUE);
532 p->cw = e->width;
533 p->ch = e->height;
534 p->cx = e->x;
535 p->cy = e->y;
4542c20d 536
a52c2257
HJYP
537 if (p->transparent)
538 fb_bg_notify_changed_bg(p->bg);
bee4c26e 539
4542c20d 540 RET(FALSE);
a52c2257
HJYP
541}
542
fddae119
FC
543static gint
544panel_popupmenu_configure(GtkWidget *widget, gpointer user_data)
545{
4b93d81e
HJYP
546 panel_configure( (Panel*)user_data, 0 );
547 return TRUE;
fddae119
FC
548}
549
9dd114c4 550/* Handler for "button_press_event" signal with Panel as parameter. */
551static gboolean panel_button_press_event_with_panel(GtkWidget *widget, GdkEventButton *event, Panel *panel)
fddae119 552{
9dd114c4 553 if (event->button == 3) /* right button */
554 {
555 GtkMenu* popup = (GtkMenu*) lxpanel_get_panel_menu(panel, NULL, FALSE);
556 gtk_menu_popup(popup, NULL, NULL, NULL, NULL, event->button, event->time);
557 return TRUE;
558 }
fddae119
FC
559 return FALSE;
560}
561
cf701cb7
HJYP
562static void panel_popupmenu_config_plugin( GtkMenuItem* item, Plugin* plugin )
563{
e3b89f43 564 plugin->class->config( plugin, GTK_WINDOW(plugin->panel->topgwin) );
930af9fd
HJYP
565
566 /* FIXME: this should be more elegant */
567 plugin->panel->config_changed = TRUE;
cf701cb7
HJYP
568}
569
4b93d81e
HJYP
570static void panel_popupmenu_add_item( GtkMenuItem* item, Panel* panel )
571{
572 /* panel_add_plugin( panel, panel->topgwin ); */
8d8b66da 573 panel_configure( panel, 2 );
cf701cb7
HJYP
574}
575
576static void panel_popupmenu_remove_item( GtkMenuItem* item, Plugin* plugin )
577{
578 Panel* panel = plugin->panel;
b7e8ad02
MJ
579
580 /* If the configuration dialog is open, there will certainly be a crash if the
581 * user manipulates the Configured Plugins list, after we remove this entry.
582 * Close the configuration dialog if it is open. */
583 if (panel->pref_dialog != NULL)
584 {
585 gtk_widget_destroy(panel->pref_dialog);
586 panel->pref_dialog = NULL;
587 }
588
cf701cb7 589 panel->plugins = g_list_remove( panel->plugins, plugin );
4b93d81e 590 panel_config_save( plugin->panel );
58e88058 591 plugin_delete(plugin);
cf701cb7
HJYP
592}
593
3e7b8eb7
HJYP
594/* FIXME: Potentially we can support multiple panels at the same edge,
595 * but currently this cannot be done due to some positioning problems. */
596static char* gen_panel_name( int edge )
597{
598 const char* edge_str = num2str( edge_pair, edge, "" );
599 char* name = NULL;
600 char* dir = get_config_file( cprofile, "panels", FALSE );
601 int i;
602 for( i = 0; i < G_MAXINT; ++i )
603 {
604 char* f;
605 if( G_LIKELY( i > 0 ) )
606 name = g_strdup_printf( "%s%d", edge_str, i );
607 else
608 name = g_strdup( edge_str );
609 f = g_build_filename( dir, name, NULL );
610 if( ! g_file_test( f, G_FILE_TEST_EXISTS ) )
611 {
612 g_free( f );
613 break;
614 }
615 g_free( name );
616 g_free( f );
617 }
618 g_free( dir );
619 return name;
620}
621
9dd114c4 622static void try_allocate_edge(Panel* p, int edge)
623{
624 if ((p->edge == EDGE_NONE) && (panel_edge_available(p, edge)))
625 p->edge = edge;
626}
627
3e7b8eb7
HJYP
628/* FIXME: Potentially we can support multiple panels at the same edge,
629 * but currently this cannot be done due to some positioning problems. */
cf701cb7
HJYP
630static void panel_popupmenu_create_panel( GtkMenuItem* item, Panel* panel )
631{
9dd114c4 632 Panel* new_panel = panel_allocate();
633
634 /* Allocate the edge. */
635 try_allocate_edge(new_panel, EDGE_BOTTOM);
636 try_allocate_edge(new_panel, EDGE_TOP);
637 try_allocate_edge(new_panel, EDGE_LEFT);
638 try_allocate_edge(new_panel, EDGE_RIGHT);
639 new_panel->name = gen_panel_name(new_panel->edge);
640
641 panel_configure(new_panel, 0);
642 panel_normalize_configuration(new_panel);
643 panel_start_gui(new_panel);
644 gtk_widget_show_all(new_panel->topgwin);
645
646 panel_config_save(new_panel);
647 all_panels = g_slist_prepend(all_panels, new_panel);
cf701cb7
HJYP
648}
649
650static void panel_popupmenu_delete_panel( GtkMenuItem* item, Panel* panel )
651{
652 GtkWidget* dlg;
653 gboolean ok;
e3b89f43 654 dlg = gtk_message_dialog_new_with_markup( GTK_WINDOW(panel->topgwin),
cf701cb7
HJYP
655 GTK_DIALOG_MODAL,
656 GTK_MESSAGE_QUESTION,
657 GTK_BUTTONS_OK_CANCEL,
658 _("Really delete this panel?\n<b>Warning: This can not be recovered.</b>") );
d1d43629 659 panel_apply_icon(GTK_WINDOW(dlg));
cf701cb7
HJYP
660 gtk_window_set_title( (GtkWindow*)dlg, _("Confirm") );
661 ok = ( gtk_dialog_run( (GtkDialog*)dlg ) == GTK_RESPONSE_OK );
662 gtk_widget_destroy( dlg );
663 if( ok )
664 {
4b93d81e 665 gchar *fname, *dir;
cf701cb7 666 all_panels = g_slist_remove( all_panels, panel );
4b93d81e
HJYP
667
668 /* delete the config file of this panel */
669 dir = get_config_file( cprofile, "panels", FALSE );
670 fname = g_build_filename( dir, panel->name, NULL );
671 g_free( dir );
672 g_unlink( fname );
930af9fd 673 panel->config_changed = 0;
cf701cb7
HJYP
674 panel_destroy( panel );
675 }
676}
677
e7a42ecf 678static void panel_popupmenu_about( GtkMenuItem* item, Panel* panel )
679{
680 GtkWidget *about;
681 const gchar* authors[] = {
682 "Hong Jen Yee (PCMan) <pcman.tw@gmail.com>",
683 "Jim Huang <jserv.tw@gmail.com>",
684 "Greg McNew <gmcnew@gmail.com> (battery plugin)",
685 "Fred Chien <cfsghost@gmail.com>",
686 "Daniel Kesler <kesler.daniel@gmail.com>",
687 "Juergen Hoetzel <juergen@archlinux.org>",
2918994e 688 "Marty Jack <martyj19@comcast.net>",
815e1027 689 "Martin Bagge <brother@bsnet.se>",
e7a42ecf 690 NULL
691 };
692 /* TRANSLATORS: Replace this string with your names, one name per line. */
693 gchar *translators = _( "translator-credits" );
694
695 about = gtk_about_dialog_new();
696 panel_apply_icon(GTK_WINDOW(about));
697 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(about), VERSION);
698 gtk_about_dialog_set_name(GTK_ABOUT_DIALOG(about), _("LXPanel"));
4c91be3e
JL
699
700 if(gtk_icon_theme_has_icon(panel->icon_theme, "video-display"))
701 {
702 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
703 gtk_icon_theme_load_icon(panel->icon_theme, "video-display", 48, 0, NULL));
704 }
705 else if (gtk_icon_theme_has_icon(panel->icon_theme, "start-here"))
706 {
707 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
708 gtk_icon_theme_load_icon(panel->icon_theme, "start-here", 48, 0, NULL));
709 }
710 else
711 {
712 gtk_about_dialog_set_logo( GTK_ABOUT_DIALOG(about),
713 gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL));
714 }
715
815e1027 716 gtk_about_dialog_set_copyright(GTK_ABOUT_DIALOG(about), _("Copyright (C) 2008-2011"));
e7a42ecf 717 gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(about), _( "Desktop panel for LXDE project"));
718 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.");
719 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(about), "http://lxde.org/");
720 gtk_about_dialog_set_authors(GTK_ABOUT_DIALOG(about), authors);
721 gtk_about_dialog_set_translator_credits(GTK_ABOUT_DIALOG(about), translators);
722 gtk_dialog_run(GTK_DIALOG(about));
723 gtk_widget_destroy(about);
724}
725
726void panel_apply_icon( GtkWindow *w )
727{
d60c9372
MR
728 GdkPixbuf* window_icon;
729
4c91be3e
JL
730 if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "video-display"))
731 {
3ad38785 732 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "video-display", 24, 0, NULL);
d60c9372 733 }
4c91be3e
JL
734 else if(gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "start-here"))
735 {
736 window_icon = gtk_icon_theme_load_icon(gtk_icon_theme_get_default(), "start-here", 24, 0, NULL);
89496346
JL
737 }
738 else
4c91be3e 739 {
d60c9372
MR
740 window_icon = gdk_pixbuf_new_from_file(PACKAGE_DATA_DIR "/lxpanel/images/my-computer.png", NULL);
741 }
21f91705 742 gtk_window_set_icon(w, window_icon);
e7a42ecf 743}
744
9dd114c4 745GtkMenu* lxpanel_get_panel_menu( Panel* panel, Plugin* plugin, gboolean use_sub_menu )
cf701cb7 746{
e3b89f43 747 GtkWidget *menu_item, *img;
748 GtkMenu *ret,*menu;
749
cf701cb7 750 char* tmp;
e3b89f43 751 ret = menu = GTK_MENU(gtk_menu_new());
cf701cb7 752
4b93d81e
HJYP
753 img = gtk_image_new_from_stock( GTK_STOCK_EDIT, GTK_ICON_SIZE_MENU );
754 menu_item = gtk_image_menu_item_new_with_label(_("Add / Remove Panel Items"));
755 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
756 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
757 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_add_item), panel );
cf701cb7
HJYP
758
759 if( plugin )
760 {
761 img = gtk_image_new_from_stock( GTK_STOCK_REMOVE, GTK_ICON_SIZE_MENU );
762 tmp = g_strdup_printf( _("Remove \"%s\" From Panel"), _(plugin->class->name) );
763 menu_item = gtk_image_menu_item_new_with_label( tmp );
764 g_free( tmp );
765 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
766 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
767 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_remove_item), plugin );
768 }
769
770 menu_item = gtk_separator_menu_item_new();
771 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
772
4b93d81e
HJYP
773 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
774 menu_item = gtk_image_menu_item_new_with_label(_("Panel Settings"));
775 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
776 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
777 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(panel_popupmenu_configure), panel );
778
cf701cb7
HJYP
779 img = gtk_image_new_from_stock( GTK_STOCK_NEW, GTK_ICON_SIZE_MENU );
780 menu_item = gtk_image_menu_item_new_with_label(_("Create New Panel"));
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( menu_item, "activate", G_CALLBACK(panel_popupmenu_create_panel), panel );
3e7b8eb7
HJYP
784 /* FIXME: Potentially we can support multiple panels at the same edge,
785 * but currently this cannot be done due to some positioning problems. */
786 /* currently, disable the option when there are already four panels */
787 if( g_slist_length( all_panels ) >= 4 )
788 gtk_widget_set_sensitive( menu_item, FALSE );
cf701cb7
HJYP
789
790 img = gtk_image_new_from_stock( GTK_STOCK_DELETE, GTK_ICON_SIZE_MENU );
791 menu_item = gtk_image_menu_item_new_with_label(_("Delete This Panel"));
792 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
793 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
794 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_delete_panel), panel );
795 if( ! all_panels->next ) /* if this is the only panel */
796 gtk_widget_set_sensitive( menu_item, FALSE );
797
e7a42ecf 798 menu_item = gtk_separator_menu_item_new();
799 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
800
801 img = gtk_image_new_from_stock( GTK_STOCK_ABOUT, GTK_ICON_SIZE_MENU );
802 menu_item = gtk_image_menu_item_new_with_label(_("About"));
803 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
804 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
805 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_about), panel );
806
cf701cb7
HJYP
807 if( use_sub_menu )
808 {
e3b89f43 809 ret = GTK_MENU(gtk_menu_new());
cf701cb7
HJYP
810 menu_item = gtk_image_menu_item_new_with_label(_("Panel"));
811 gtk_menu_shell_append(GTK_MENU_SHELL(ret), menu_item);
e3b89f43 812 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), GTK_WIDGET(menu) );
cf701cb7 813
e3b89f43 814 gtk_widget_show_all(GTK_WIDGET(ret));
cf701cb7
HJYP
815 }
816
3e7b8eb7
HJYP
817 if( plugin )
818 {
819 menu_item = gtk_separator_menu_item_new();
820 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
821
822 img = gtk_image_new_from_stock( GTK_STOCK_PREFERENCES, GTK_ICON_SIZE_MENU );
823 tmp = g_strdup_printf( _("\"%s\" Settings"), _(plugin->class->name) );
824 menu_item = gtk_image_menu_item_new_with_label( tmp );
825 g_free( tmp );
826 gtk_image_menu_item_set_image( (GtkImageMenuItem*)menu_item, img );
827 gtk_menu_shell_prepend(GTK_MENU_SHELL(ret), menu_item);
828 if( plugin->class->config )
829 g_signal_connect( menu_item, "activate", G_CALLBACK(panel_popupmenu_config_plugin), plugin );
830 else
831 gtk_widget_set_sensitive( menu_item, FALSE );
832 }
833
e3b89f43 834 gtk_widget_show_all(GTK_WIDGET(menu));
3e7b8eb7 835
cf701cb7
HJYP
836 g_signal_connect( ret, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
837 return ret;
838}
839
a52c2257
HJYP
840/****************************************************
841 * panel creation *
842 ****************************************************/
176fb687 843
a52c2257 844static void
22242ed4 845make_round_corners(Panel *p)
a52c2257 846{
a97d06a6 847 /* FIXME: This should be re-written with shape extension of X11 */
4542c20d 848 /* gdk_window_shape_combine_mask() can be used */
bee4c26e
HJYP
849}
850
22242ed4 851void panel_set_dock_type(Panel *p)
bee4c26e
HJYP
852{
853 if (p->setdocktype) {
854 Atom state = a_NET_WM_WINDOW_TYPE_DOCK;
855 XChangeProperty(GDK_DISPLAY(), p->topxwin,
856 a_NET_WM_WINDOW_TYPE, XA_ATOM, 32,
857 PropModeReplace, (unsigned char *) &state, 1);
858 }
859 else {
860 XDeleteProperty( GDK_DISPLAY(), p->topxwin, a_NET_WM_WINDOW_TYPE );
861 }
a52c2257
HJYP
862}
863
176fb687 864static void panel_set_visibility(Panel *p, gboolean visible)
865{
866 if ( ! visible) gtk_widget_hide(p->box);
867 p->visible = visible;
868 calculate_position(p);
869 gtk_widget_set_size_request(p->topgwin, p->aw, p->ah);
870 gdk_window_move(p->topgwin->window, p->ax, p->ay);
871 if (visible) gtk_widget_show(p->box);
872 panel_set_wm_strut(p);
873}
874
875static gboolean panel_leave_real(Panel *p)
876{
fab48ed0 877 /* If the pointer is grabbed by this application, leave the panel displayed.
878 * There is no way to determine if it is grabbed by another application, such as an application that has a systray icon. */
176fb687 879 if (gdk_display_pointer_is_grabbed(p->display))
880 return TRUE;
881
fab48ed0 882 /* If the pointer is inside the panel, leave the panel displayed. */
176fb687 883 gint x, y;
884 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
885 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
886 return TRUE;
887
fab48ed0 888 /* If the panel is configured to autohide and if it is visible, hide the panel. */
176fb687 889 if ((p->autohide) && (p->visible))
890 panel_set_visibility(p, FALSE);
891
fab48ed0 892 /* Clear the timer. */
176fb687 893 p->hide_timeout = 0;
894 return FALSE;
895}
896
897static gboolean panel_enter(GtkImage *widget, GdkEventCrossing *event, Panel *p)
898{
fab48ed0 899 /* We may receive multiple enter-notify events when the pointer crosses into the panel.
900 * Do extra tests to make sure this does not cause misoperation such as blinking.
901 * If the pointer is inside the panel, unhide it. */
902 gint x, y;
903 gdk_display_get_pointer(p->display, NULL, &x, &y, NULL);
904 if ((p->cx <= x) && (x <= (p->cx + p->cw)) && (p->cy <= y) && (y <= (p->cy + p->ch)))
905 {
906 /* If the pointer is inside the panel and we have not already unhidden it, do so and
907 * set a timer to recheck it in a half second. */
908 if (p->hide_timeout == 0)
909 {
910 p->hide_timeout = g_timeout_add(500, (GSourceFunc) panel_leave_real, p);
911 panel_set_visibility(p, TRUE);
912 }
913 }
914 else
915 {
916 /* If the pointer is not inside the panel, simulate a timer expiration. */
917 panel_leave_real(p);
918 }
176fb687 919 return TRUE;
920}
921
922static gboolean panel_drag_motion(GtkWidget *widget, GdkDragContext *drag_context, gint x,
923 gint y, guint time, Panel *p)
924{
925 panel_enter(NULL, NULL, p);
926 return TRUE;
927}
928
929void panel_establish_autohide(Panel *p)
930{
931 if (p->autohide)
932 {
933 gtk_widget_add_events(p->topgwin, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
934 g_signal_connect(G_OBJECT(p->topgwin), "enter-notify-event", G_CALLBACK(panel_enter), p);
935 g_signal_connect(G_OBJECT(p->topgwin), "drag-motion", (GCallback) panel_drag_motion, p);
936 gtk_drag_dest_set(p->topgwin, GTK_DEST_DEFAULT_MOTION, NULL, 0, 0);
937 gtk_drag_dest_set_track_motion(p->topgwin, TRUE);
938 panel_enter(NULL, NULL, p);
939 }
940 else if ( ! p->visible)
941 {
942 gtk_widget_show(p->box);
943 p->visible = TRUE;
944 }
945}
946
8f9e6256 947/* Set an image from a file with scaling to the panel icon size. */
fcb35553 948void panel_image_set_from_file(Panel * p, GtkWidget * image, const char * file)
8f9e6256 949{
950 GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file_at_scale(file, p->icon_size, p->icon_size, TRUE, NULL);
951 if (pixbuf != NULL)
952 {
953 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
954 g_object_unref(pixbuf);
955 }
956}
957
c14620f2
MJ
958/* Set an image from a icon theme with scaling to the panel icon size. */
959gboolean panel_image_set_icon_theme(Panel * p, GtkWidget * image, const gchar * icon)
960{
1b532bce 961 if (gtk_icon_theme_has_icon(p->icon_theme, icon))
c14620f2 962 {
1b532bce 963 GdkPixbuf * pixbuf = gtk_icon_theme_load_icon(p->icon_theme, icon, p->icon_size, 0, NULL);
c14620f2
MJ
964 gtk_image_set_from_pixbuf(GTK_IMAGE(image), pixbuf);
965 g_object_unref(pixbuf);
966 return TRUE;
967 }
968 return FALSE;
969}
970
0defe4b9 971static void
22242ed4 972panel_start_gui(Panel *p)
a52c2257
HJYP
973{
974 Atom state[3];
975 XWMHints wmhints;
976 guint32 val;
6db11841 977
a52c2257
HJYP
978 ENTER;
979
9dd114c4 980 p->curdesk = get_net_current_desktop();
981 p->desknum = get_net_number_of_desktops();
982 p->workarea = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_WORKAREA, XA_CARDINAL, &p->wa_len);
983
cb22c87c
HJYP
984 /* main toplevel window */
985 /* p->topgwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); */
986 p->topgwin = (GtkWidget*)g_object_new(PANEL_TOPLEVEL_TYPE, NULL);
987 gtk_widget_set_name(p->topgwin, "PanelToplevel");
176fb687 988 p->display = gdk_display_get_default();
a52c2257
HJYP
989 gtk_container_set_border_width(GTK_CONTAINER(p->topgwin), 0);
990 gtk_window_set_resizable(GTK_WINDOW(p->topgwin), FALSE);
991 gtk_window_set_wmclass(GTK_WINDOW(p->topgwin), "panel", "lxpanel");
992 gtk_window_set_title(GTK_WINDOW(p->topgwin), "panel");
993 gtk_window_set_position(GTK_WINDOW(p->topgwin), GTK_WIN_POS_NONE);
994 gtk_window_set_decorated(GTK_WINDOW(p->topgwin), FALSE);
77886b88 995
e2957bd2
HJYP
996 gtk_window_group_add_window( win_grp, (GtkWindow*)p->topgwin );
997
a52c2257
HJYP
998 g_signal_connect(G_OBJECT(p->topgwin), "delete-event",
999 G_CALLBACK(panel_delete_event), p);
1000 g_signal_connect(G_OBJECT(p->topgwin), "destroy-event",
1001 G_CALLBACK(panel_destroy_event), p);
1002 g_signal_connect (G_OBJECT (p->topgwin), "size-request",
1003 (GCallback) panel_size_req, p);
1004 g_signal_connect (G_OBJECT (p->topgwin), "size-allocate",
1005 (GCallback) panel_size_alloc, p);
1006 g_signal_connect (G_OBJECT (p->topgwin), "configure-event",
1007 (GCallback) panel_configure_event, p);
3e7b8eb7
HJYP
1008
1009 gtk_widget_add_events( p->topgwin, GDK_BUTTON_PRESS_MASK );
fddae119 1010 g_signal_connect(G_OBJECT (p->topgwin), "button_press_event",
9dd114c4 1011 (GCallback) panel_button_press_event_with_panel, p);
f2d54481 1012
a52c2257
HJYP
1013 g_signal_connect (G_OBJECT (p->topgwin), "realize",
1014 (GCallback) panel_realize, p);
f2d54481 1015
84fc1d55
HJYP
1016 g_signal_connect (G_OBJECT (p->topgwin), "style-set",
1017 (GCallback)panel_style_set, p);
a52c2257
HJYP
1018 gtk_widget_realize(p->topgwin);
1019 //gdk_window_set_decorations(p->topgwin->window, 0);
2de71c90 1020
4542c20d 1021 // main layout manager as a single child of panel
a97d06a6 1022 p->box = p->my_box_new(FALSE, 0);
a52c2257 1023 gtk_container_set_border_width(GTK_CONTAINER(p->box), 0);
4542c20d
HJYP
1024// gtk_container_add(GTK_CONTAINER(p->bbox), p->box);
1025 gtk_container_add(GTK_CONTAINER(p->topgwin), p->box);
a52c2257 1026 gtk_widget_show(p->box);
a97d06a6
HJYP
1027 if (p->round_corners)
1028 make_round_corners(p);
6db11841 1029
a52c2257
HJYP
1030 p->topxwin = GDK_WINDOW_XWINDOW(GTK_WIDGET(p->topgwin)->window);
1031 DBG("topxwin = %x\n", p->topxwin);
1032
1033 /* the settings that should be done before window is mapped */
1034 wmhints.flags = InputHint;
1035 wmhints.input = 0;
bee4c26e 1036 XSetWMHints (GDK_DISPLAY(), p->topxwin, &wmhints);
24053345 1037#define WIN_HINTS_SKIP_FOCUS (1<<0) /* "alt-tab" skips this win */
a52c2257
HJYP
1038 val = WIN_HINTS_SKIP_FOCUS;
1039 XChangeProperty(GDK_DISPLAY(), p->topxwin,
1040 XInternAtom(GDK_DISPLAY(), "_WIN_HINTS", False), XA_CARDINAL, 32,
1041 PropModeReplace, (unsigned char *) &val, 1);
1042
bee4c26e 1043 panel_set_dock_type(p);
a52c2257
HJYP
1044
1045 /* window mapping point */
1046 gtk_widget_show_all(p->topgwin);
1047
1048 /* the settings that should be done after window is mapped */
176fb687 1049 panel_establish_autohide(p);
a52c2257
HJYP
1050
1051 /* send it to running wm */
1052 Xclimsg(p->topxwin, a_NET_WM_DESKTOP, 0xFFFFFFFF, 0, 0, 0, 0);
1053 /* and assign it ourself just for case when wm is not running */
1054 val = 0xFFFFFFFF;
1055 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_DESKTOP, XA_CARDINAL, 32,
1056 PropModeReplace, (unsigned char *) &val, 1);
1057
1058 state[0] = a_NET_WM_STATE_SKIP_PAGER;
1059 state[1] = a_NET_WM_STATE_SKIP_TASKBAR;
1060 state[2] = a_NET_WM_STATE_STICKY;
1061 XChangeProperty(GDK_DISPLAY(), p->topxwin, a_NET_WM_STATE, XA_ATOM,
1062 32, PropModeReplace, (unsigned char *) state, 3);
1063
a52c2257
HJYP
1064 calculate_position(p);
1065 gdk_window_move_resize(p->topgwin->window, p->ax, p->ay, p->aw, p->ah);
bee4c26e 1066 panel_set_wm_strut(p);
77886b88 1067
a52c2257
HJYP
1068 RET();
1069}
1070
2918994e 1071/* Exchange the "width" and "height" terminology for vertical and horizontal panels. */
1072void panel_adjust_geometry_terminology(Panel * p)
9dd114c4 1073{
8ed3b3d4 1074 if ((p->height_label != NULL) && (p->width_label != NULL)
1075 && (p->alignment_left_label != NULL) && (p->alignment_right_label != NULL))
9dd114c4 1076 {
1077 if ((p->edge == EDGE_TOP) || (p->edge == EDGE_BOTTOM))
1078 {
1079 gtk_label_set_text(GTK_LABEL(p->height_label), _("Height:"));
1080 gtk_label_set_text(GTK_LABEL(p->width_label), _("Width:"));
2918994e 1081 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Left"));
1082 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Right"));
9dd114c4 1083 }
1084 else
1085 {
1086 gtk_label_set_text(GTK_LABEL(p->height_label), _("Width:"));
1087 gtk_label_set_text(GTK_LABEL(p->width_label), _("Height:"));
2918994e 1088 gtk_button_set_label(GTK_BUTTON(p->alignment_left_label), _("Top"));
1089 gtk_button_set_label(GTK_BUTTON(p->alignment_right_label), _("Bottom"));
9dd114c4 1090 }
1091 }
1092}
1093
2918994e 1094/* Draw text into a label, with the user preference color and optionally bold. */
3fd2186c
HG
1095void panel_draw_label_text(Panel * p, GtkWidget * label, char * text, gboolean bold,
1096 float custom_size_factor, gboolean custom_color)
2918994e 1097{
2918994e 1098 if (text == NULL)
1099 {
1100 /* Null string. */
1101 gtk_label_set_text(GTK_LABEL(label), NULL);
83d410c1 1102 return;
2918994e 1103 }
1104
83d410c1
HG
1105 /* Compute an appropriate size so the font will scale with the panel's icon size. */
1106 int font_desc;
1107 if (p->usefontsize)
1108 font_desc = p->fontsize;
f669ce5e 1109 else
2918994e 1110 {
83d410c1
HG
1111 if (p->icon_size < 20)
1112 font_desc = 9;
1113 else if (p->icon_size >= 20 && p->icon_size < 36)
1114 font_desc = 10;
1115 else
1116 font_desc = 12;
1117 }
1118 font_desc *= custom_size_factor;
1119
1120 /* Check the string for characters that need to be escaped.
1121 * If any are found, create the properly escaped string and use it instead. */
1122 char * valid_markup = text;
1123 char * escaped_text = NULL;
1124 char * q;
1125 for (q = text; *q != '\0'; q += 1)
1126 {
1127 if ((*q == '<') || (*q == '>') || (*q == '&'))
2918994e 1128 {
83d410c1
HG
1129 escaped_text = g_markup_escape_text(text, -1);
1130 valid_markup = escaped_text;
1131 break;
2918994e 1132 }
83d410c1 1133 }
2918994e 1134
83d410c1
HG
1135 gchar * formatted_text;
1136 if ((custom_color) && (p->usefontcolor))
1137 {
1138 /* Color, optionally bold. */
1139 formatted_text = g_strdup_printf("<span font_desc=\"%d\" color=\"#%06x\">%s%s%s</span>",
f669ce5e 1140 font_desc,
2918994e 1141 gcolor2rgb24(&p->gfontcolor),
1142 ((bold) ? "<b>" : ""),
1143 valid_markup,
1144 ((bold) ? "</b>" : ""));
83d410c1
HG
1145 }
1146 else
1147 {
1148 /* No color, optionally bold. */
1149 formatted_text = g_strdup_printf("<span font_desc=\"%d\">%s%s%s</span>",
f669ce5e 1150 font_desc,
1151 ((bold) ? "<b>" : ""),
1152 valid_markup,
1153 ((bold) ? "</b>" : ""));
2918994e 1154 }
83d410c1
HG
1155
1156 gtk_label_set_markup(GTK_LABEL(label), formatted_text);
1157 g_free(formatted_text);
1158 g_free(escaped_text);
2918994e 1159}
1160
1161void panel_set_panel_configuration_changed(Panel *p)
a97d06a6
HJYP
1162{
1163 GList* l;
9dd114c4 1164
1165 int previous_orientation = p->orientation;
a97d06a6
HJYP
1166 p->orientation = (p->edge == EDGE_TOP || p->edge == EDGE_BOTTOM)
1167 ? ORIENT_HORIZ : ORIENT_VERT;
9dd114c4 1168
1169 if (previous_orientation != p->orientation)
1170 {
1171 panel_adjust_geometry_terminology(p);
1b0b2df6 1172 if (previous_orientation != ORIENT_NONE)
1173 p->height = ((p->orientation == ORIENT_HORIZ) ? PANEL_HEIGHT_DEFAULT : PANEL_WIDTH_DEFAULT);
9dd114c4 1174 if (p->height_control != NULL)
1175 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->height_control), p->height);
2918994e 1176 if ((p->widthtype == WIDTH_PIXEL) && (p->width_control != NULL))
1177 {
1178 int value = ((p->orientation == ORIENT_HORIZ) ? gdk_screen_width() : gdk_screen_height());
1179 gtk_spin_button_set_range(GTK_SPIN_BUTTON(p->width_control), 0, value);
1180 gtk_spin_button_set_value(GTK_SPIN_BUTTON(p->width_control), value);
1181 }
1182
9dd114c4 1183 }
1184
a97d06a6
HJYP
1185 if (p->orientation == ORIENT_HORIZ) {
1186 p->my_box_new = gtk_hbox_new;
1187 p->my_separator_new = gtk_vseparator_new;
1188 } else {
1189 p->my_box_new = gtk_vbox_new;
1190 p->my_separator_new = gtk_hseparator_new;
1191 }
1192
1193 /* recreate the main layout box */
964b8b7e 1194 if (p->box != NULL)
1195 {
1196#if GTK_CHECK_VERSION(2,16,0)
1197 GtkOrientation bo = (p->orientation == ORIENT_HORIZ) ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL;
1198 gtk_orientable_set_orientation(GTK_ORIENTABLE(p->box), bo);
1199#else
1200 GtkBox * newbox = GTK_BOX(recreate_box(GTK_BOX(p->box), p->orientation));
1201 if (GTK_WIDGET(newbox) != p->box)
1202 {
5a343ad5 1203 p->box = GTK_WIDGET(newbox);
964b8b7e 1204 gtk_container_add(GTK_CONTAINER(p->topgwin), GTK_WIDGET(newbox));
a97d06a6 1205 }
964b8b7e 1206#endif
a97d06a6 1207 }
9dd114c4 1208
a97d06a6
HJYP
1209 /* NOTE: This loop won't be executed when panel started since
1210 plugins are not loaded at that time.
1211 This is used when the orientation of the panel is changed
1212 from the config dialog, and plugins should be re-layout.
1213 */
1214 for( l = p->plugins; l; l = l->next ) {
22242ed4 1215 Plugin* pl = (Plugin*)l->data;
2918994e 1216 if( pl->class->panel_configuration_changed ) {
1217 pl->class->panel_configuration_changed( pl );
a97d06a6
HJYP
1218 }
1219 }
1220}
1221
a52c2257 1222static int
22242ed4 1223panel_parse_global(Panel *p, char **fp)
a52c2257
HJYP
1224{
1225 line s;
1226 s.len = 256;
6db11841 1227
3e7b8eb7
HJYP
1228 if( G_LIKELY( fp ) )
1229 {
1230 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
1231 if (s.type == LINE_VAR) {
1232 if (!g_ascii_strcasecmp(s.t[0], "edge")) {
1233 p->edge = str2num(edge_pair, s.t[1], EDGE_NONE);
1234 } else if (!g_ascii_strcasecmp(s.t[0], "allign")) {
1235 p->allign = str2num(allign_pair, s.t[1], ALLIGN_NONE);
1236 } else if (!g_ascii_strcasecmp(s.t[0], "margin")) {
1237 p->margin = atoi(s.t[1]);
1238 } else if (!g_ascii_strcasecmp(s.t[0], "widthtype")) {
1239 p->widthtype = str2num(width_pair, s.t[1], WIDTH_NONE);
1240 } else if (!g_ascii_strcasecmp(s.t[0], "width")) {
1241 p->width = atoi(s.t[1]);
1242 } else if (!g_ascii_strcasecmp(s.t[0], "heighttype")) {
1243 p->heighttype = str2num(height_pair, s.t[1], HEIGHT_NONE);
1244 } else if (!g_ascii_strcasecmp(s.t[0], "height")) {
1245 p->height = atoi(s.t[1]);
1246 } else if (!g_ascii_strcasecmp(s.t[0], "spacing")) {
1247 p->spacing = atoi(s.t[1]);
1248 } else if (!g_ascii_strcasecmp(s.t[0], "SetDockType")) {
1249 p->setdocktype = str2num(bool_pair, s.t[1], 0);
1250 } else if (!g_ascii_strcasecmp(s.t[0], "SetPartialStrut")) {
1251 p->setstrut = str2num(bool_pair, s.t[1], 0);
1252 } else if (!g_ascii_strcasecmp(s.t[0], "RoundCorners")) {
1253 p->round_corners = str2num(bool_pair, s.t[1], 0);
1254 } else if (!g_ascii_strcasecmp(s.t[0], "Transparent")) {
1255 p->transparent = str2num(bool_pair, s.t[1], 0);
1256 } else if (!g_ascii_strcasecmp(s.t[0], "Alpha")) {
1257 p->alpha = atoi(s.t[1]);
1258 if (p->alpha > 255)
1259 p->alpha = 255;
176fb687 1260 } else if (!g_ascii_strcasecmp(s.t[0], "AutoHide")) {
1261 p->autohide = str2num(bool_pair, s.t[1], 0);
1262 } else if (!g_ascii_strcasecmp(s.t[0], "HeightWhenHidden")) {
1263 p->height_when_hidden = atoi(s.t[1]);
3e7b8eb7
HJYP
1264 } else if (!g_ascii_strcasecmp(s.t[0], "TintColor")) {
1265 if (!gdk_color_parse (s.t[1], &p->gtintcolor))
1266 gdk_color_parse ("white", &p->gtintcolor);
1267 p->tintcolor = gcolor2rgb24(&p->gtintcolor);
1268 DBG("tintcolor=%x\n", p->tintcolor);
1269 } else if (!g_ascii_strcasecmp(s.t[0], "useFontColor")) {
1270 p->usefontcolor = str2num(bool_pair, s.t[1], 0);
1271 } else if (!g_ascii_strcasecmp(s.t[0], "FontColor")) {
1272 if (!gdk_color_parse (s.t[1], &p->gfontcolor))
1273 gdk_color_parse ("black", &p->gfontcolor);
1274 p->fontcolor = gcolor2rgb24(&p->gfontcolor);
1275 DBG("fontcolor=%x\n", p->fontcolor);
1869ef90
LK
1276 } else if (!g_ascii_strcasecmp(s.t[0], "useFontSize")) {
1277 p->usefontsize = str2num(bool_pair, s.t[1], 0);
1278 } else if (!g_ascii_strcasecmp(s.t[0], "FontSize")) {
1279 p->fontsize = atoi(s.t[1]);
3e7b8eb7
HJYP
1280 } else if (!g_ascii_strcasecmp(s.t[0], "Background")) {
1281 p->background = str2num(bool_pair, s.t[1], 0);
1282 } else if( !g_ascii_strcasecmp(s.t[0], "BackgroundFile") ) {
1283 p->background_file = g_strdup( s.t[1] );
8f9e6256 1284 } else if (!g_ascii_strcasecmp(s.t[0], "IconSize")) {
1285 p->icon_size = atoi(s.t[1]);
82ca42af
HG
1286 } else if (!g_ascii_strcasecmp(s.t[0], "LogLevel")) {
1287 configured_log_level = atoi(s.t[1]);
1288 if (!log_level_set_on_commandline)
1289 log_level = configured_log_level;
3e7b8eb7
HJYP
1290 } else {
1291 ERR( "lxpanel: %s - unknown var in Global section\n", s.t[0]);
3e7b8eb7
HJYP
1292 }
1293 } else if (s.type == LINE_BLOCK_END) {
1294 break;
a52c2257 1295 } else {
3e7b8eb7 1296 ERR( "lxpanel: illegal in this context %s\n", s.str);
a52c2257
HJYP
1297 RET(0);
1298 }
a52c2257
HJYP
1299 }
1300 }
4542c20d 1301
9dd114c4 1302 panel_normalize_configuration(p);
239cb032 1303
9dd114c4 1304 return 1;
a52c2257
HJYP
1305}
1306
1307static int
22242ed4 1308panel_parse_plugin(Panel *p, char **fp)
a52c2257
HJYP
1309{
1310 line s;
22242ed4 1311 Plugin *plug = NULL;
a52c2257 1312 gchar *type = NULL;
a52c2257 1313 int expand , padding, border;
db449f6e
HJYP
1314 char* pconfig = NULL;
1315
a52c2257
HJYP
1316 ENTER;
1317 s.len = 256;
a52c2257 1318 border = expand = padding = 0;
c69ac68e 1319 while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
a52c2257
HJYP
1320 if (s.type == LINE_NONE) {
1321 ERR( "lxpanel: bad line %s\n", s.str);
1322 goto error;
1323 }
1324 if (s.type == LINE_VAR) {
1325 if (!g_ascii_strcasecmp(s.t[0], "type")) {
1326 type = g_strdup(s.t[1]);
1327 DBG("plug %s\n", type);
1328 } else if (!g_ascii_strcasecmp(s.t[0], "expand"))
1329 expand = str2num(bool_pair, s.t[1], 0);
1330 else if (!g_ascii_strcasecmp(s.t[0], "padding"))
1331 padding = atoi(s.t[1]);
1332 else if (!g_ascii_strcasecmp(s.t[0], "border"))
1333 border = atoi(s.t[1]);
1334 else {
1335 ERR( "lxpanel: unknown var %s\n", s.t[0]);
a52c2257
HJYP
1336 }
1337 } else if (s.type == LINE_BLOCK_START) {
1338 if (!g_ascii_strcasecmp(s.t[0], "Config")) {
db449f6e 1339 pconfig = *fp;
a52c2257
HJYP
1340 int pno = 1;
1341 while (pno) {
1342 get_line_as_is(fp, &s);
1343 if (s.type == LINE_NONE) {
1344 ERR( "lxpanel: unexpected eof\n");
1345 goto error;
1346 } else if (s.type == LINE_BLOCK_START) {
1347 pno++;
1348 } else if (s.type == LINE_BLOCK_END) {
1349 pno--;
bee4c26e 1350 }
db449f6e 1351 }
a52c2257
HJYP
1352 } else {
1353 ERR( "lxpanel: unknown block %s\n", s.t[0]);
1354 goto error;
1355 }
1356 } else {
1357 ERR( "lxpanel: illegal in this context %s\n", s.str);
1358 goto error;
1359 }
1360 }
db449f6e 1361
a52c2257
HJYP
1362 if (!type || !(plug = plugin_load(type))) {
1363 ERR( "lxpanel: can't load %s plugin\n", type);
1364 goto error;
1365 }
db449f6e 1366
a52c2257 1367 plug->panel = p;
2918994e 1368 if (plug->class->expand_available) plug->expand = expand;
a52c2257
HJYP
1369 plug->padding = padding;
1370 plug->border = border;
a52c2257 1371 DBG("starting\n");
db449f6e 1372 if (!plugin_start(plug, pconfig ? &pconfig : NULL)) {
a52c2257
HJYP
1373 ERR( "lxpanel: can't start plugin %s\n", type);
1374 goto error;
1375 }
1376 DBG("plug %s\n", type);
1377 p->plugins = g_list_append(p->plugins, plug);
0dcb6bf5
HJYP
1378
1379 g_free( type );
a52c2257 1380 RET(1);
db449f6e 1381
a52c2257 1382 error:
2918994e 1383 if (plug != NULL)
1384 plugin_unload(plug);
a52c2257 1385 g_free(type);
a52c2257 1386 RET(0);
a52c2257
HJYP
1387}
1388
3e7b8eb7 1389int panel_start( Panel *p, char **fp )
a52c2257
HJYP
1390{
1391 line s;
db449f6e 1392
a52c2257
HJYP
1393 /* parse global section */
1394 ENTER;
1395 s.len = 256;
8110399f 1396
c69ac68e 1397 if ((lxpanel_get_line(fp, &s) != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Global")) {
a52c2257
HJYP
1398 ERR( "lxpanel: config file must start from Global section\n");
1399 RET(0);
1400 }
1401 if (!panel_parse_global(p, fp))
1402 RET(0);
1403
9dd114c4 1404 panel_start_gui(p);
1405
c69ac68e 1406 while (lxpanel_get_line(fp, &s) != LINE_NONE) {
a52c2257
HJYP
1407 if ((s.type != LINE_BLOCK_START) || g_ascii_strcasecmp(s.t[0], "Plugin")) {
1408 ERR( "lxpanel: expecting Plugin section\n");
1409 RET(0);
1410 }
9939506e 1411 panel_parse_plugin(p, fp);
a52c2257 1412 }
4542c20d
HJYP
1413
1414 /* update backgrond of panel and all plugins */
1415 panel_update_background( p );
9dd114c4 1416 return 1;
a52c2257
HJYP
1417}
1418
1419static void
1420delete_plugin(gpointer data, gpointer udata)
1421{
2918994e 1422 plugin_delete((Plugin *)data);
a52c2257
HJYP
1423}
1424
8110399f 1425void panel_destroy(Panel *p)
a52c2257
HJYP
1426{
1427 ENTER;
1428
2918994e 1429 if (p->pref_dialog != NULL)
1430 gtk_widget_destroy(p->pref_dialog);
1431 if (p->plugin_pref_dialog != NULL)
1432 {
1433 gtk_widget_destroy(p->plugin_pref_dialog);
1434 p->plugin_pref_dialog = NULL;
1435 }
1436
1437 if (p->bg != NULL)
1438 {
1439 g_signal_handlers_disconnect_by_func(G_OBJECT(p->bg), on_root_bg_changed, p);
1440 g_object_unref(p->bg);
1441 }
1442
930af9fd
HJYP
1443 if( p->config_changed )
1444 panel_config_save( p );
1445
a52c2257
HJYP
1446 g_list_foreach(p->plugins, delete_plugin, NULL);
1447 g_list_free(p->plugins);
1448 p->plugins = NULL;
8c44345a 1449
5297da29 1450 if( p->system_menus ){
8c44345a 1451 do{
5297da29 1452 } while ( g_source_remove_by_user_data( p->system_menus ) );
8c44345a
HJYP
1453 }
1454
16fbda14 1455 gtk_window_group_remove_window( win_grp, GTK_WINDOW( p->topgwin ) );
e2957bd2 1456
4b93d81e
HJYP
1457 if( p->topgwin )
1458 gtk_widget_destroy(p->topgwin);
a52c2257 1459 g_free(p->workarea);
2de71c90 1460 g_free( p->background_file );
5297da29 1461 g_slist_free( p->system_menus );
a52c2257
HJYP
1462 gdk_flush();
1463 XFlush(GDK_DISPLAY());
1464 XSync(GDK_DISPLAY(), True);
8110399f
HJYP
1465
1466 g_free( p->name );
2918994e 1467 g_free(p);
a52c2257
HJYP
1468 RET();
1469}
1470
8110399f
HJYP
1471Panel* panel_new( const char* config_file, const char* config_name )
1472{
1473 char *fp, *pfp; /* point to current position of profile data in memory */
cf701cb7 1474 Panel* panel = NULL;
16fbda14 1475
3e7b8eb7 1476 if( G_LIKELY(config_file) )
cf701cb7 1477 {
3e7b8eb7
HJYP
1478 g_file_get_contents( config_file, &fp, NULL, NULL );
1479 if( fp )
1480 {
9dd114c4 1481 panel = panel_allocate();
8ed3b3d4 1482 panel->orientation = ORIENT_NONE;
3e7b8eb7
HJYP
1483 panel->name = g_strdup( config_name );
1484 pfp = fp;
1485
1486 if (! panel_start( panel, &pfp )) {
1487 ERR( "lxpanel: can't start panel\n");
1488 panel_destroy( panel );
1489 panel = NULL;
1490 }
cf701cb7 1491
3e7b8eb7 1492 g_free( fp );
cf701cb7 1493 }
cf701cb7
HJYP
1494 }
1495 return panel;
8110399f 1496}
a52c2257 1497
0defe4b9 1498static void
a52c2257
HJYP
1499usage()
1500{
e7cb732b
HJYP
1501 g_print(_("lxpanel %s - lightweight GTK2+ panel for UNIX desktops\n"), version);
1502 g_print(_("Command line options:\n"));
1503 g_print(_(" --help -- print this help and exit\n"));
1504 g_print(_(" --version -- print version and exit\n"));
1505 g_print(_(" --log <number> -- set log level 0-5. 0 - none 5 - chatty\n"));
2918994e 1506// g_print(_(" --configure -- launch configuration utility\n"));
e7cb732b
HJYP
1507 g_print(_(" --profile name -- use specified profile\n"));
1508 g_print("\n");
1509 g_print(_(" -h -- same as --help\n"));
1510 g_print(_(" -p -- same as --profile\n"));
1511 g_print(_(" -v -- same as --version\n"));
2918994e 1512 // g_print(_(" -C -- same as --configure\n"));
b37c9aae 1513 g_print(_("\nVisit http://lxde.org/ for detail.\n\n"));
a52c2257
HJYP
1514}
1515
2918994e 1516int panel_handle_x_error(Display * d, XErrorEvent * ev)
a52c2257
HJYP
1517{
1518 char buf[256];
1519
a52c2257
HJYP
1520 if (log_level >= LOG_WARN) {
1521 XGetErrorText(GDK_DISPLAY(), ev->error_code, buf, 256);
1522 LOG(LOG_WARN, "lxpanel : X error: %s\n", buf);
1523 }
2918994e 1524 return 0; /* Ignored */
1525}
1526
1527int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
1528{
1529 if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
1530 panel_handle_x_error(d, ev);
1531 return 0; /* Ignored */
a52c2257
HJYP
1532}
1533
e68b47dc
JH
1534/* Lightweight lock related functions - X clipboard hacks */
1535
1536#define CLIPBOARD_NAME "LXPANEL_SELECTION"
1537
1538/*
1539 * clipboard_get_func - dummy get_func for gtk_clipboard_set_with_data ()
1540 */
1541static void
1542clipboard_get_func(
1543 GtkClipboard *clipboard G_GNUC_UNUSED,
1544 GtkSelectionData *selection_data G_GNUC_UNUSED,
1545 guint info G_GNUC_UNUSED,
1546 gpointer user_data_or_owner G_GNUC_UNUSED)
1547{
1548}
1549
1550/*
1551 * clipboard_clear_func - dummy clear_func for gtk_clipboard_set_with_data ()
1552 */
1553static void clipboard_clear_func(
1554 GtkClipboard *clipboard G_GNUC_UNUSED,
1555 gpointer user_data_or_owner G_GNUC_UNUSED)
1556{
1557}
1558
1559/*
1560 * Lightweight version for checking single instance.
1561 * Try and get the CLIPBOARD_NAME clipboard instead of using file manipulation.
1562 *
1563 * Returns TRUE if successfully retrieved and FALSE otherwise.
1564 */
16fb8c2e 1565static gboolean check_main_lock()
e68b47dc
JH
1566{
1567 static const GtkTargetEntry targets[] = { { CLIPBOARD_NAME, 0, 0 } };
1568 gboolean retval = FALSE;
1569 GtkClipboard *clipboard;
1570 Atom atom;
1571
1572 atom = gdk_x11_get_xatom_by_name(CLIPBOARD_NAME);
1573
1574 XGrabServer(GDK_DISPLAY());
1575
1576 if (XGetSelectionOwner(GDK_DISPLAY(), atom) != None)
1577 goto out;
1578
1579 clipboard = gtk_clipboard_get(gdk_atom_intern(CLIPBOARD_NAME, FALSE));
1580
1581 if (gtk_clipboard_set_with_data(clipboard, targets,
1582 G_N_ELEMENTS (targets),
1583 clipboard_get_func,
1584 clipboard_clear_func, NULL))
1585 retval = TRUE;
1586
1587out:
1588 XUngrabServer (GDK_DISPLAY ());
1589 gdk_flush ();
1590
1591 return retval;
1592}
1593#undef CLIPBOARD_NAME
1594
8110399f
HJYP
1595static gboolean start_all_panels( )
1596{
cf701cb7 1597 gboolean is_global;
4b93d81e 1598 for( is_global = 0; ! all_panels && is_global < 2; ++is_global )
cf701cb7
HJYP
1599 {
1600 char* panel_dir = get_config_file( cprofile, "panels", is_global );
1601 GDir* dir = g_dir_open( panel_dir, 0, NULL );
e3b89f43 1602 const gchar* name;
cf701cb7
HJYP
1603
1604 if( ! dir )
1605 {
1606 g_free( panel_dir );
1607 continue;
1608 }
1609
547ece89 1610 while((name = g_dir_read_name(dir)) != NULL)
cf701cb7
HJYP
1611 {
1612 char* panel_config = g_build_filename( panel_dir, name, NULL );
2918994e 1613 if (strchr(panel_config, '~') == NULL) /* Skip editor backup files in case user has hand edited in this directory */
1614 {
1615 Panel* panel = panel_new( panel_config, name );
1616 if( panel )
1617 all_panels = g_slist_prepend( all_panels, panel );
1618 }
cf701cb7
HJYP
1619 g_free( panel_config );
1620 }
1621 g_dir_close( dir );
1622 g_free( panel_dir );
1623 }
1624 return all_panels != NULL;
8110399f
HJYP
1625}
1626
cf701cb7
HJYP
1627void load_global_config();
1628void free_global_config();
1629
8110399f 1630int main(int argc, char *argv[], char *env[])
a52c2257
HJYP
1631{
1632 int i;
9df6826f 1633 const char* desktop_name;
f277dbb7 1634
a52c2257 1635 setlocale(LC_CTYPE, "");
f277dbb7 1636
1d434622
HJYP
1637 g_thread_init(NULL);
1638 gdk_threads_init();
3e71f8af 1639 gdk_threads_enter();
1d434622 1640
a52c2257
HJYP
1641 gtk_init(&argc, &argv);
1642
1643#ifdef ENABLE_NLS
1644 bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR );
1645 bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" );
1646 textdomain ( GETTEXT_PACKAGE );
1647#endif
1648
1649 XSetLocaleModifiers("");
2918994e 1650 XSetErrorHandler((XErrorHandler) panel_handle_x_error);
8110399f 1651
a52c2257 1652 resolve_atoms();
8110399f 1653
9df6826f
HJYP
1654 desktop_name = g_getenv("XDG_CURRENT_DESKTOP");
1655 is_in_lxde = desktop_name && (0 == strcmp(desktop_name, "LXDE"));
95095259 1656
a52c2257
HJYP
1657 for (i = 1; i < argc; i++) {
1658 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
1659 usage();
1660 exit(0);
1661 } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
1662 printf("lxpanel %s\n", version);
1663 exit(0);
1664 } else if (!strcmp(argv[i], "--log")) {
1665 i++;
1666 if (i == argc) {
1667 ERR( "lxpanel: missing log level\n");
1668 usage();
1669 exit(1);
1670 } else {
1671 log_level = atoi(argv[i]);
82ca42af 1672 log_level_set_on_commandline = true;
a52c2257
HJYP
1673 }
1674 } else if (!strcmp(argv[i], "--configure") || !strcmp(argv[i], "-C")) {
1675 config = 1;
1676 } else if (!strcmp(argv[i], "--profile") || !strcmp(argv[i], "-p")) {
1677 i++;
1678 if (i == argc) {
1679 ERR( "lxpanel: missing profile name\n");
1680 usage();
1681 exit(1);
1682 } else {
1683 cprofile = g_strdup(argv[i]);
1684 }
1685 } else {
1686 printf("lxpanel: unknown option - %s\n", argv[i]);
1687 usage();
1688 exit(1);
1689 }
1690 }
f277dbb7 1691
8110399f 1692 /* Check for duplicated lxpanel instances */
e68b47dc 1693 if (!check_main_lock() && !config) {
e7a42ecf 1694 printf("There is already an instance of LXPanel. Now to exit\n");
e68b47dc
JH
1695 exit(1);
1696 }
1697
730e3f7b
JL
1698 /* Add our own icons to the search path of icon theme */
1699 gtk_icon_theme_append_search_path( gtk_icon_theme_get_default(), PACKAGE_DATA_DIR "/lxpanel/images" );
1700
cf701cb7 1701 fbev = fb_ev_new();
e2957bd2 1702 win_grp = gtk_window_group_new();
22242ed4 1703
f7cb330e
HJYP
1704restart:
1705 is_restarting = FALSE;
1706
cf701cb7
HJYP
1707 load_global_config();
1708
1d9dc649
HJYP
1709 /* NOTE: StructureNotifyMask is required by XRandR
1710 * See init_randr_support() in gdkscreen-x11.c of gtk+ for detail.
1711 */
1712 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), StructureNotifyMask|SubstructureNotifyMask|PropertyChangeMask);
cf701cb7 1713 gdk_window_add_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
8110399f 1714
cf701cb7
HJYP
1715 if( G_UNLIKELY( ! start_all_panels() ) )
1716 g_warning( "Config files are not found.\n" );
8110399f
HJYP
1717/*
1718 * FIXME: configure??
6a6ad54e
HJYP
1719 if (config)
1720 configure();
8110399f 1721*/
6a6ad54e 1722 gtk_main();
8110399f 1723
cf701cb7
HJYP
1724 XSelectInput (GDK_DISPLAY(), GDK_ROOT_WINDOW(), NoEventMask);
1725 gdk_window_remove_filter(gdk_get_default_root_window (), (GdkFilterFunc)panel_event_filter, NULL);
1726
8110399f
HJYP
1727 /* destroy all panels */
1728 g_slist_foreach( all_panels, (GFunc) panel_destroy, NULL );
2a26c0d8
HJYP
1729 g_slist_free( all_panels );
1730 all_panels = NULL;
6a6ad54e 1731 g_free( cfgfile );
5541b8d2 1732
cf701cb7
HJYP
1733 free_global_config();
1734
f7cb330e
HJYP
1735 if( is_restarting )
1736 goto restart;
1737
3e71f8af
HG
1738 gdk_threads_leave();
1739
e2957bd2 1740 g_object_unref(win_grp);
22242ed4
HJYP
1741 g_object_unref(fbev);
1742
5541b8d2 1743 return 0;
a52c2257 1744}