Now lines without labels in the input generate a menu separator.
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. 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.
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*/
struct Item *next;
struct Menu *submenu;
};
struct Item *next;
struct Menu *submenu;
};
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->h = item->label ? geom.itemh : geom.separator;
item->next = NULL;
item->submenu = NULL;
item->next = NULL;
item->submenu = NULL;
/* 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;
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) {
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;
/* 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},
XFillPolygon(dpy, menu->win, dc.gc, triangle, LEN(triangle),
Convex, CoordModeOrigin);
}
XFillPolygon(dpy, menu->win, dc.gc, triangle, LEN(triangle),
Convex, CoordModeOrigin);
}
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 {
--- /dev/null
+#!/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
+