Added separators
authorphillbush <phillbush@cock.li>
Sat, 16 May 2020 01:51:26 +0000 (22:51 -0300)
committerphillbush <phillbush@cock.li>
Sat, 16 May 2020 01:51:26 +0000 (22:51 -0300)
Now lines without labels in the input generate a menu separator.

README
xmenu.c
xmenu.sh [new file with mode: 0755]

diff --git a/README b/README
index c289e23..d4dea30 100644 (file)
--- a/README
+++ b/README
@@ -22,5 +22,7 @@ build and install dwm (if necessary as root).
 xmenu receives as input a menu specification where each line is a menu
 entry.  Each line can be indented with tabs to represent nested menus.
 Each line is made out of a label and a command separated by any number
 xmenu receives as input a menu specification where each line is a menu
 entry.  Each line can be indented with tabs to represent nested menus.
 Each line is made out of a label and a command separated by any number
-of tabs.
+of tabs.  Lines without labels are menu separators.
 
 
+See the script ./xmenu.sh for an example of how xmenu can be used to
+draw a simple menu with submenus and separators.
diff --git a/xmenu.c b/xmenu.c
index 71c1149..e3588fd 100644 (file)
--- a/xmenu.c
+++ b/xmenu.c
@@ -43,7 +43,8 @@ struct ScreenGeometry {
 struct Item {
        char *label;
        char *output;
 struct Item {
        char *label;
        char *output;
-       int y;      /* only y is necessary, item's x is always 0 relative to the menu*/
+       int y;
+       int h;
        struct Item *next;
        struct Menu *submenu;
 };
        struct Item *next;
        struct Menu *submenu;
 };
@@ -199,11 +200,17 @@ allocitem(const char *label, const char *output)
 
        if ((item = malloc(sizeof *item)) == NULL)
                err(1, "malloc");
 
        if ((item = malloc(sizeof *item)) == NULL)
                err(1, "malloc");
-       if ((item->label = strdup(label)) == NULL)
-               err(1, "strdup");
-       if ((item->output = strdup(output)) == NULL)
-               err(1, "strdup");
+       if (*label == '\0') {
+               item->label = NULL;
+               item->output = NULL;
+       } else {
+               if ((item->label = strdup(label)) == NULL)
+                       err(1, "strdup");
+               if ((item->output = strdup(output)) == NULL)
+                       err(1, "strdup");
+       }
        item->y = 0;
        item->y = 0;
+       item->h = item->label ? geom.itemh : geom.separator;
        item->next = NULL;
        item->submenu = NULL;
 
        item->next = NULL;
        item->submenu = NULL;
 
@@ -343,7 +350,7 @@ calcmenu(struct Menu *menu)
        /* calculate items positions and menu height */
        for (item = menu->list; item != NULL; item = item->next) {
                item->y = menu->h;
        /* calculate items positions and menu height */
        for (item = menu->list; item != NULL; item = item->next) {
                item->y = menu->h;
-               if (*item->label == '\0')   /* height for separator item */
+               if (item->label == NULL)   /* height for separator item */
                        menu->h += geom.separator;
                else
                        menu->h += geom.itemh;
                        menu->h += geom.separator;
                else
                        menu->h += geom.itemh;
@@ -403,7 +410,7 @@ getmenuitem(Window win, int y,
        for (menu = currmenu; menu != NULL; menu = menu->parent) {
                if (menu->win == win) {
                        for (item = menu->list; item != NULL; item = item->next) {
        for (menu = currmenu; menu != NULL; menu = menu->parent) {
                if (menu->win == win) {
                        for (item = menu->list; item != NULL; item = item->next) {
-                               if (y >= item->y && y <= item->y + geom.itemh) {
+                               if (y >= item->y && y <= item->y + item->h) {
                                        goto done;
                                }
                        }
                                        goto done;
                                }
                        }
@@ -443,40 +450,39 @@ drawmenu(void)
        struct Item *item;
 
        for (menu = currmenu; menu != NULL; menu = menu->parent) {
        struct Item *item;
 
        for (menu = currmenu; menu != NULL; menu = menu->parent) {
-               size_t nitems;      /* number of items before current item */
-
-               nitems = 0;
                for (item = menu->list; item != NULL; item = item->next) {
                        unsigned long *color;
                        size_t labellen;
                        int labelx, labely;
                for (item = menu->list; item != NULL; item = item->next) {
                        unsigned long *color;
                        size_t labellen;
                        int labelx, labely;
-                       int y;
 
                        /* determine item color */
 
                        /* determine item color */
-                       if (item == menu->selected)
+                       if (item->label == NULL)
+                               color = dc.decoration;
+                       else if (item == menu->selected)
                                color = dc.pressed;
                        else
                                color = dc.unpressed;
 
                                color = dc.pressed;
                        else
                                color = dc.unpressed;
 
-                       /* calculate item's y position */
-                       y = nitems * geom.itemh;
-
                        /* draw item box */
                        XSetForeground(dpy, dc.gc, color[ColorBG]);
                        /* draw item box */
                        XSetForeground(dpy, dc.gc, color[ColorBG]);
-                       XFillRectangle(dpy, menu->win, dc.gc, 0, y,
-                                      geom.itemw, geom.itemh);
+                       XFillRectangle(dpy, menu->win, dc.gc, 0, item->y,
+                                      geom.itemw, item->h);
+
+                       /* continue if item is a separator */
+                       if (item->label == NULL)
+                               continue;
 
                        /* draw item label */
                        labellen = strlen(item->label);
                        labelx = 0 + dc.fonth;
 
                        /* draw item label */
                        labellen = strlen(item->label);
                        labelx = 0 + dc.fonth;
-                       labely = y + dc.fonth + geom.itemb;
+                       labely = item->y + dc.fonth + geom.itemb;
                        XSetForeground(dpy, dc.gc, color[ColorFG]);
                        XDrawString(dpy, menu->win, dc.gc, labelx, labely, item->label, labellen);
 
                        /* draw triangle, if item contains a submenu */
                        if (item->submenu != NULL) {
                                int trianglex = geom.itemw - (geom.itemb + dc.fonth);
                        XSetForeground(dpy, dc.gc, color[ColorFG]);
                        XDrawString(dpy, menu->win, dc.gc, labelx, labely, item->label, labellen);
 
                        /* draw triangle, if item contains a submenu */
                        if (item->submenu != NULL) {
                                int trianglex = geom.itemw - (geom.itemb + dc.fonth);
-                               int triangley = y + geom.itemb;
+                               int triangley = item->y + geom.itemb;
 
                                XPoint triangle[] = {
                                        {trianglex, triangley},
 
                                XPoint triangle[] = {
                                        {trianglex, triangley},
@@ -488,8 +494,6 @@ drawmenu(void)
                                XFillPolygon(dpy, menu->win, dc.gc, triangle, LEN(triangle),
                                             Convex, CoordModeOrigin);
                        }
                                XFillPolygon(dpy, menu->win, dc.gc, triangle, LEN(triangle),
                                             Convex, CoordModeOrigin);
                        }
-
-                       nitems++;
                }
        }
 }
                }
        }
 }
@@ -527,6 +531,8 @@ run(void)
                case ButtonRelease:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
                        if (menu != NULL && item != NULL) {
                case ButtonRelease:
                        getmenuitem(ev.xbutton.window, ev.xbutton.y, &menu, &item);
                        if (menu != NULL && item != NULL) {
+                               if (item->label == NULL)
+                                       break;  /* ignore separators */
                                if (item->submenu != NULL) {
                                        setcurrmenu(item->submenu);
                                } else {
                                if (item->submenu != NULL) {
                                        setcurrmenu(item->submenu);
                                } else {
diff --git a/xmenu.sh b/xmenu.sh
new file mode 100755 (executable)
index 0000000..e3f3279
--- /dev/null
+++ b/xmenu.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+cat <<EOF | ./xmenu | xargs sh -c
+Applications
+       Web Browser     firefox
+       Image editor    gimp
+Terminal (xterm)       xterm
+Terminal (urxvt)       urxvt
+Terminal (st)          st
+
+Shutdown               poweroff
+Reboot                 reboot
+EOF
+