Support for 1-9 numbers to select specific item
[xmenu] / xmenu.c
diff --git a/xmenu.c b/xmenu.c
index 56d470b..e74c3c7 100644 (file)
--- a/xmenu.c
+++ b/xmenu.c
@@ -96,7 +96,6 @@ static Atom wmdelete;
 static Atom netatom[NetLast];
 
 /* flags */
 static Atom netatom[NetLast];
 
 /* flags */
-static int fflag = 0;   /* whether glyphs should align based on the first font */
 static int iflag = 0;   /* whether to disable icons */
 static int mflag = 0;   /* whether the user specified a monitor with -p */
 static int pflag = 0;   /* whether the user specified a position with -p */
 static int iflag = 0;   /* whether to disable icons */
 static int mflag = 0;   /* whether the user specified a monitor with -p */
 static int pflag = 0;   /* whether the user specified a position with -p */
@@ -118,11 +117,8 @@ main(int argc, char *argv[])
        XClassHint classh;
        int ch;
 
        XClassHint classh;
        int ch;
 
-       while ((ch = getopt(argc, argv, "fip:w")) != -1) {
+       while ((ch = getopt(argc, argv, "ip:w")) != -1) {
                switch (ch) {
                switch (ch) {
-               case 'f':
-                       fflag = 1;
-                       break;
                case 'i':
                        iflag = 1;
                        break;
                case 'i':
                        iflag = 1;
                        break;
@@ -146,7 +142,7 @@ main(int argc, char *argv[])
 
        /* open connection to server and set X variables */
        if ((dpy = XOpenDisplay(NULL)) == NULL)
 
        /* open connection to server and set X variables */
        if ((dpy = XOpenDisplay(NULL)) == NULL)
-               errx(1, "cannot open display");
+               errx(1, "could not open display");
        screen = DefaultScreen(dpy);
        visual = DefaultVisual(dpy, screen);
        rootwin = RootWindow(dpy, screen);
        screen = DefaultScreen(dpy);
        visual = DefaultVisual(dpy, screen);
        rootwin = RootWindow(dpy, screen);
@@ -260,9 +256,8 @@ parsefonts(const char *s)
                i = 0;
                while (isspace(*p))
                        p++;
                i = 0;
                while (isspace(*p))
                        p++;
-               while (i < sizeof buf && *p != '\0' && *p != ',') {
+               while (i < sizeof buf && *p != '\0' && *p != ',')
                        buf[i++] = *p++;
                        buf[i++] = *p++;
-               }
                if (i >= sizeof buf)
                        errx(1, "font name too long");
                if (*p == ',')
                if (i >= sizeof buf)
                        errx(1, "font name too long");
                if (*p == ',')
@@ -272,7 +267,7 @@ parsefonts(const char *s)
                        if ((dc.pattern = FcNameParse((FcChar8 *)buf)) == NULL)
                                errx(1, "the first font in the cache must be loaded from a font string");
                if ((dc.fonts[nfont++] = XftFontOpenName(dpy, screen, buf)) == NULL)
                        if ((dc.pattern = FcNameParse((FcChar8 *)buf)) == NULL)
                                errx(1, "the first font in the cache must be loaded from a font string");
                if ((dc.fonts[nfont++] = XftFontOpenName(dpy, screen, buf)) == NULL)
-                       errx(1, "cannot load font");
+                       errx(1, "could not load font");
        }
 }
 
        }
 }
 
@@ -281,7 +276,7 @@ static void
 ealloccolor(const char *s, XftColor *color)
 {
        if(!XftColorAllocName(dpy, visual, colormap, s, color))
 ealloccolor(const char *s, XftColor *color)
 {
        if(!XftColorAllocName(dpy, visual, colormap, s, color))
-               errx(1, "cannot allocate color: %s", s);
+               errx(1, "could not allocate color: %s", s);
 }
 
 /* query monitor information and cursor position */
 }
 
 /* query monitor information and cursor position */
@@ -305,7 +300,7 @@ initmonitor(void)
        if ((info = XineramaQueryScreens(dpy, &nmons)) != NULL) {
                int selmon = 0;
 
        if ((info = XineramaQueryScreens(dpy, &nmons)) != NULL) {
                int selmon = 0;
 
-               if (!mflag || (mflag && (config.monitor < 0 || config.monitor >= nmons))) {
+               if (!mflag || config.monitor < 0 || config.monitor >= nmons) {
                        for (i = 0; i < nmons; i++) {
                                if (BETWEEN(cursx, info[i].x_org, info[i].x_org + info[i].width) &&
                                    BETWEEN(cursy, info[i].y_org, info[i].y_org + info[i].height)) {
                        for (i = 0; i < nmons; i++) {
                                if (BETWEEN(cursx, info[i].x_org, info[i].x_org + info[i].width) &&
                                    BETWEEN(cursy, info[i].y_org, info[i].y_org + info[i].height)) {
@@ -321,6 +316,8 @@ initmonitor(void)
                mon.y = info[selmon].y_org;
                mon.w = info[selmon].width;
                mon.h = info[selmon].height;
                mon.y = info[selmon].y_org;
                mon.w = info[selmon].width;
                mon.h = info[selmon].height;
+
+               XFree(info);
        }
 
        if (!pflag) {
        }
 
        if (!pflag) {
@@ -336,9 +333,9 @@ initmonitor(void)
 static void
 initresources(void)
 {
 static void
 initresources(void)
 {
-       char *xrm;
        long n;
        char *type;
        long n;
        char *type;
+       char *xrm;
        XrmDatabase xdb;
        XrmValue xval;
 
        XrmDatabase xdb;
        XrmValue xval;
 
@@ -519,7 +516,7 @@ buildmenutree(unsigned level, const char *label, const char *output, char *file)
                          menu = menu->parent, i++)
                        ;
                if (menu == NULL)
                          menu = menu->parent, i++)
                        ;
                if (menu == NULL)
-                       errx(1, "reached NULL menu");
+                       errx(1, "improper indentation detected");
 
                /* find last item in the new menu */
                for (item = menu->list; item->next != NULL; item = item->next)
 
                /* find last item in the new menu */
                for (item = menu->list; item->next != NULL; item = item->next)
@@ -648,11 +645,11 @@ getnextutf8char(const char *s, const char **next_ret)
 static XftFont *
 getfontucode(FcChar32 ucode)
 {
 static XftFont *
 getfontucode(FcChar32 ucode)
 {
-       FcCharSet *fccharset;
-       FcPattern *fcpattern;
-       FcPattern *match;
+       FcCharSet *fccharset = NULL;
+       FcPattern *fcpattern = NULL;
+       FcPattern *match = NULL;
+       XftFont *retfont = NULL;
        XftResult result;
        XftResult result;
-       XftFont *retfont;
        size_t i;
 
        for (i = 0; i < dc.nfonts; i++)
        size_t i;
 
        for (i = 0; i < dc.nfonts; i++)
@@ -663,14 +660,18 @@ getfontucode(FcChar32 ucode)
        fccharset = FcCharSetCreate();
        FcCharSetAddChar(fccharset, ucode);
 
        fccharset = FcCharSetCreate();
        FcCharSetAddChar(fccharset, ucode);
 
-       /* create a pattern akin to the dc.pattern but containing our code point */
-       fcpattern = FcPatternDuplicate(dc.pattern);
-       FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+       /* create a pattern akin to the dc.pattern but containing our charset */
+       if (fccharset) {
+               fcpattern = FcPatternDuplicate(dc.pattern);
+               FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
+       }
 
        /* find pattern matching fcpattern */
 
        /* find pattern matching fcpattern */
-       FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
-       FcDefaultSubstitute(fcpattern);
-       match = XftFontMatch(dpy, screen, fcpattern, &result);
+       if (fcpattern) {
+               FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
+               FcDefaultSubstitute(fcpattern);
+               match = XftFontMatch(dpy, screen, fcpattern, &result);
+       }
 
        /* if found a pattern, open its font */
        if (match) {
 
        /* if found a pattern, open its font */
        if (match) {
@@ -678,8 +679,8 @@ getfontucode(FcChar32 ucode)
                if (retfont && XftCharExists(dpy, retfont, ucode) == FcTrue) {
                        if ((dc.fonts = realloc(dc.fonts, dc.nfonts+1)) == NULL)
                                err(1, "realloc");
                if (retfont && XftCharExists(dpy, retfont, ucode) == FcTrue) {
                        if ((dc.fonts = realloc(dc.fonts, dc.nfonts+1)) == NULL)
                                err(1, "realloc");
-                       dc.fonts[dc.nfonts++] = retfont;
-                       return retfont;
+                       dc.fonts[dc.nfonts] = retfont;
+                       return dc.fonts[dc.nfonts++];
                } else {
                        XftFontClose(dpy, retfont);
                }
                } else {
                        XftFontClose(dpy, retfont);
                }
@@ -694,9 +695,6 @@ static int
 drawtext(XftDraw *draw, XftColor *color, int x, int y, unsigned h, const char *text)
 {
        int textwidth = 0;
 drawtext(XftDraw *draw, XftColor *color, int x, int y, unsigned h, const char *text)
 {
        int textwidth = 0;
-       int texty;
-
-       texty = y + (h - (dc.fonts[0]->ascent + dc.fonts[0]->descent))/2 + dc.fonts[0]->ascent;
 
        while (*text) {
                XftFont *currfont;
 
        while (*text) {
                XftFont *currfont;
@@ -713,8 +711,9 @@ drawtext(XftDraw *draw, XftColor *color, int x, int y, unsigned h, const char *t
                textwidth += ext.xOff;
 
                if (draw) {
                textwidth += ext.xOff;
 
                if (draw) {
-                       if (!fflag)
-                               texty = y + (h - (currfont->ascent + currfont->descent))/2 + currfont->ascent;
+                       int texty;
+
+                       texty = y + (h - (currfont->ascent + currfont->descent))/2 + currfont->ascent;
                        XftDrawStringUtf8(draw, color, currfont, x, texty, (XftChar8 *)text, len);
                        x += ext.xOff;
                }
                        XftDrawStringUtf8(draw, color, currfont, x, texty, (XftChar8 *)text, len);
                        x += ext.xOff;
                }
@@ -867,7 +866,7 @@ grabpointer(void)
                        return;
                nanosleep(&ts, NULL);
        }
                        return;
                nanosleep(&ts, NULL);
        }
-       errx(1, "cannot grab keyboard");
+       errx(1, "could not grab pointer");
 }
 
 /* try to grab keyboard, we may have to wait for another process to ungrab */
 }
 
 /* try to grab keyboard, we may have to wait for another process to ungrab */
@@ -883,7 +882,7 @@ grabkeyboard(void)
                        return;
                nanosleep(&ts, NULL);
        }
                        return;
                nanosleep(&ts, NULL);
        }
-       errx(1, "cannot grab keyboard");
+       errx(1, "could not grab keyboard");
 }
 
 /* load and scale icon */
 }
 
 /* load and scale icon */
@@ -891,13 +890,55 @@ static Imlib_Image
 loadicon(const char *file)
 {
        Imlib_Image icon;
 loadicon(const char *file)
 {
        Imlib_Image icon;
+       Imlib_Load_Error errcode;
+       const char *errstr;
        int width;
        int height;
        int imgsize;
 
        int width;
        int height;
        int imgsize;
 
-       icon = imlib_load_image(file);
-       if (icon == NULL)
-               errx(1, "cannot load icon %s", file);
+       icon = imlib_load_image_with_error_return(file, &errcode);
+       if (*file == '\0') {
+               warnx("could not load icon (file name is blank)");
+               return NULL;
+       } else if (icon == NULL) {
+               switch (errcode) {
+               case IMLIB_LOAD_ERROR_FILE_DOES_NOT_EXIST:
+                       errstr = "file does not exist";
+                       break;
+               case IMLIB_LOAD_ERROR_FILE_IS_DIRECTORY:
+                       errstr = "file is directory";
+                       break;
+               case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_READ:
+               case IMLIB_LOAD_ERROR_PERMISSION_DENIED_TO_WRITE:
+                       errstr = "permission denied";
+                       break;
+               case IMLIB_LOAD_ERROR_NO_LOADER_FOR_FILE_FORMAT:
+                       errstr = "unknown file format";
+                       break;
+               case IMLIB_LOAD_ERROR_PATH_TOO_LONG:
+                       errstr = "path too long";
+                       break;
+               case IMLIB_LOAD_ERROR_PATH_COMPONENT_NON_EXISTANT:
+               case IMLIB_LOAD_ERROR_PATH_COMPONENT_NOT_DIRECTORY:
+               case IMLIB_LOAD_ERROR_PATH_POINTS_OUTSIDE_ADDRESS_SPACE:
+                       errstr = "improper path";
+                       break;
+               case IMLIB_LOAD_ERROR_TOO_MANY_SYMBOLIC_LINKS:
+                       errstr = "too many symbolic links";
+                       break;
+               case IMLIB_LOAD_ERROR_OUT_OF_MEMORY:
+                       errstr = "out of memory";
+                       break;
+               case IMLIB_LOAD_ERROR_OUT_OF_FILE_DESCRIPTORS:
+                       errstr = "out of file descriptors";
+                       break;
+               default:
+                       errstr = "unknown error";
+                       break;
+               }
+               warnx("could not load icon (%s): %s", errstr, file);
+               return NULL;
+       }
 
        imlib_context_set_image(icon);
 
 
        imlib_context_set_image(icon);
 
@@ -974,15 +1015,21 @@ drawitems(struct Menu *menu)
                                             Convex, CoordModeOrigin);
                        }
 
                                             Convex, CoordModeOrigin);
                        }
 
-                       /* draw icon */
-                       if (item->file != NULL && !iflag) {
+                       /* try to load icon */
+                       if (item->file && !iflag) {
                                item->icon = loadicon(item->file);
                                item->icon = loadicon(item->file);
+                               free(item->file);
+                       }
 
 
+                       /* draw icon if properly loaded */
+                       if (item->icon) {
                                imlib_context_set_image(item->icon);
                                imlib_context_set_drawable(item->sel);
                                imlib_render_image_on_drawable(config.horzpadding, config.iconpadding);
                                imlib_context_set_drawable(item->unsel);
                                imlib_render_image_on_drawable(config.horzpadding, config.iconpadding);
                                imlib_context_set_image(item->icon);
                                imlib_context_set_drawable(item->sel);
                                imlib_render_image_on_drawable(config.horzpadding, config.iconpadding);
                                imlib_context_set_drawable(item->unsel);
                                imlib_render_image_on_drawable(config.horzpadding, config.iconpadding);
+                               imlib_context_set_image(item->icon);
+                               imlib_free_image();
                        }
                }
        }
                        }
                }
        }
@@ -1103,38 +1150,53 @@ getitem(struct Menu *menu, int y)
 static struct Item *
 itemcycle(struct Menu *currmenu, int direction)
 {
 static struct Item *
 itemcycle(struct Menu *currmenu, int direction)
 {
-       struct Item *item;
+       struct Item *item = NULL;
        struct Item *lastitem;
 
        struct Item *lastitem;
 
-       item = NULL;
+       for (lastitem = currmenu->list; lastitem && lastitem->next; lastitem = lastitem->next)
+               ;
 
 
-       if (direction == ITEMNEXT) {
+       /* select item (either separator or labeled item) in given direction */
+       switch (direction) {
+       case ITEMNEXT:
                if (currmenu->selected == NULL)
                        item = currmenu->list;
                else if (currmenu->selected->next != NULL)
                        item = currmenu->selected->next;
                if (currmenu->selected == NULL)
                        item = currmenu->list;
                else if (currmenu->selected->next != NULL)
                        item = currmenu->selected->next;
-
-               while (item != NULL && item->label == NULL)
-                       item = item->next;
-
-               if (item == NULL)
-                       item = currmenu->list;
-       } else {
-               for (lastitem = currmenu->list;
-                    lastitem != NULL && lastitem->next != NULL;
-                    lastitem = lastitem->next)
-                       ;
-
+               break;
+       case ITEMPREV:
                if (currmenu->selected == NULL)
                        item = lastitem;
                else if (currmenu->selected->prev != NULL)
                        item = currmenu->selected->prev;
                if (currmenu->selected == NULL)
                        item = lastitem;
                else if (currmenu->selected->prev != NULL)
                        item = currmenu->selected->prev;
+               break;
+       case ITEMFIRST:
+               item = currmenu->list;
+               break;
+       case ITEMLAST:
+               item = lastitem;
+               break;
+       }
 
 
+       /*
+        * the selected item can be a separator
+        * let's select the closest labeled item (ie., one that isn't a separator)
+        */
+       switch (direction) {
+       case ITEMNEXT:
+       case ITEMFIRST:
+               while (item != NULL && item->label == NULL)
+                       item = item->next;
+               if (item == NULL)
+                       item = currmenu->list;
+               break;
+       case ITEMPREV:
+       case ITEMLAST:
                while (item != NULL && item->label == NULL)
                        item = item->prev;
                while (item != NULL && item->label == NULL)
                        item = item->prev;
-
                if (item == NULL)
                        item = lastitem;
                if (item == NULL)
                        item = lastitem;
+               break;
        }
 
        return item;
        }
 
        return item;
@@ -1210,15 +1272,26 @@ selectitem:
 
                        /* cycle through menu */
                        item = NULL;
 
                        /* cycle through menu */
                        item = NULL;
-                       if (ksym == XK_ISO_Left_Tab || ksym == XK_Up) {
+                       if (ksym == XK_Home || ksym == KSYMFIRST) {
+                               item = itemcycle(currmenu, ITEMFIRST);
+                       } else if (ksym == XK_End || ksym == KSYMLAST) {
+                               item = itemcycle(currmenu, ITEMLAST);
+                       } else if (ksym == XK_ISO_Left_Tab || ksym == XK_Up || ksym == KSYMUP) {
                                item = itemcycle(currmenu, ITEMPREV);
                                item = itemcycle(currmenu, ITEMPREV);
-                       } else if (ksym == XK_Tab || ksym == XK_Down) {
+                       } else if (ksym == XK_Tab || ksym == XK_Down || ksym == KSYMDOWN) {
                                item = itemcycle(currmenu, ITEMNEXT);
                                item = itemcycle(currmenu, ITEMNEXT);
-                       } else if ((ksym == XK_Return || ksym == XK_Right) &&
+                       } else if (ksym >= XK_1 && ksym <= XK_9){
+                                item = itemcycle(currmenu, ITEMFIRST);
+                                currmenu->selected = item;
+                                for(int i = ksym-XK_1; i > 0; i-=1){
+                                    item = itemcycle(currmenu, ITEMNEXT);
+                                    currmenu->selected = item;
+                                }
+                        } else if ((ksym == XK_Return || ksym == XK_Right || ksym == KSYMRIGHT) &&
                                   currmenu->selected != NULL) {
                                item = currmenu->selected;
                                goto selectitem;
                                   currmenu->selected != NULL) {
                                item = currmenu->selected;
                                goto selectitem;
-                       } else if ((ksym == XK_Escape || ksym == XK_Left) &&
+                       } else if ((ksym == XK_Escape || ksym == XK_Left || ksym == KSYMLEFT) &&
                                   currmenu->parent != NULL) {
                                item = currmenu->parent->selected;
                                currmenu = currmenu->parent;
                                   currmenu->parent != NULL) {
                                item = currmenu->parent->selected;
                                currmenu = currmenu->parent;
@@ -1274,13 +1347,6 @@ cleanmenu(struct Menu *menu)
                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->icon != NULL) {
-                               imlib_context_set_image(tmp->icon);
-                               imlib_free_image();
-                       }
-               }
                item = item->next;
                free(tmp);
        }
                item = item->next;
                free(tmp);
        }
@@ -1316,6 +1382,6 @@ cleanup(void)
 static void
 usage(void)
 {
 static void
 usage(void)
 {
-       (void)fprintf(stderr, "usage: xmenu [-fiw] [-p position] [title]\n");
+       (void)fprintf(stderr, "usage: xmenu [-iw] [-p position] [title]\n");
        exit(1);
 }
        exit(1);
 }