Merging icons branch on master branch.
[xmenu] / xmenu.c
diff --git a/xmenu.c b/xmenu.c
index 2e9f389..6f75c2c 100644 (file)
--- a/xmenu.c
+++ b/xmenu.c
@@ -8,10 +8,12 @@
 #include <X11/Xresource.h>
 #include <X11/XKBlib.h>
 #include <X11/Xft/Xft.h>
 #include <X11/Xresource.h>
 #include <X11/XKBlib.h>
 #include <X11/Xft/Xft.h>
+#include <Imlib2.h>
 
 #define PROGNAME "xmenu"
 #define ITEMPREV 0
 #define ITEMNEXT 1
 
 #define PROGNAME "xmenu"
 #define ITEMPREV 0
 #define ITEMNEXT 1
+#define IMGPADDING 8
 
 /* macros */
 #define LEN(x) (sizeof (x) / sizeof (x[0]))
 
 /* macros */
 #define LEN(x) (sizeof (x) / sizeof (x[0]))
@@ -45,12 +47,14 @@ struct Geometry {
 struct Item {
        char *label;            /* string to be drawed on menu */
        char *output;           /* string to be outputed when item is clicked */
 struct Item {
        char *label;            /* string to be drawed on menu */
        char *output;           /* string to be outputed when item is clicked */
+       char *file;             /* filename of the image */
        int y;                  /* item y position relative to menu */
        int h;                  /* item height */
        size_t labellen;        /* strlen(label) */
        struct Item *prev;      /* previous item */
        struct Item *next;      /* next item */
        struct Menu *submenu;   /* submenu spawned by clicking on item */
        int y;                  /* item y position relative to menu */
        int h;                  /* item height */
        size_t labellen;        /* strlen(label) */
        struct Item *prev;      /* previous item */
        struct Item *next;      /* next item */
        struct Menu *submenu;   /* submenu spawned by clicking on item */
+       Imlib_Image image;
 };
 
 /* menu structure */
 };
 
 /* menu structure */
@@ -71,9 +75,9 @@ static void getresources(void);
 static void getcolor(const char *s, XftColor *color);
 static void setupdc(void);
 static void calcgeom(struct Geometry *geom);
 static void getcolor(const char *s, XftColor *color);
 static void setupdc(void);
 static void calcgeom(struct Geometry *geom);
-static struct Item *allocitem(const char *label, const char *output);
+static struct Item *allocitem(const char *label, const char *output, char *file);
 static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
 static struct Menu *allocmenu(struct Menu *parent, struct Item *list, unsigned level);
-static struct Menu *buildmenutree(unsigned level, const char *label, const char *output);
+static struct Menu *buildmenutree(unsigned level, const char *label, const char *output, char *file);
 static struct Menu *parsestdin(void);
 static void setupmenusize(struct Geometry *geom, struct Menu *menu);
 static void setupmenupos(struct Geometry *geom, struct Menu *menu);
 static struct Menu *parsestdin(void);
 static void setupmenusize(struct Geometry *geom, struct Menu *menu);
 static void setupmenupos(struct Geometry *geom, struct Menu *menu);
@@ -131,6 +135,13 @@ main(int argc, char *argv[])
        rootwin = RootWindow(dpy, screen);
        colormap = DefaultColormap(dpy, screen);
 
        rootwin = RootWindow(dpy, screen);
        colormap = DefaultColormap(dpy, screen);
 
+       /* imlib2 stuff */
+       imlib_set_cache_size(2048 * 1024);
+       imlib_context_set_dither(1);
+       imlib_context_set_display(dpy);
+       imlib_context_set_visual(visual);
+       imlib_context_set_colormap(colormap);
+
        /* setup */
        getresources();
        setupdc();
        /* setup */
        getresources();
        setupdc();
@@ -249,7 +260,7 @@ calcgeom(struct Geometry *geom)
 
 /* allocate an item */
 static struct Item *
 
 /* allocate an item */
 static struct Item *
-allocitem(const char *label, const char *output)
+allocitem(const char *label, const char *output, char *file)
 {
        struct Item *item;
 
 {
        struct Item *item;
 
@@ -268,6 +279,12 @@ allocitem(const char *label, const char *output)
                                err(1, "strdup");
                }
        }
                                err(1, "strdup");
                }
        }
+       if (file == NULL) {
+               item->file = NULL;
+       } else {
+               if ((item->file = strdup(file)) == NULL)
+                       err(1, "strdup");
+       }
        item->y = 0;
        item->h = 0;
        if (item->label == NULL)
        item->y = 0;
        item->h = 0;
        if (item->label == NULL)
@@ -276,6 +293,7 @@ allocitem(const char *label, const char *output)
                item->labellen = strlen(item->label);
        item->next = NULL;
        item->submenu = NULL;
                item->labellen = strlen(item->label);
        item->next = NULL;
        item->submenu = NULL;
+       item->image = NULL;
 
        return item;
 }
 
        return item;
 }
@@ -316,7 +334,7 @@ allocmenu(struct Menu *parent, struct Item *list, unsigned level)
 
 /* build the menu tree */
 static struct Menu *
 
 /* build the menu tree */
 static struct Menu *
-buildmenutree(unsigned level, const char *label, const char *output)
+buildmenutree(unsigned level, const char *label, const char *output, char *file)
 {
        static struct Menu *prevmenu = NULL;    /* menu the previous item was added to */
        static struct Menu *rootmenu = NULL;    /* menu to be returned */
 {
        static struct Menu *prevmenu = NULL;    /* menu the previous item was added to */
        static struct Menu *rootmenu = NULL;    /* menu to be returned */
@@ -326,7 +344,7 @@ buildmenutree(unsigned level, const char *label, const char *output)
        unsigned i;
 
        /* create the item */
        unsigned i;
 
        /* create the item */
-       curritem = allocitem(label, output);
+       curritem = allocitem(label, output, file);
 
        /* put the item in the menu tree */
        if (prevmenu == NULL) {                 /* there is no menu yet */
 
        /* put the item in the menu tree */
        if (prevmenu == NULL) {                 /* there is no menu yet */
@@ -379,7 +397,7 @@ parsestdin(void)
 {
        struct Menu *rootmenu;
        char *s, buf[BUFSIZ];
 {
        struct Menu *rootmenu;
        char *s, buf[BUFSIZ];
-       char *label, *output;
+       char *file, *label, *output;
        unsigned level = 0;
 
        rootmenu = NULL;
        unsigned level = 0;
 
        rootmenu = NULL;
@@ -392,6 +410,13 @@ parsestdin(void)
                s = level + buf;
                label = strtok(s, "\t\n");
 
                s = level + buf;
                label = strtok(s, "\t\n");
 
+               /* get the filename */
+               file = NULL;
+               if (label != NULL && strncmp(label, "IMG:", 4) == 0) {
+                       file = label + 4;
+                       label = strtok(NULL, "\t\n");
+               }
+
                /* get the output */
                output = strtok(NULL, "\n");
                if (output == NULL) {
                /* get the output */
                output = strtok(NULL, "\n");
                if (output == NULL) {
@@ -401,12 +426,36 @@ parsestdin(void)
                                output++;
                }
 
                                output++;
                }
 
-               rootmenu = buildmenutree(level, label, output);
+               rootmenu = buildmenutree(level, label, output, file);
        }
 
        return rootmenu;
 }
 
        }
 
        return rootmenu;
 }
 
+/* load and scale image */
+static Imlib_Image
+loadimage(const char *file, int size)
+{
+       Imlib_Image image;
+       int width;
+       int height;
+       int imgsize;
+
+       image = imlib_load_image(file);
+       if (image == NULL)
+               errx(1, "cannot load image %s", file);
+
+       imlib_context_set_image(image);
+
+       width = imlib_image_get_width();
+       height = imlib_image_get_height();
+       imgsize = MIN(width, height);
+
+       image = imlib_create_cropped_scaled_image(0, 0, imgsize, imgsize, size, size);
+
+       return image;
+}
+
 /* setup the size of a menu and the position of its items */
 static void
 setupmenusize(struct Geometry *geom, struct Menu *menu)
 /* setup the size of a menu and the position of its items */
 static void
 setupmenusize(struct Geometry *geom, struct Menu *menu)
@@ -428,8 +477,12 @@ setupmenusize(struct Geometry *geom, struct Menu *menu)
                /* get length of item->label rendered in the font */
                XftTextExtentsUtf8(dpy, dc.font, (XftChar8 *)item->label,
                                   item->labellen, &ext);
                /* get length of item->label rendered in the font */
                XftTextExtentsUtf8(dpy, dc.font, (XftChar8 *)item->label,
                                   item->labellen, &ext);
-               labelwidth = ext.xOff + dc.font->height * 2;
+               labelwidth = ext.xOff + dc.font->height * 2 + IMGPADDING * 2;
                menu->w = MAX(menu->w, labelwidth);
                menu->w = MAX(menu->w, labelwidth);
+
+               /* create image */
+               if (item->file != NULL)
+                       item->image = loadimage(item->file, dc.font->height);
        }
 }
 
        }
 }
 
@@ -639,7 +692,7 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
 {
        int x, y;
 
 {
        int x, y;
 
-       x = dc.font->height;
+       x = dc.font->height + IMGPADDING;
        y = item->y + item->h/2 + dc.font->ascent/2 - 1;
        XSetForeground(dpy, dc.gc, color[ColorFG].pixel);
        XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font,
        y = item->y + item->h/2 + dc.font->ascent/2 - 1;
        XSetForeground(dpy, dc.gc, color[ColorFG].pixel);
        XftDrawStringUtf8(menu->draw, &color[ColorFG], dc.font,
@@ -647,7 +700,7 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
 
        /* draw triangle, if item contains a submenu */
        if (item->submenu != NULL) {
 
        /* draw triangle, if item contains a submenu */
        if (item->submenu != NULL) {
-               x = menu->w - dc.font->height/2 - triangle_width/2;
+               x = menu->w - dc.font->height/2 - IMGPADDING/2 - triangle_width/2 - 1;
                y = item->y + item->h/2 - triangle_height/2 - 1;
 
                XPoint triangle[] = {
                y = item->y + item->h/2 - triangle_height/2 - 1;
 
                XPoint triangle[] = {
@@ -660,6 +713,15 @@ drawitem(struct Menu *menu, struct Item *item, XftColor *color)
                XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
                             Convex, CoordModeOrigin);
        }
                XFillPolygon(dpy, menu->pixmap, dc.gc, triangle, LEN(triangle),
                             Convex, CoordModeOrigin);
        }
+
+       /* draw image */
+       if (item->file != NULL) {
+               x = IMGPADDING / 2;
+               y = item->y + (item->h - dc.font->height) / 2;
+               imlib_context_set_drawable(menu->pixmap);
+               imlib_context_set_image(item->image);
+               imlib_render_image_on_drawable(x, y);
+       }
 }
 
 /* draw items of the current menu and of its ancestors */
 }
 
 /* draw items of the current menu and of its ancestors */
@@ -845,10 +907,17 @@ freemenu(struct Menu *menu)
                if (item->submenu != NULL)
                        freemenu(item->submenu);
                tmp = item;
                if (item->submenu != NULL)
                        freemenu(item->submenu);
                tmp = item;
-               item = item->next;
                if (tmp->label != tmp->output)
                        free(tmp->label);
                free(tmp->output);
                if (tmp->label != tmp->output)
                        free(tmp->label);
                free(tmp->output);
+               if (tmp->file != NULL) {
+                       free(tmp->file);
+                       if (tmp->image != NULL) {
+                               imlib_context_set_image(tmp->image);
+                               imlib_free_image();
+                       }
+               }
+               item = item->next;
                free(tmp);
        }
 
                free(tmp);
        }