Adding upstream version 0.8.0.
[debian/lxpanel.git] / src / misc.c
index b9abe39..73dd245 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
+ * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 #include <string.h>
 #include <stdio.h>
 #include <stdarg.h>
+#include <libfm/fm-gtk.h>
 
 #include "misc.h"
-#include "glib-mem.h"
-#include "panel.h"
+#include "private.h"
 
 #include "dbg.h"
 
 /* data used by themed images buttons */
-typedef struct
-{
-    char* fname;
+typedef struct {
+    FmIcon *icon;
     guint theme_changed_handler;
+    guint icon_changed_handler;
+    guint font_changed_handler;
     GdkPixbuf* pixbuf;
     GdkPixbuf* hilight;
     gulong hicolor;
-    int dw, dh; /* desired size */
-    gboolean keep_ratio;
-}ImgData;
+    gint size; /* desired size */
+    LXPanel *panel;
+    char *fallback;
+} ImgData;
 
 static GQuark img_data_id = 0;
 
-static void on_theme_changed(GtkIconTheme* theme, GtkWidget* img);
-void
-_gtk_image_set_from_file_scaled( GtkWidget* img, const gchar *file, gint width,
-        gint height, gboolean keep_ratio);
+static void _gtk_image_set_from_file_scaled(GtkWidget *img, ImgData *data);
 
 /* X11 data types */
 Atom a_UTF8_STRING;
@@ -73,6 +72,7 @@ Atom a_NET_DESKTOP_VIEWPORT;
 Atom a_NET_DESKTOP_NAMES;
 Atom a_NET_ACTIVE_WINDOW;
 Atom a_NET_CLOSE_WINDOW;
+Atom a_NET_SHOWING_DESKTOP;
 Atom a_NET_SUPPORTED;
 Atom a_NET_WM_STATE;
 Atom a_NET_WM_STATE_SKIP_TASKBAR;
@@ -98,8 +98,17 @@ Atom a_NET_WM_STRUT_PARTIAL;
 Atom a_NET_WM_ICON;
 Atom a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR;
 
+/* SYSTEM TRAY spec */
+Atom a_NET_SYSTEM_TRAY_OPCODE;
+Atom a_NET_SYSTEM_TRAY_MESSAGE_DATA;
+Atom a_NET_SYSTEM_TRAY_ORIENTATION;
+Atom a_MANAGER;
+
 Atom a_LXPANEL_CMD; /* for private client message */
 
+/* if current window manager is EWMH conforming. */
+gboolean is_ewmh_supported;
+
 enum{
     I_UTF8_STRING,
     I_XROOTPMAP_ID,
@@ -107,13 +116,16 @@ enum{
     I_WM_CLASS,
     I_WM_DELETE_WINDOW,
     I_WM_PROTOCOLS,
+
     I_NET_WORKAREA,
     I_NET_CLIENT_LIST,
     I_NET_CLIENT_LIST_STACKING,
     I_NET_NUMBER_OF_DESKTOPS,
     I_NET_CURRENT_DESKTOP,
+    I_NET_DESKTOP_VIEWPORT,
     I_NET_DESKTOP_NAMES,
     I_NET_ACTIVE_WINDOW,
+    I_NET_SHOWING_DESKTOP,
     I_NET_SUPPORTED,
 
     I_NET_WM_STATE,
@@ -141,15 +153,20 @@ enum{
     I_NET_WM_ICON,
     I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR,
 
+    I_NET_SYSTEM_TRAY_OPCODE,
+    I_NET_SYSTEM_TRAY_MESSAGE_DATA,
+    I_NET_SYSTEM_TRAY_ORIENTATION,
+    I_MANAGER,
+
     I_LXPANEL_CMD,
     N_ATOMS
 };
 
 pair allign_pair[] = {
-    { ALLIGN_NONE, "none" },
-    { ALLIGN_LEFT, "left" },
-    { ALLIGN_RIGHT, "right" },
-    { ALLIGN_CENTER, "center"},
+    { ALIGN_NONE, "none" },
+    { ALIGN_LEFT, "left" },
+    { ALIGN_RIGHT, "right" },
+    { ALIGN_CENTER, "center"},
     { 0, NULL },
 };
 
@@ -181,16 +198,9 @@ pair bool_pair[] = {
     { 1, "1" },
     { 0, NULL },
 };
-pair pos_pair[] = {
-    { POS_NONE, "none" },
-    { POS_START, "start" },
-    { POS_END,  "end" },
-    { 0, NULL},
-};
-
 
 int
-str2num(pair *p, gchar *str, int defval)
+str2num(pair *p, const gchar *str, int defval)
 {
     ENTER;
     for (;p && p->str; p++) {
@@ -200,8 +210,8 @@ str2num(pair *p, gchar *str, int defval)
     RET(defval);
 }
 
-gchar *
-num2str(pair *p, int num, gchar *defval)
+const gchar *
+num2str(pair *p, int num, const gchar *defval)
 {
     ENTER;
     for (;p && p->str; p++) {
@@ -294,49 +304,13 @@ lxpanel_get_line(char**fp, line *s)
             *tmp = 0;
             s->type = LINE_BLOCK_START;
         } else {
-            ERR( "parser: unknown token: '%c'\n", *tmp2);
+            g_warning( "parser: unknown token: '%c'", *tmp2);
         }
         break;
     }
     return s->type;
 }
 
-int
-get_line_as_is(char** fp, line *s)
-{
-    gchar *tmp, *tmp2;
-
-    ENTER;
-    if (!fp) {
-        s->type = LINE_NONE;
-        RET(s->type);
-    }
-    s->type = LINE_NONE;
-    while (buf_gets(s->str, s->len, fp)) {
-        g_strstrip(s->str);
-        if (s->str[0] == '#' || s->str[0] == 0)
-        continue;
-        DBG( ">> %s\n", s->str);
-        if (!g_ascii_strcasecmp(s->str, "}")) {
-            s->type = LINE_BLOCK_END;
-            DBG( "        : line_block_end\n");
-            break;
-        }
-        for (tmp = s->str; isalnum(*tmp); tmp++);
-        for (tmp2 = tmp; isspace(*tmp2); tmp2++);
-        if (*tmp2 == '=') {
-            s->type = LINE_VAR;
-        } else if  (*tmp2 == '{') {
-            s->type = LINE_BLOCK_START;
-        } else {
-            DBG( "        : ? <%c>\n", *tmp2);
-        }
-        break;
-    }
-    RET(s->type);
-
-}
-
 void resolve_atoms()
 {
     static const char* atom_names[ N_ATOMS ];
@@ -352,8 +326,10 @@ void resolve_atoms()
     atom_names[ I_NET_CLIENT_LIST_STACKING ] = "_NET_CLIENT_LIST_STACKING";
     atom_names[ I_NET_NUMBER_OF_DESKTOPS ] = "_NET_NUMBER_OF_DESKTOPS";
     atom_names[ I_NET_CURRENT_DESKTOP ] = "_NET_CURRENT_DESKTOP";
+    atom_names[ I_NET_DESKTOP_VIEWPORT ] = "_NET_DESKTOP_VIEWPORT";
     atom_names[ I_NET_DESKTOP_NAMES ] = "_NET_DESKTOP_NAMES";
     atom_names[ I_NET_ACTIVE_WINDOW ] = "_NET_ACTIVE_WINDOW";
+    atom_names[ I_NET_SHOWING_DESKTOP ] = "_NET_SHOWING_DESKTOP";
     atom_names[ I_NET_SUPPORTED ] = "_NET_SUPPORTED";
     atom_names[ I_NET_WM_DESKTOP ] = "_NET_WM_DESKTOP";
     atom_names[ I_NET_WM_STATE ] = "_NET_WM_STATE";
@@ -380,13 +356,19 @@ void resolve_atoms()
     atom_names[ I_NET_WM_STRUT_PARTIAL ] = "_NET_WM_STRUT_PARTIAL";
     atom_names[ I_NET_WM_ICON ] = "_NET_WM_ICON";
     atom_names[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ] = "_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR";
+
+    atom_names[ I_NET_SYSTEM_TRAY_OPCODE ] = "_NET_SYSTEM_TRAY_OPCODE";
+    atom_names[ I_NET_SYSTEM_TRAY_MESSAGE_DATA ] = "_NET_SYSTEM_TRAY_MESSAGE_DATA";
+    atom_names[ I_NET_SYSTEM_TRAY_ORIENTATION ] = "_NET_SYSTEM_TRAY_ORIENTATION";
+    atom_names[ I_MANAGER ] = "MANAGER";
+
     atom_names[ I_LXPANEL_CMD ] = "_LXPANEL_CMD";
 
     Atom atoms[ N_ATOMS ];
 
     ENTER;
-   
-    if( !  XInternAtoms( GDK_DISPLAY(), (char**)atom_names,
+
+    if( !  XInternAtoms( GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), (char**)atom_names,
             N_ATOMS, False, atoms ) )
     {
         g_warning( "Error: unable to return Atoms" );
@@ -399,13 +381,16 @@ void resolve_atoms()
     a_WM_CLASS = atoms[ I_WM_CLASS ];
     a_WM_DELETE_WINDOW = atoms[ I_WM_DELETE_WINDOW ];
     a_WM_PROTOCOLS = atoms[ I_WM_PROTOCOLS ];
+
     a_NET_WORKAREA = atoms[ I_NET_WORKAREA ];
     a_NET_CLIENT_LIST = atoms[ I_NET_CLIENT_LIST ];
     a_NET_CLIENT_LIST_STACKING = atoms[ I_NET_CLIENT_LIST_STACKING ];
     a_NET_NUMBER_OF_DESKTOPS = atoms[ I_NET_NUMBER_OF_DESKTOPS ];
     a_NET_CURRENT_DESKTOP = atoms[ I_NET_CURRENT_DESKTOP ];
+    a_NET_DESKTOP_VIEWPORT = atoms[ I_NET_DESKTOP_VIEWPORT ];
     a_NET_DESKTOP_NAMES = atoms[ I_NET_DESKTOP_NAMES ];
     a_NET_ACTIVE_WINDOW = atoms[ I_NET_ACTIVE_WINDOW ];
+    a_NET_SHOWING_DESKTOP = atoms[ I_NET_SHOWING_DESKTOP ];
     a_NET_SUPPORTED = atoms[ I_NET_SUPPORTED ];
     a_NET_WM_STATE = atoms[ I_NET_WM_STATE ];
     a_NET_WM_STATE_SKIP_TASKBAR = atoms[ I_NET_WM_STATE_SKIP_TASKBAR ];
@@ -424,6 +409,7 @@ void resolve_atoms()
     a_NET_WM_WINDOW_TYPE_DIALOG = atoms[ I_NET_WM_WINDOW_TYPE_DIALOG ];
     a_NET_WM_WINDOW_TYPE_NORMAL = atoms[ I_NET_WM_WINDOW_TYPE_NORMAL ];
     a_NET_WM_DESKTOP = atoms[ I_NET_WM_DESKTOP ];
+    a_NET_WM_PID = atoms[ I_NET_WM_PID ];
     a_NET_WM_NAME = atoms[ I_NET_WM_NAME ];
     a_NET_WM_VISIBLE_NAME = atoms[ I_NET_WM_VISIBLE_NAME ];
     a_NET_WM_STRUT = atoms[ I_NET_WM_STRUT ];
@@ -431,6 +417,11 @@ void resolve_atoms()
     a_NET_WM_ICON = atoms[ I_NET_WM_ICON ];
     a_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR = atoms[ I_KDE_NET_WM_SYSTEM_TRAY_WINDOW_FOR ];
 
+    a_NET_SYSTEM_TRAY_OPCODE = atoms[ I_NET_SYSTEM_TRAY_OPCODE ];
+    a_NET_SYSTEM_TRAY_MESSAGE_DATA = atoms [ I_NET_SYSTEM_TRAY_MESSAGE_DATA ];
+    a_NET_SYSTEM_TRAY_ORIENTATION = atoms[ I_NET_SYSTEM_TRAY_ORIENTATION ];
+    a_MANAGER = atoms[ I_MANAGER ];
+
     a_LXPANEL_CMD = atoms[ I_LXPANEL_CMD ];
 
     RET();
@@ -438,10 +429,9 @@ void resolve_atoms()
 
 
 void
-Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4)
+Xclimsg(Window win, Atom type, long l0, long l1, long l2, long l3, long l4)
 {
     XClientMessageEvent xev;
-
     xev.type = ClientMessage;
     xev.window = win;
     xev.message_type = type;
@@ -451,9 +441,9 @@ Xclimsg(Window win, long type, long l0, long l1, long l2, long l3, long l4)
     xev.data.l[2] = l2;
     xev.data.l[3] = l3;
     xev.data.l[4] = l4;
-    XSendEvent(GDK_DISPLAY(), GDK_ROOT_WINDOW(), False,
+    XSendEvent(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), GDK_ROOT_WINDOW(), False,
           (SubstructureNotifyMask | SubstructureRedirectMask),
-          (XEvent *) & xev);
+          (XEvent *) &xev);
 }
 
 void
@@ -467,7 +457,7 @@ Xclimsgwm(Window win, Atom type, Atom arg)
     xev.format = 32;
     xev.data.l[0] = arg;
     xev.data.l[1] = GDK_CURRENT_TIME;
-    XSendEvent(GDK_DISPLAY(), win, False, 0L, (XEvent *) &xev);
+    XSendEvent(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, False, 0L, (XEvent *) &xev);
 }
 
 
@@ -484,7 +474,7 @@ get_utf8_property(Window win, Atom atom)
 
     type = None;
     retval = NULL;
-    result = XGetWindowProperty (GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
+    result = XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
           a_UTF8_STRING, &type, &format, &nitems,
           &bytes_after, &tmp);
     if (result != Success || type == None)
@@ -503,15 +493,15 @@ char **
 get_utf8_property_list(Window win, Atom atom, int *count)
 {
     Atom type;
-    int format, i;
-    gulong nitems;
+    int format;
+    gulong nitems, i;
     gulong bytes_after;
     gchar *s, **retval = NULL;
     int result;
     guchar *tmp = NULL;
 
     *count = 0;
-    result = XGetWindowProperty(GDK_DISPLAY(), win, atom, 0, G_MAXLONG, False,
+    result = XGetWindowProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, atom, 0, G_MAXLONG, False,
           a_UTF8_STRING, &type, &format, &nitems,
           &bytes_after, &tmp);
     if (result != Success || type != a_UTF8_STRING || tmp == NULL)
@@ -525,7 +515,7 @@ get_utf8_property_list(Window win, Atom atom, int *count)
                 (*count)++;
         }
         retval = g_new0 (char*, *count + 2);
-        for (i = 0, s = val; i < *count; i++, s = s +  strlen (s) + 1) {
+        for (i = 0, s = val; (int)i < *count; i++, s = s +  strlen (s) + 1) {
             retval[i] = g_strdup(s);
         }
         if (val[nitems-1]) {
@@ -555,9 +545,9 @@ get_xaproperty (Window win, Atom prop, Atom type, int *nitems)
 
     ENTER;
     prop_data = NULL;
-    if (XGetWindowProperty (GDK_DISPLAY(), win, prop, 0, 0x7fffffff, False,
+    if (XGetWindowProperty (GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, prop, 0, G_MAXLONG, False,
               type, &type_ret, &format_ret, &items_ret,
-              &after_ret, &prop_data) != Success)
+              &after_ret, &prop_data) != Success || items_ret == 0)
     {
         if( G_UNLIKELY(prop_data) )
             XFree( prop_data );
@@ -579,7 +569,8 @@ text_property_to_utf8 (const XTextProperty *prop)
 
   ENTER;
   list = NULL;
-  count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
+  count = gdk_text_property_to_utf8_list_for_display (gdk_display_get_default(),
+                                          gdk_x11_xatom_to_atom (prop->encoding),
                                           prop->format,
                                           prop->value,
                                           prop->nitems,
@@ -604,7 +595,7 @@ get_textproperty(Window win, Atom atom)
     char *retval;
 
     ENTER;
-    if (XGetTextProperty(GDK_DISPLAY(), win, &text_prop, atom)) {
+    if (XGetTextProperty(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), win, &text_prop, atom)) {
         DBG("format=%d enc=%d nitems=%d value=%s   \n",
               text_prop.format,
               text_prop.encoding,
@@ -624,7 +615,7 @@ int
 get_net_number_of_desktops()
 {
     int desknum;
-    guint32 *data;
+    gulong *data;
 
     ENTER;
     data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_NUMBER_OF_DESKTOPS,
@@ -642,7 +633,7 @@ int
 get_net_current_desktop ()
 {
     int desk;
-    guint32 *data;
+    gulong *data;
 
     ENTER;
     data = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, XA_CARDINAL, 0);
@@ -658,7 +649,7 @@ int
 get_net_wm_desktop(Window win)
 {
     int desk = 0;
-    guint32 *data;
+    gulong *data;
 
     ENTER;
     data = get_xaproperty (win, a_NET_WM_DESKTOP, XA_CARDINAL, 0);
@@ -673,7 +664,7 @@ GPid
 get_net_wm_pid(Window win)
 {
     GPid pid = 0;
-    guint32 *data;
+    gulong *data;
 
     ENTER;
     data = get_xaproperty (win, a_NET_WM_PID, XA_CARDINAL, 0);
@@ -692,7 +683,7 @@ get_net_wm_state(Window win, NetWMState *nws)
 
 
     ENTER;
-    bzero(nws, sizeof(nws));
+    memset(nws, 0, sizeof(*nws));
     if (!(state = get_xaproperty(win, a_NET_WM_STATE, XA_ATOM, &num3)))
         RET();
 
@@ -730,7 +721,7 @@ get_net_wm_window_type(Window win, NetWMWindowType *nwwt)
 
 
     ENTER;
-    bzero(nwwt, sizeof(nwwt));
+    memset(nwwt, 0, sizeof(*nwwt));
     if (!(state = get_xaproperty(win, a_NET_WM_WINDOW_TYPE, XA_ATOM, &num3)))
         RET();
 
@@ -784,13 +775,29 @@ get_wm_state (Window win)
     RET(ret);
 }
 
+int panel_handle_x_error(Display * d, XErrorEvent * ev)
+{
+    char buf[256];
+
+    XGetErrorText(d, ev->error_code, buf, 256);
+    g_warning("lxpanel : X error: %s", buf);
+    return 0;    /* Ignored */
+}
+
+int panel_handle_x_error_swallow_BadWindow_BadDrawable(Display * d, XErrorEvent * ev)
+{
+    if ((ev->error_code != BadWindow) && (ev->error_code != BadDrawable))
+        panel_handle_x_error(d, ev);
+    return 0;    /* Ignored */
+}
+
 static void
-calculate_width(int scrw, int wtype, int allign, int margin,
+calculate_width(int scrw, int wtype, int align, int margin,
       int *panw, int *x)
 {
     ENTER;
     DBG("scrw=%d\n", scrw);
-    DBG("IN panw=%d\n", *panw);
+    DBG("IN panw=%d, margin=%d\n", *panw, margin);
     //scrw -= 2;
     if (wtype == WIDTH_PERCENT) {
         /* sanity check */
@@ -800,76 +807,87 @@ calculate_width(int scrw, int wtype, int allign, int margin,
             *panw = 1;
         *panw = ((gfloat) scrw * (gfloat) *panw) / 100.0;
     }
-    if (allign != ALLIGN_CENTER) {
+    if (align != ALIGN_CENTER) {
         if (margin > scrw) {
-            ERR( "margin is bigger then edge size %d > %d. Ignoring margin\n",
+            g_warning( "margin is bigger then edge size %d > %d. Ignoring margin",
                   margin, scrw);
             margin = 0;
         }
-        if (wtype == WIDTH_PERCENT)
-            //*panw = MAX(scrw - margin, *panw);
-            ;
-        else
-            *panw = MIN(scrw - margin, *panw);
+       *panw = MIN(scrw - margin, *panw);
     }
     DBG("OUT panw=%d\n", *panw);
-    if (allign == ALLIGN_LEFT)
+    if (align == ALIGN_LEFT)
         *x += margin;
-    else if (allign == ALLIGN_RIGHT) {
+    else if (align == ALIGN_RIGHT) {
         *x += scrw - *panw - margin;
         if (*x < 0)
             *x = 0;
-    } else if (allign == ALLIGN_CENTER)
+    } else if (align == ALIGN_CENTER)
         *x += (scrw - *panw) / 2;
     RET();
 }
 
 
-void
-calculate_position(Panel *np)
+void _calculate_position(LXPanel *panel, GdkRectangle *rect)
 {
-    int sswidth, ssheight, minx, miny;
+    Panel *np = panel->priv;
+    GdkScreen *screen;
+    GdkRectangle marea;
 
     ENTER;
-    /* FIXME: Why this doesn't work? */
-    if (0)  {
-//        if (np->curdesk < np->wa_len/4) {
-        minx = np->workarea[np->curdesk*4 + 0];
-        miny = np->workarea[np->curdesk*4 + 1];
-        sswidth  = np->workarea[np->curdesk*4 + 2];
-        ssheight = np->workarea[np->curdesk*4 + 3];
-    } else {
-        minx = miny = 0;
-        sswidth  = gdk_screen_get_width( gtk_widget_get_screen(np->topgwin) );
-        ssheight = gdk_screen_get_height( gtk_widget_get_screen(np->topgwin) );
+    screen = gdk_screen_get_default();
+    if (np->monitor < 0) /* all monitors */
+    {
+        marea.x = 0;
+        marea.y = 0;
+        marea.width = gdk_screen_get_width(screen);
+        marea.height = gdk_screen_get_height(screen);
+    }
+    else if (np->monitor < gdk_screen_get_n_monitors(screen))
+        gdk_screen_get_monitor_geometry(screen,np->monitor,&marea);
+    else
+    {
+        marea.x = 0;
+        marea.y = 0;
+        marea.width = 0;
+        marea.height = 0;
     }
 
     if (np->edge == EDGE_TOP || np->edge == EDGE_BOTTOM) {
-        np->aw = np->width;
-        np->ax = minx;
-        calculate_width(sswidth, np->widthtype, np->allign, np->margin,
-              &np->aw, &np->ax);
-        np->ah = np->height;
-        np->ah = MIN(PANEL_HEIGHT_MAX, np->ah);
-        np->ah = MAX(PANEL_HEIGHT_MIN, np->ah);
-        np->ay = miny + ((np->edge == EDGE_TOP) ? 0 : (ssheight - np->ah));
+        rect->width = np->width;
+        rect->x = marea.x;
+        calculate_width(marea.width, np->widthtype, np->align, np->margin,
+              &rect->width, &rect->x);
+        rect->height = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
+        rect->y = marea.y + ((np->edge == EDGE_TOP) ? 0 : (marea.height - rect->height));
 
     } else {
-        np->ah = np->width;
-        np->ay = miny;
-        calculate_width(ssheight, np->widthtype, np->allign, np->margin,
-              &np->ah, &np->ay);
-        np->aw = np->height;
-        np->aw = MIN(PANEL_HEIGHT_MAX, np->aw);
-        np->aw = MAX(PANEL_HEIGHT_MIN, np->aw);
-        np->ax = minx + ((np->edge == EDGE_LEFT) ? 0 : (sswidth - np->aw));
+        rect->height = np->width;
+        rect->y = marea.y;
+        calculate_width(marea.height, np->widthtype, np->align, np->margin,
+              &rect->height, &rect->y);
+        rect->width = ((( ! np->autohide) || (np->visible)) ? np->height : np->height_when_hidden);
+        rect->x = marea.x + ((np->edge == EDGE_LEFT) ? 0 : (marea.width - rect->width));
     }
     //g_debug("%s - x=%d y=%d w=%d h=%d\n", __FUNCTION__, np->ax, np->ay, np->aw, np->ah);
     RET();
 }
 
+void calculate_position(Panel *np)
+{
+    GdkRectangle rect;
+
+    rect.width = np->aw;
+    rect.height = np->ah;
+    _calculate_position(np->topgwin, &rect);
+    np->aw = rect.width;
+    np->ah = rect.height;
+    np->ax = rect.x;
+    np->ay = rect.y;
+}
+
 gchar *
-expand_tilda(gchar *file)
+expand_tilda(const gchar *file)
 {
     ENTER;
     RET((file[0] == '~') ?
@@ -878,54 +896,6 @@ expand_tilda(gchar *file)
 
 }
 
-#if 0
-Window
-Select_Window(Display *dpy)
-{
-    int status;
-    Cursor cursor;
-    XEvent event;
-    Window target_win = None, root = RootWindow(dpy,DefaultScreen(dpy));
-    int buttons = 0;
-
-    ENTER;
-    /* Make the target cursor */
-    cursor = XCreateFontCursor(dpy, XC_crosshair);
-
-    /* Grab the pointer using target cursor, letting it room all over */
-    status = XGrabPointer(dpy, root, False,
-          ButtonPressMask|ButtonReleaseMask, GrabModeSync,
-          GrabModeAsync, root, cursor, CurrentTime);
-    if (status != GrabSuccess) {
-        ERR("Can't grab the mouse.");
-        RET(None);
-    }
-    /* Let the user select a window... */
-    while ((target_win == None) || (buttons != 0)) {
-        /* allow one more event */
-        XAllowEvents(dpy, SyncPointer, CurrentTime);
-        XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event);
-        switch (event.type) {
-        case ButtonPress:
-            if (target_win == None) {
-                target_win = event.xbutton.subwindow; /* window selected */
-                DBG("target win = 0x%x\n", target_win);
-                if (target_win == None) target_win = root;
-            }
-            buttons++;
-            break;
-        case ButtonRelease:
-            if (buttons > 0) /* there may have been some down before we started */
-                buttons--;
-            break;
-        }
-    }
-
-    XUngrabPointer(dpy, CurrentTime);      /* Done with pointer */
-    RET(target_win);
-}
-#endif
-
 /*
  * SuxPanel version 0.1
  * Copyright (c) 2003 Leandro Pereira <leandro@linuxmag.com.br>
@@ -938,222 +908,255 @@ Select_Window(Display *dpy)
  *
  */
 
-GdkPixbuf *
-gdk_pixbuf_scale_ratio(GdkPixbuf *p, int width, int height, GdkInterpType itype, gboolean keep_ratio)
+/* DestroyNotify handler for image data in _gtk_image_new_from_file_scaled. */
+static void img_data_free(ImgData * data)
 {
-    gfloat w, h, rw, rh;
-
-    if (keep_ratio) {
-        w = gdk_pixbuf_get_width(p);
-        h = gdk_pixbuf_get_height(p);
-        rw = w / width;
-        rh = h / height;
-        if (rw > rh)
-            height = h / rw;
-        else
-            width =  w / rh;
+    g_object_unref(data->icon);
+    if (data->theme_changed_handler != 0)
+        g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
+    if (data->panel != NULL)
+    {
+        g_object_remove_weak_pointer(G_OBJECT(data->panel), (gpointer *)&data->panel);
+        g_signal_handler_disconnect(data->panel, data->icon_changed_handler);
+        if (data->font_changed_handler != 0)
+            g_signal_handler_disconnect(data->panel, data->font_changed_handler);
     }
-    return  gdk_pixbuf_scale_simple(p, width, height, itype);
-
-}
-
-void img_data_free( ImgData* data )
-{
-    g_free( data->fname );
-    if( data->theme_changed_handler )
-        g_signal_handler_disconnect( gtk_icon_theme_get_default(), data->theme_changed_handler );
-    if( data->pixbuf )
-        g_object_unref( data->pixbuf );
-    if( data->hilight )
-        g_object_unref( data->hilight );
-    g_slice_free( ImgData, data );
+    if (data->pixbuf != NULL)
+        g_object_unref(data->pixbuf);
+    if (data->hilight != NULL)
+        g_object_unref(data->hilight);
+    if (data->fallback != NULL)
+        g_free(data->fallback);
+    g_free(data);
 }
 
-static void on_theme_changed(GtkIconTheme* theme, GtkWidget* img)
+/* Handler for "changed" signal in _gtk_image_new_from_file_scaled. */
+static void on_theme_changed(GtkWidget * img, GObject * object)
 {
-    ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(img), img_data_id );
-    /* g_debug("reload icon: %s", data->fname); */
-    _gtk_image_set_from_file_scaled(img, data->fname,
-                    data->dw, data->dh, data->keep_ratio );
+    ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
+    _gtk_image_set_from_file_scaled(img, data);
 }
 
-void fb_button_set_from_file(GtkWidget* btn, const char* img_file)
+/* consumes reference on icon */
+static void _lxpanel_button_set_icon(GtkWidget* btn, FmIcon* icon, gint size)
 {
-    GtkWidget* child = gtk_bin_get_child(GTK_BIN(btn));
-    GtkWidget* img = NULL;
-
-    if( GTK_IS_IMAGE(child) )
+    /* Locate the image within the button. */
+    GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
+    GtkWidget * img = NULL;
+    if (GTK_IS_IMAGE(child))
         img = child;
-    else if( GTK_IS_BOX(child) )
+    else if (GTK_IS_BOX(child))
     {
-        GList* children = gtk_container_get_children(GTK_CONTAINER(child));
+        GList * children = gtk_container_get_children(GTK_CONTAINER(child));
         img = GTK_WIDGET(GTK_IMAGE(children->data));
-        g_list_free( children );
+        g_list_free(children);
     }
 
-    if(G_LIKELY(img))
+    if (img != NULL)
     {
-        ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(img), img_data_id );
-        g_free(data->fname);
-        data->fname = g_strdup(img_file);
-        _gtk_image_set_from_file_scaled(img, data->fname,
-                        data->dw, data->dh, data->keep_ratio );
+        ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
+
+        if (size == 0) /* 0: set as before; -1: reset to panel icon size */
+            size = data->size;
+        if (icon != data->icon || size != data->size) /* something was changed */
+        {
+            g_object_unref(data->icon);
+            data->icon = icon;
+            data->size = size;
+            _gtk_image_set_from_file_scaled(img, data);
+        }
+        else
+            g_object_unref(icon);
     }
+    else
+        g_object_unref(icon);
 }
 
-/* FIXME: currently, the size of those images cannot be changed dynamically */
-static void on_img_size_allocated(GtkWidget* img, GtkAllocation *allocation, ImgData* data)
+void lxpanel_button_set_icon(GtkWidget* btn, const gchar *name, gint size)
 {
-    int size;
+    _lxpanel_button_set_icon(btn, fm_icon_from_name(name), size);
+}
 
-    /* enlarge */
-    if( allocation->width > data->dw ||
-        allocation->height > data->dh )
-    {
-        size = MAX(allocation->width, allocation->height);
-    }
-    /* shrinkage */
-    else if( allocation->width < data->dw ||
-        allocation->height < data->dh )
+void lxpanel_button_update_icon(GtkWidget* btn, FmIcon *icon, gint size)
+{
+    _lxpanel_button_set_icon(btn, g_object_ref(icon), size);
+}
+
+gboolean lxpanel_button_set_label(GtkWidget *btn, const char *label)
+{
+    /* Locate the image within the button. */
+    GtkWidget * child = gtk_bin_get_child(GTK_BIN(btn));
+    GtkWidget * lbl = NULL;
+    GtkWidget * img = NULL;
+    ImgData * data = NULL;
+
+    if (GTK_IS_BOX(child))
     {
-        size = MIN(allocation->width, allocation->height);
+        GList * children = gtk_container_get_children(GTK_CONTAINER(child)), *l;
+        for (l = children; l; l = l->next)
+            if (GTK_IS_LABEL(l->data))
+                lbl = l->data;
+            else if (GTK_IS_IMAGE(l->data))
+                img = l->data;
+        g_list_free(children);
     }
+
+    if (G_UNLIKELY(lbl == NULL))
+        return FALSE;
+
+    if (G_LIKELY(img != NULL))
+        data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
+
+    if (G_LIKELY(data != NULL && data->panel != NULL))
+        lxpanel_draw_label_text(data->panel, lbl, label, FALSE, 1, TRUE);
     else
-        return;
-    data->dw = data->dh = size;
-/*
-    g_debug("size = %d, pix: %d, %d, alloc:%d, %d", size,
-       gdk_pixbuf_get_width(data->pixbuf), gdk_pixbuf_get_height(data->pixbuf),
-       allocation->width, allocation->height );
-*/
-    g_signal_handlers_block_by_func( img, on_img_size_allocated, data );
+        gtk_label_set_text(GTK_LABEL(lbl), label);
+    return TRUE;
+}
 
-    if (gdk_pixbuf_get_width(data->pixbuf)!=gdk_pixbuf_get_height(data->pixbuf)) {
-        gtk_widget_set_size_request(img, allocation->width, allocation->height);
-        _gtk_image_set_from_file_scaled( img, data->fname,
-                        allocation->width, allocation->height, data->keep_ratio );
-    } else {
-        gtk_widget_set_size_request(img, size, size);
-        _gtk_image_set_from_file_scaled( img, data->fname,
-                        size, size, data->keep_ratio );
-    }
-//    g_debug("size-allocated: %d, %d", allocation->width, allocation->height);
-
-#if 0
-    /* FIXME: This is a very bad dirty hack! */
-    if( gtk_events_pending() )
-        gtk_main_iteration();
-#endif
-    g_signal_handlers_unblock_by_func( img, on_img_size_allocated, data );
+/* parameters width and keep_ratio are unused, kept for backward compatibility */
+void fb_button_set_from_file(GtkWidget * btn, const char * img_file, gint width, gint height, gboolean keep_ratio)
+{
+    lxpanel_button_set_icon(btn, img_file, height);
 }
 
-void
-_gtk_image_set_from_file_scaled( GtkWidget* img, const gchar *file, gint width,
-        gint height, gboolean keep_ratio)
+static void _gtk_image_set_from_file_scaled(GtkWidget * img, ImgData * data)
 {
-    GdkPixbuf *pb_scaled;
-    gboolean themed = TRUE;
-    ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(img), img_data_id );
+    gint size = data->size;
 
-    data->dw = width;
-    data->dh = height;
+    if (size < 0 && data->panel)
+        size = data->panel->priv->icon_size;
 
-    if( data->pixbuf )
+    if (data->pixbuf != NULL)
     {
-        g_object_unref( data->pixbuf );
+        g_object_unref(data->pixbuf);
         data->pixbuf = NULL;
     }
+
     /* if there is a cached hilighted version of this pixbuf, free it */
-    if( data->hilight )
+    if (data->hilight != NULL)
     {
-        g_object_unref( data->hilight );
+        g_object_unref(data->hilight);
         data->hilight = NULL;
     }
 
-    /* if they are the same string, eliminate unnecessary copy. */
-    if( data->fname != file )
+    if (G_LIKELY(G_IS_THEMED_ICON(data->icon)))
+    {
+        const char *fallback = data->fallback;
+
+        if (fallback == NULL)
+            fallback = "application-x-executable";
+        data->pixbuf = fm_pixbuf_from_icon_with_fallback(data->icon, size, fallback);
+    }
+    else
     {
-        g_free( data->fname );
-        data->fname = g_strdup(file);
+        char *file = g_icon_to_string(fm_icon_get_gicon(data->icon));
+        data->pixbuf = gdk_pixbuf_new_from_file_at_scale(file, -1, size, TRUE, NULL);
+        g_free(file);
     }
-    data->keep_ratio = TRUE;
 
-    if( G_UNLIKELY( ! file ) )
-        goto err;
+    if (data->pixbuf == NULL && data->fallback != NULL && data->fallback[0] == '/')
+    {
+        /* if fallback was provided as a file path */
+        data->pixbuf = gdk_pixbuf_new_from_file_at_scale(data->fallback, -1, size, TRUE, NULL);
+    }
 
-    if( g_file_test(file, G_FILE_TEST_EXISTS) )
+    if (data->pixbuf != NULL)
     {
-        pb_scaled = gdk_pixbuf_new_from_file_at_scale( file, width, height,
-                                                       keep_ratio, NULL );
-        if( !pb_scaled )
-            goto err;
-        data->pixbuf = pb_scaled;
-        themed = FALSE;
+        /* Set the pixbuf into the image widget. */
+        gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
     }
     else
     {
-        data->pixbuf = lxpanel_load_icon(file, MAX(width, height),TRUE);
-        if( ! data->pixbuf )
-            goto err;
+        /* No pixbuf available.  Set the "missing image" icon. */
+        gtk_image_set_from_stock(GTK_IMAGE(img), GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON);
     }
-    gtk_image_set_from_pixbuf((GtkImage *)img, data->pixbuf);
+}
+
+/* consumes reference on icon */
+static GtkWidget *_gtk_image_new_for_icon(LXPanel *p, FmIcon *icon, gint size,
+                                          const char *fallback)
+{
+    GtkWidget * img = gtk_image_new();
+    ImgData * data = g_new0(ImgData, 1);
 
-    if( themed ) /* This image is loaded from icon theme */
+    data->icon = icon;
+    data->size = size;
+    data->fallback = g_strdup(fallback);
+    if (img_data_id == 0)
+        img_data_id = g_quark_from_static_string("ImgData");
+    g_object_set_qdata_full(G_OBJECT(img), img_data_id, data, (GDestroyNotify) img_data_free);
+    if (p && size < 0)
     {
-        /* update the image when icon theme get changed */
-        if( ! data->theme_changed_handler )
-        {
-            data->theme_changed_handler = g_signal_connect( gtk_icon_theme_get_default(), "changed",
-                                            G_CALLBACK(on_theme_changed), img );
-        }
+        data->panel = p;
+        data->icon_changed_handler = g_signal_connect_swapped(p, "icon-size-changed",
+                                                G_CALLBACK(on_theme_changed), img);
+        /* it is in fact not required if image is panel child but let be safe */
+        g_object_add_weak_pointer(G_OBJECT(p), (gpointer *)&data->panel);
     }
-    else /* this is not loaded from icon theme */
+    _gtk_image_set_from_file_scaled(img, data);
+    if (G_IS_THEMED_ICON(data->icon))
     {
-        if( data->theme_changed_handler )
-        {
-            g_signal_handler_disconnect( gtk_icon_theme_get_default(), data->theme_changed_handler );
-            data->theme_changed_handler = 0;
-        }
+        /* This image is loaded from icon theme.  Update the image if the icon theme is changed. */
+        data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
+                                                "changed", G_CALLBACK(on_theme_changed), img);
     }
-    return;
+    return img;
+}
 
- err:
-    gtk_image_set_from_stock((GtkImage *)img, GTK_STOCK_MISSING_IMAGE,
-                                   GTK_ICON_SIZE_BUTTON);
+/* parameters width and keep_ratio are unused, kept for backward compatibility */
+GtkWidget * _gtk_image_new_from_file_scaled(const gchar * file, gint width, gint height, gboolean keep_ratio)
+{
+    return _gtk_image_new_for_icon(NULL, fm_icon_from_name(file), height, NULL);
 }
 
-GtkWidget *
-_gtk_image_new_from_file_scaled(const gchar *file, gint width,
-        gint height, gboolean keep_ratio)
+GtkWidget *lxpanel_image_new_for_icon(LXPanel *panel, const gchar *name,
+                                      gint height, const char *fallback)
 {
-    GtkWidget *img;
-    ImgData* data;
+    return _gtk_image_new_for_icon(panel, fm_icon_from_name(name), height, fallback);
+}
 
-    img = gtk_image_new();
-    data = g_slice_new0( ImgData );
-    if( G_UNLIKELY( 0 == img_data_id ) )
-        img_data_id = g_quark_from_static_string("ImgData");
-    g_object_set_qdata_full( G_OBJECT(img), img_data_id, data, 
-            (GDestroyNotify)img_data_free );
-    _gtk_image_set_from_file_scaled( img, file, width, height, keep_ratio );
-//    gtk_widget_set_size_request(img, width, height);
-    g_signal_connect( img, "size-allocate", G_CALLBACK(on_img_size_allocated), data );
-    return img;
+GtkWidget *lxpanel_image_new_for_fm_icon(LXPanel *panel, FmIcon *icon,
+                                         gint height, const char *fallback)
+{
+    return _gtk_image_new_for_icon(panel, g_object_ref(icon), height, fallback);
 }
 
+gboolean lxpanel_image_change_icon(GtkWidget *img, const gchar *name, const char *fallback)
+{
+    ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(img), img_data_id);
+
+    g_return_val_if_fail(data != NULL && name != NULL, FALSE);
+    g_object_unref(data->icon);
+    g_free(data->fallback);
+    data->icon = fm_icon_from_name(name);
+    data->fallback = g_strdup(fallback);
+    if (!G_IS_THEMED_ICON(data->icon))
+    {
+        if (data->theme_changed_handler != 0)
+            g_signal_handler_disconnect(gtk_icon_theme_get_default(), data->theme_changed_handler);
+        data->theme_changed_handler = 0;
+    }
+    else if (data->theme_changed_handler == 0)
+    {
+        /* This image is loaded from icon theme.  Update the image if the icon theme is changed. */
+        data->theme_changed_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(),
+                                                "changed", G_CALLBACK(on_theme_changed), img);
+    }
+    _gtk_image_set_from_file_scaled(img, data);
+    return TRUE;
+}
 
 void
 get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
 {
     GtkWidget *b;
-    //gint focus_width;
-    //gint focus_pad;
 
     ENTER;
     b = gtk_button_new();
     gtk_widget_set_name(GTK_WIDGET(b), name);
-    GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
-    GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_DEFAULT);
+    gtk_widget_set_can_focus(b, FALSE);
+    gtk_widget_set_can_default(b, FALSE);
     gtk_container_set_border_width (GTK_CONTAINER (b), 0);
 
     if (parent)
@@ -1170,15 +1173,9 @@ get_button_spacing(GtkRequisition *req, GtkContainer *parent, gchar *name)
 guint32 gcolor2rgb24(GdkColor *color)
 {
     guint32 i;
-    guint16 r, g, b;
 
     ENTER;
 
-    r = color->red * 0xFF / 0xFFFF;
-    g = color->green * 0xFF / 0xFFFF;
-    b = color->blue * 0xFF / 0xFFFF;
-    DBG("%x %x %x ==> %x %x %x\n", color->red, color->green, color->blue, r, g, b);
-
     i = (color->red * 0xFF / 0xFFFF) & 0xFF;
     i <<= 8;
     i |= (color->green * 0xFF / 0xFFFF) & 0xFF;
@@ -1188,187 +1185,173 @@ guint32 gcolor2rgb24(GdkColor *color)
     RET(i);
 }
 
-
-static gboolean
-fb_button_enter (GtkImage *widget, GdkEventCrossing *event)
+/* Handler for "enter-notify-event" signal on image that has highlighting requested. */
+static gboolean fb_button_enter(GtkImage * widget, GdkEventCrossing * event)
 {
-    GdkPixbuf *dark, *light;
-    int i, height, rowstride;
-    gulong hicolor;
-    guchar *src, *up, extra[3];
-    ImgData* data;
-
-    if (gtk_image_get_storage_type(widget) != GTK_IMAGE_PIXBUF)
-        return TRUE;
-
-    data = (ImgData*)g_object_get_qdata( G_OBJECT(widget), img_data_id );
-    if( G_UNLIKELY( ! data ) )
-        return TRUE;
-
-    if( ! data->hilight )
+    if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
     {
-        dark = data->pixbuf;
-        height = gdk_pixbuf_get_height( dark );
-        rowstride = gdk_pixbuf_get_rowstride( dark );
-        hicolor = data->hicolor;
-
-        light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
-        if( !light )
-            return TRUE;
-        src = gdk_pixbuf_get_pixels(light);
-        for (i = 2; i >= 0; i--, hicolor >>= 8)
-            extra[i] = hicolor & 0xFF;
-        for( up = src + height * rowstride; src < up; src+=4 )
+        ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
+        if (data != NULL)
         {
-            if (src[3] == 0)
-                continue;
-            for (i = 0; i < 3; i++)
+            if (data->hilight == NULL)
             {
-                if (src[i] + extra[i] >= 255)
-                    src[i] = 255;
-                else
-                    src[i] += extra[i];
+                GdkPixbuf * dark = data->pixbuf;
+                int height = gdk_pixbuf_get_height(dark);
+                int rowstride = gdk_pixbuf_get_rowstride(dark);
+                gulong hicolor = data->hicolor;
+
+                GdkPixbuf * light = gdk_pixbuf_add_alpha(dark, FALSE, 0, 0, 0);
+                if (light != NULL)
+                {
+                    guchar extra[3];
+                    int i;
+                    for (i = 2; i >= 0; i--, hicolor >>= 8)
+                        extra[i] = hicolor & 0xFF;
+
+                    guchar * src = gdk_pixbuf_get_pixels(light);
+                    guchar * up;
+                    for (up = src + height * rowstride; src < up; src += 4)
+                    {
+                        if (src[3] != 0)
+                        {
+                            for (i = 0; i < 3; i++)
+                            {
+                            int value = src[i] + extra[i];
+                            if (value > 255) value = 255;
+                            src[i] = value;
+                            }
+                        }
+                    }
+                data->hilight = light;
+                }
             }
+
+        if (data->hilight != NULL)
+            gtk_image_set_from_pixbuf(widget, data->hilight);
         }
-        data->hilight = light;
     }
-
-    if( G_LIKELY( data->hilight ) )
-        gtk_image_set_from_pixbuf(widget, data->hilight);
     return TRUE;
 }
 
-static gboolean
-fb_button_leave (GtkImage *widget, GdkEventCrossing *event, gpointer user_data)
+/* Handler for "leave-notify-event" signal on image that has highlighting requested. */
+static gboolean fb_button_leave(GtkImage * widget, GdkEventCrossing * event, gpointer user_data)
 {
-    ImgData* data;
-
-    if (gtk_image_get_storage_type(widget) != GTK_IMAGE_PIXBUF)
-        return TRUE;
-
-    data = (ImgData*)g_object_get_qdata( G_OBJECT(widget), img_data_id );
-    if( data && data->pixbuf )
-        gtk_image_set_from_pixbuf(widget, data->pixbuf);
-
+    if (gtk_image_get_storage_type(widget) == GTK_IMAGE_PIXBUF)
+    {
+        ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(widget), img_data_id);
+        if ((data != NULL) && (data->pixbuf != NULL))
+            gtk_image_set_from_pixbuf(widget, data->pixbuf);
+    }
     return TRUE;
 }
 
+static void on_font_changed(LXPanel * panel, GtkLabel * lbl)
+{
+    const char *label = gtk_label_get_text(lbl);
+    lxpanel_draw_label_text(panel, GTK_WIDGET(lbl), label, FALSE, 1, TRUE);
+}
 
-GtkWidget *
-fb_button_new_from_file(gchar *fname, int width, int height, gulong hicolor, gboolean keep_ratio)
+static GtkWidget *_lxpanel_button_compose(GtkWidget *event_box, GtkWidget *image,
+                                          gulong highlight_color, const gchar *label)
 {
-    GtkWidget *b, *image;
-    ENTER;
-    b = gtk_event_box_new();
-    gtk_container_set_border_width(GTK_CONTAINER(b), 0);
-    GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
+    ImgData * data = (ImgData *) g_object_get_qdata(G_OBJECT(image), img_data_id);
 
-    image = _gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
-    gtk_misc_set_padding (GTK_MISC(image), 0, 0);
-    gtk_misc_set_alignment( (GtkMisc*)image, 0, 0 );
+    gtk_misc_set_padding(GTK_MISC(image), 0, 0);
+    gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.5);
+    if (highlight_color != 0 && data != NULL)
+    {
+        data->hicolor = highlight_color;
+        gtk_widget_add_events(event_box, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
+        g_signal_connect_swapped(G_OBJECT(event_box), "enter-notify-event", G_CALLBACK(fb_button_enter), image);
+        g_signal_connect_swapped(G_OBJECT(event_box), "leave-notify-event", G_CALLBACK(fb_button_leave), image);
+    }
 
-    if(hicolor > 0)
+    if (label == NULL)
+        gtk_container_add(GTK_CONTAINER(event_box), image);
+    else
     {
-        ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(image), img_data_id );
-        data->hicolor = hicolor;
-
-        gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
-        g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
-              G_CALLBACK (fb_button_enter), image);
-        g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
-              G_CALLBACK (fb_button_leave), image);
+        GtkWidget *inner, *lbl;
+
+        inner = gtk_hbox_new(FALSE, 0);
+        gtk_container_set_border_width(GTK_CONTAINER(inner), 0);
+        gtk_widget_set_can_focus(inner, FALSE);
+        gtk_container_add(GTK_CONTAINER(event_box), inner);
+
+        gtk_box_pack_start(GTK_BOX(inner), image, FALSE, FALSE, 0);
+
+        lbl = gtk_label_new("");
+        if (G_LIKELY(data != NULL && data->panel != NULL))
+        {
+            lxpanel_draw_label_text(data->panel, lbl, label, FALSE, 1, TRUE);
+            data->font_changed_handler = g_signal_connect(data->panel,
+                                                          "panel-font-changed",
+                                                          G_CALLBACK(on_font_changed),
+                                                          lbl);
+        }
+        else
+            gtk_label_set_text(GTK_LABEL(lbl), label);
+        gtk_misc_set_padding(GTK_MISC(lbl), 2, 0);
+        gtk_box_pack_end(GTK_BOX(inner), lbl, FALSE, FALSE, 0);
     }
-    gtk_container_add(GTK_CONTAINER(b), image);
-    gtk_widget_show(image);
-    gtk_widget_show(b);
-    RET(b);
+
+    gtk_widget_show_all(event_box);
+    return event_box;
 }
 
-GtkWidget *
-fb_button_new_from_file_with_colorlabel(gchar *fname, int width, int height,
-      gulong hicolor, gulong fcolor, gboolean keep_ratio, gchar *name)
+GtkWidget *lxpanel_button_compose(GtkWidget *event_box, GtkWidget *image,
+                                  GdkColor *color, const gchar *label)
 {
-    GtkWidget *b, *image, *box, *label;
+    gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
+    return _lxpanel_button_compose(event_box, image, highlight_color, label);
+}
 
-    ENTER;
-    b = gtk_event_box_new();
-    gtk_container_set_border_width(GTK_CONTAINER(b), 0);
-    GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
-
-    box = gtk_hbox_new(FALSE, 0);
-    gtk_container_set_border_width(GTK_CONTAINER(box), 0);
-    GTK_WIDGET_UNSET_FLAGS (box, GTK_CAN_FOCUS);
-    gtk_container_add(GTK_CONTAINER(b), box);
-
-    image = _gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
-    gtk_misc_set_padding (GTK_MISC(image), 0, 0);
-    if(hicolor > 0)
-    {
-        ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(image), img_data_id );
-        data->hicolor = hicolor;
-
-        gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
-        g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
-              G_CALLBACK (fb_button_enter), image);
-        g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
-              G_CALLBACK (fb_button_leave), image);
-    }
-    gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
-    if (name) {
-        label =  gtk_label_new("");
-        char *lname = g_strdup_printf("<span color=\"#%06x\">%s</span>", fcolor, name);
-       gtk_label_set_markup(GTK_LABEL(label), lname);
-        gtk_misc_set_padding(GTK_MISC(label), 2, 0);
-       g_free(lname);
-        gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 0);
-    }
-    gtk_widget_show_all(b);
-    RET(b);
+/* consumes reference on icon */
+static GtkWidget *_lxpanel_button_new_for_icon(LXPanel *panel, FmIcon *icon,
+                                               gint size, gulong highlight_color,
+                                               const gchar *label)
+{
+    GtkWidget * event_box = gtk_event_box_new();
+    gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
+    gtk_widget_set_can_focus(event_box, FALSE);
+
+    GtkWidget * image = _gtk_image_new_for_icon(panel, icon, size, NULL);
+    return _lxpanel_button_compose(event_box, image, highlight_color, label);
 }
 
-GtkWidget *
-fb_button_new_from_file_with_label(gchar *fname, int width, int height,
-      gulong hicolor, gboolean keep_ratio, gchar *name)
+GtkWidget *lxpanel_button_new_for_icon(LXPanel *panel, const gchar *name, GdkColor *color, const gchar *label)
 {
-    GtkWidget *b, *image, *box, *label;
+    gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
+    return _lxpanel_button_new_for_icon(panel, fm_icon_from_name(name), -1,
+                                        highlight_color, label);
+}
 
-    ENTER;
-    b = gtk_event_box_new();
-    gtk_container_set_border_width(GTK_CONTAINER(b), 0);
-    GTK_WIDGET_UNSET_FLAGS (b, GTK_CAN_FOCUS);
-
-    box = gtk_hbox_new(FALSE, 0);
-    gtk_container_set_border_width(GTK_CONTAINER(box), 0);
-    GTK_WIDGET_UNSET_FLAGS (box, GTK_CAN_FOCUS);
-    gtk_container_add(GTK_CONTAINER(b), box);
-
-    image = _gtk_image_new_from_file_scaled(fname, width, height, keep_ratio);
-    gtk_misc_set_padding (GTK_MISC(image), 0, 0);
-    if(hicolor > 0)
-    {
-        ImgData* data = (ImgData*)g_object_get_qdata( G_OBJECT(image), img_data_id );
-        data->hicolor = hicolor;
-
-        gtk_widget_add_events(b, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
-        g_signal_connect_swapped (G_OBJECT (b), "enter-notify-event",
-              G_CALLBACK (fb_button_enter), image);
-        g_signal_connect_swapped (G_OBJECT (b), "leave-notify-event",
-              G_CALLBACK (fb_button_leave), image);
-    }
-    gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE, 0);
-    if (name) {
-        label =  gtk_label_new(name);
-        gtk_misc_set_padding(GTK_MISC(label), 2, 0);
-        gtk_box_pack_end(GTK_BOX(box), label, FALSE, FALSE, 0);
-    }
-    gtk_widget_show_all(b);
-    RET(b);
+GtkWidget *lxpanel_button_new_for_fm_icon(LXPanel *panel, FmIcon *icon, GdkColor *color, const gchar *label)
+{
+    gulong highlight_color = color ? gcolor2rgb24(color) : PANEL_ICON_HIGHLIGHT;
+    return _lxpanel_button_new_for_icon(panel, g_object_ref(icon), -1,
+                                        highlight_color, label);
+}
+
+/* parameters width and keep_ratio are unused, kept for backward compatibility */
+GtkWidget * fb_button_new_from_file(
+    const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio)
+{
+    return fb_button_new_from_file_with_label(image_file, width, height, highlight_color, keep_ratio, NULL, NULL);
+}
+
+/* parameters width and keep_ratio are unused, kept for backward compatibility */
+GtkWidget * fb_button_new_from_file_with_label(
+    const gchar * image_file, int width, int height, gulong highlight_color, gboolean keep_ratio, Panel * panel, const gchar * label)
+{
+    return _lxpanel_button_new_for_icon(panel->topgwin, fm_icon_from_name(image_file), height, highlight_color, label);
 }
 
 char* translate_exec_to_cmd( const char* exec, const char* icon,
                              const char* title, const char* fpath )
 {
     GString* cmd = g_string_sized_new( 256 );
+    if (!exec)
+           return NULL;
     for( ; *exec; ++exec )
     {
         if( G_UNLIKELY(*exec == '%') )
@@ -1379,7 +1362,10 @@ char* translate_exec_to_cmd( const char* exec, const char* icon,
             switch( *exec )
             {
                 case 'c':
-                    g_string_append( cmd, title );
+                    if( title )
+                    {
+                        g_string_append( cmd, title );
+                    }
                     break;
                 case 'i':
                     if( icon )
@@ -1389,12 +1375,13 @@ char* translate_exec_to_cmd( const char* exec, const char* icon,
                     }
                     break;
                 case 'k':
-                {
-                    char* uri = g_filename_to_uri( fpath, NULL, NULL );
-                    g_string_append( cmd, uri );
-                    g_free( uri );
+                    if( fpath )
+                    {
+                        char* uri = g_filename_to_uri( fpath, NULL, NULL );
+                        g_string_append( cmd, uri );
+                        g_free( uri );
+                    }
                     break;
-                }
                 case '%':
                     g_string_append_c( cmd, '%' );
                     break;
@@ -1414,166 +1401,30 @@ char* translate_exec_to_cmd( const char* exec, const char* icon,
  any plugin with a layout box. Since GtkHBox cannot be changed to GtkVBox,
  recreating a new box to replace the old one is required.
 */
+/* for compatibility with old plugins */
 GtkWidget* recreate_box( GtkBox* oldbox, GtkOrientation orientation )
 {
-    GtkBox* newbox;
-    GList *child, *children;
-    GtkWidget* (*my_box_new) (gboolean homogeneous, gint spacing);
-
-    if( GTK_IS_HBOX(oldbox) ) {
-        if( orientation == GTK_ORIENTATION_HORIZONTAL )
-            return GTK_WIDGET(oldbox);
-    }
-    else {
-        if( orientation == GTK_ORIENTATION_VERTICAL )
-            return GTK_WIDGET(oldbox);
-    }
-    my_box_new = (orientation == GTK_ORIENTATION_HORIZONTAL ? gtk_hbox_new : gtk_vbox_new);
-
-    newbox = GTK_BOX(my_box_new( gtk_box_get_homogeneous(oldbox),
-                                 gtk_box_get_spacing(oldbox) ));
-    gtk_container_set_border_width (GTK_CONTAINER (newbox),
-                                    gtk_container_get_border_width(GTK_CONTAINER(oldbox)) );
-    children = gtk_container_get_children( GTK_CONTAINER (oldbox) );
-    for( child = children; child; child = child->next ) {
-        gboolean expand, fill;
-        guint padding;
-        GtkWidget* w = GTK_WIDGET(child->data);
-        gtk_box_query_child_packing( oldbox, w,
-                                     &expand, &fill, &padding, NULL );
-        /* g_debug( "repack %s, expand=%d, fill=%d", gtk_widget_get_name(w), expand, fill ); */
-        g_object_ref( w );
-        gtk_container_remove( GTK_CONTAINER (oldbox), w );
-        gtk_box_pack_start( newbox, w, expand, fill, padding );
-        g_object_unref( w );
-    }
-    g_list_free( children );
-    gtk_widget_show_all( GTK_WIDGET(newbox) );
-    gtk_widget_destroy( GTK_WIDGET(oldbox) );
-    return GTK_WIDGET(newbox);
+    gtk_orientable_set_orientation(GTK_ORIENTABLE(oldbox), orientation);
+    return GTK_WIDGET(oldbox);
 }
 
+/* for compatibility with old plugins */
 void show_error( GtkWindow* parent_win, const char* msg )
 {
-    GtkWidget* dlg = gtk_message_dialog_new( parent_win,
-                                             GTK_DIALOG_MODAL,
-                                             GTK_MESSAGE_ERROR,
-                                             GTK_BUTTONS_OK, msg );
-    gtk_dialog_run( (GtkDialog*)dlg );
-    gtk_widget_destroy( dlg );
-}
-
-static GdkPixbuf* load_icon_file( const char* file_name, int size )
-{
-    GdkPixbuf* icon = NULL;
-    char* file_path;
-    const gchar** dirs = (const gchar**) g_get_system_data_dirs();
-    const gchar** dir;
-    for( dir = dirs; *dir; ++dir )
-    {
-        file_path = g_build_filename( *dir, "pixmaps", file_name, NULL );
-        icon = gdk_pixbuf_new_from_file_at_scale( file_path, size, size, TRUE, NULL );
-        g_free( file_path );
-        if( icon )
-            break;
-    }
-    return icon;
+    fm_show_error(parent_win, NULL, msg);
 }
 
-static GdkPixbuf* vfs_load_icon( GtkIconTheme* theme, const char* icon_name, int size )
+/* old plugins compatibility mode, use fm_pixbuf_from_icon_with_fallback() instead */
+GdkPixbuf * lxpanel_load_icon(const char * name, int width, int height, gboolean use_fallback)
 {
-    GdkPixbuf* icon = NULL;
-    const char* file;
-    GtkIconInfo* inf = gtk_icon_theme_lookup_icon( theme, icon_name, size,
-                                             GTK_ICON_LOOKUP_USE_BUILTIN );
-    if( G_UNLIKELY( ! inf ) )
-        return NULL;
-    file = gtk_icon_info_get_filename( inf );
-    if( G_LIKELY( file ) )
-    {
-        /* icon = gdk_pixbuf_new_from_file_at_scale( file, size, size, TRUE, NULL ); */
-        icon = gdk_pixbuf_new_from_file( file, NULL );
-    }
-    else
-        icon = gtk_icon_info_get_builtin_pixbuf( inf );
-    gtk_icon_info_free( inf );
-
-    if( G_LIKELY( icon ) )  /* scale down the icon if it's too big */
-    {
-        int width, height;
-        height = gdk_pixbuf_get_height(icon);
-        width = gdk_pixbuf_get_width(icon);
-        if( G_UNLIKELY( height > size || width > size ) )
-        {
-            GdkPixbuf* scaled;
-            if( height > width )
-            {
-                width = size * height / width;
-                height = size;
-            }
-            else if( height < width )
-            {
-                height = size * width / height;
-                width = size;
-            }
-            else
-                height = width = size;
-            scaled = gdk_pixbuf_scale_simple( icon, width, height, GDK_INTERP_BILINEAR );
-            g_object_unref( icon );
-            icon = scaled;
-        }
-    }
-    return icon;
-}
-
-GdkPixbuf* lxpanel_load_icon( const char* name, int size, gboolean use_fallback )
-{
-    GtkIconTheme* theme;
-    char *icon_name = NULL, *suffix;
-    GdkPixbuf* icon = NULL;
-    if( name )
-    {
-        if( g_path_is_absolute( name) )
-        {
-            icon = gdk_pixbuf_new_from_file_at_scale( name,
-                                                     size, size, TRUE, NULL );
-        }
-        else
-        {
-            theme = gtk_icon_theme_get_default();
-            suffix = strchr( name, '.' );
-            if( suffix
-                && (0 == g_strcasecmp(++suffix, "png")
-                || 0 == g_strcasecmp(suffix, "svg")
-                || 0 == g_strcasecmp(suffix, "xpm")) ) /* has file extension, it's a basename of icon file */
-            {
-                /* try to find it in pixmaps dirs */
-                icon = load_icon_file( name, size );
-                if( G_UNLIKELY( ! icon ) )  /* unfortunately, not found */
-                {
-                    /* Let's remove the suffix, and see if this name can match an icon
-                         in current icon theme */
-                    icon_name = g_strndup( name,
-                                           (suffix - name - 1) );
-                    icon = vfs_load_icon( theme, icon_name, size );
-                    g_free( icon_name );
-                }
-            }
-            else  /* no file extension, it could be an icon name in the icon theme */
-            {
-                icon = vfs_load_icon( theme, name, size );
-            }
-        }
-    }
-    if( G_UNLIKELY( ! icon ) && use_fallback )  /* fallback to generic icon */
-    {
-        theme = gtk_icon_theme_get_default();
-        icon = vfs_load_icon( theme, "application-x-executable", size );
-        if( G_UNLIKELY( ! icon ) )  /* fallback to generic icon */
-        {
-            icon = vfs_load_icon( theme, "gnome-mime-application-x-executable", size );
-        }
-    }
+    FmIcon * fm_icon;
+    GdkPixbuf * icon = NULL;
+
+    fm_icon = fm_icon_from_name(name ? name : "application-x-executable");
+    /* well, we don't use parameter width and not really use cache here */
+    icon = fm_pixbuf_from_icon_with_fallback(fm_icon, height,
+                            use_fallback ? "application-x-executable" : NULL);
+    g_object_unref(fm_icon);
     return icon;
 }
 
@@ -1608,7 +1459,8 @@ static char* translate_app_exec_to_command_line( const char* pexec,
                     file = g_shell_quote( tmp );
                     g_free( tmp );
                     g_string_append( cmd, file );
-                    g_string_append_c( cmd, ' ' );
+                    if (l->next)
+                        g_string_append_c( cmd, ' ' );
                     g_free( file );
                 }
                 add_files = TRUE;
@@ -1632,7 +1484,8 @@ static char* translate_app_exec_to_command_line( const char* pexec,
                     file = (char*)l->data;
                     tmp = g_shell_quote( file );
                     g_string_append( cmd, tmp );
-                    g_string_append_c( cmd, ' ' );
+                    if (l->next)
+                        g_string_append_c( cmd, ' ' );
                     g_free( tmp );
                 }
                 add_files = TRUE;
@@ -1655,7 +1508,8 @@ static char* translate_app_exec_to_command_line( const char* pexec,
                     file = g_shell_quote( tmp );
                     g_free( tmp );
                     g_string_append( cmd, file );
-                    g_string_append_c( cmd, ' ' );
+                    if (l->next)
+                        g_string_append_c( cmd, ' ' );
                     g_free( file );
                 }
                 add_files = TRUE;
@@ -1708,13 +1562,12 @@ static char* translate_app_exec_to_command_line( const char* pexec,
 _finish:
     if( ! add_files )
     {
-        g_string_append_c ( cmd, ' ' );
         for( l = file_list; l; l = l->next )
         {
+            g_string_append_c( cmd, ' ' );
             file = (char*)l->data;
             tmp = g_shell_quote( file );
             g_string_append( cmd, tmp );
-            g_string_append_c( cmd, ' ' );
             g_free( tmp );
         }
     }
@@ -1722,7 +1575,32 @@ _finish:
     return g_string_free( cmd, FALSE );
 }
 
-gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal)
+gboolean spawn_command_async(GtkWindow *parent_window, gchar const* workdir,
+        gchar const* cmd)
+{
+    GError* err = NULL;
+    gchar** argv = NULL;
+
+    g_info("lxpanel: spawning \"%s\"...", cmd);
+
+    g_shell_parse_argv(cmd, NULL, &argv, &err);
+    if (!err)
+        g_spawn_async(workdir, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
+
+    if (err)
+    {
+        g_warning("%s\n", err->message);
+        fm_show_error(parent_window, NULL, err->message);
+        g_error_free(err);
+    }
+
+    g_strfreev(argv);
+
+    return !err;
+}
+
+/* FIXME: this should be replaced with fm_launch_file_simple() */
+gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal, char const* in_workdir)
 {
     GError *error = NULL;
     char* cmd;
@@ -1731,22 +1609,24 @@ gboolean lxpanel_launch_app(const char* exec, GList* files, gboolean in_terminal
     cmd = translate_app_exec_to_command_line(exec, files);
     if( in_terminal )
     {
+       char * escaped_cmd = g_shell_quote(cmd);
         char* term_cmd;
-        const char* term = lxpanel_get_terminal();
+        const char* term = fm_config->terminal ? fm_config->terminal : "lxterminal";
         if( strstr(term, "%s") )
-            term_cmd = g_strdup_printf(term, cmd);
+            term_cmd = g_strdup_printf(term, escaped_cmd);
         else
-            term_cmd = g_strconcat( term, " -e ", cmd, NULL );
+            term_cmd = g_strconcat( term, " -e ", escaped_cmd, NULL );
+       g_free(escaped_cmd);
         if( cmd != exec )
             g_free(cmd);
         cmd = term_cmd;
     }
-    if (! g_spawn_command_line_async(cmd, &error) ) {
-        ERR("can't spawn %s\nError is %s\n", cmd, error->message);
-        g_error_free (error);
-    }
+
+    spawn_command_async(NULL, in_workdir, cmd);
+
     g_free(cmd);
 
     return (error == NULL);
 }
 
+/* vim: set sw=4 et sts=4 : */