diff -u dmenu-3.9/config.h dmenu-3.9-fresch/config.h --- dmenu-3.9/config.h 2008-10-05 02:42:26.000000000 +0200 +++ dmenu-3.9-fresch/config.h 2008-10-05 03:23:14.000000000 +0200 @@ -6,4 +6,7 @@ static const char *normfgcolor = "#000000"; static const char *selbgcolor = "#0066ff"; static const char *selfgcolor = "#ffffff"; +static const char *lastbgcolor = "#cccccc"; +static const char *lastfgcolor = "#000000"; static unsigned int spaceitem = 30; /* px between menu items */ +static unsigned int maxtokens = 16; /* max. tokens for pattern matching */ diff -u dmenu-3.9/config.mk dmenu-3.9-fresch/config.mk --- dmenu-3.9/config.mk 2008-10-05 02:42:26.000000000 +0200 +++ dmenu-3.9-fresch/config.mk 2008-10-05 03:23:14.000000000 +0200 @@ -1,5 +1,5 @@ # dmenu version -VERSION = 3.9 +VERSION = 3.9-vertical # Customize below to fit your system diff -u dmenu-3.9/dmenu.1 dmenu-3.9-fresch/dmenu.1 --- dmenu-3.9/dmenu.1 2008-10-05 02:42:26.000000000 +0200 +++ dmenu-3.9-fresch/dmenu.1 2008-10-05 03:23:14.000000000 +0200 @@ -5,12 +5,27 @@ .B dmenu .RB [ \-i ] .RB [ \-b ] +.RB [ \-r ] +.RB [ \-x " "] +.RB [ \-y " "] +.RB [ \-w " "] .RB [ \-fn " "] .RB [ \-nb " "] .RB [ \-nf " "] .RB [ \-p " "] .RB [ \-sb " "] .RB [ \-sf " "] +.RB [ \-l " <#items>"] +.RB [ \-h " "] +.RB [ \-c ] +.RB [ \-ms ] +.RB [ \-ml ] +.RB [ \-lb " "] +.RB [ \-lf " "] +.RB [ \-rs ] +.RB [ \-ni ] +.RB [ \-nl ] +.RB [ \-xs ] .RB [ \-v ] .SH DESCRIPTION .SS Overview @@ -26,6 +41,18 @@ .B \-b defines that dmenu appears at the bottom. .TP +.B \-r +defines that dmenu appears on the right. +.TP +.B \-x +defines distance to left or right side of the screen, depends on -r. +.TP +.B \-y +defines distance to top or bottom of the screen, depends on -b. +.TP +.B \-w +defines the window width. +.TP .B \-fn defines the font. .TP @@ -44,8 +71,47 @@ .B \-sf defines the selected foreground color (#RGB, #RRGGBB, and color names are supported). .TP +.B \-nl +seperates standard output by newlines. +.TP +.B \-xs +xmms-like pattern matching. +.TP .B \-v prints version information to standard output, then exits. +.SS Vertical Mode Options +.TP +.B \-l <#items> +activates vertical list mode. +window-size will be adjusted for displaying the given number of items. +.TP +.B \-h +activates vertical list mode and defines the window height. +don't expect exact results. given height is used to calculate +#items. gets overridden by -l. +.TP +.B \-c +displays a counter showing hits on applied filter. +.TP +.B \-ms +multi-select; selecting an item and pressing return won't terminate dmenu. +.TP +.B \-ml +marks last selected item using the colors defined with -lb and -lf. +only useful when used with -ms. +.TP +.B \-lb +defines the last-selected background color (#RGB, #RRGGBB, and color names are supported). +.TP +.B \-lf +defines the last-selected foreground color (#RGB, #RRGGBB, and color names are supported). +.TP +.B \-rs +resize; adjust window-size for displaying the matching items. +-l or -h define the maximum. +.TP +.B \-ni +don't display indicators. .SH USAGE dmenu reads a list of newline-separated items from standard input and creates a menu. When the user selects an item or enters any text and presses Return, his/her diff -u dmenu-3.9/dmenu.c dmenu-3.9-fresch/dmenu.c --- dmenu-3.9/dmenu.c 2008-10-05 02:42:26.000000000 +0200 +++ dmenu-3.9-fresch/dmenu.c 2008-10-05 03:23:14.000000000 +0200 @@ -28,6 +28,7 @@ int x, y, w, h; unsigned long norm[ColLast]; unsigned long sel[ColLast]; + unsigned long last[ColLast]; Drawable drawable; GC gc; struct { @@ -48,20 +49,23 @@ /* forward declarations */ static void appenditem(Item *i, Item **list, Item **last); -static void calcoffsets(void); +static void calcoffsetsh(void); +static void calcoffsetsv(void); static char *cistrstr(const char *s, const char *sub); static void cleanup(void); -static void drawmenu(void); +static void drawmenuh(void); +static void drawmenuv(void); static void drawtext(const char *text, unsigned long col[ColLast]); static void eprint(const char *errstr, ...); static unsigned long getcolor(const char *colstr); static Bool grabkeyboard(void); static void initfont(const char *fontstr); static void kpress(XKeyEvent * e); +static void resizewindow(void); static void match(char *pattern); static void readstdin(void); static void run(void); -static void setup(Bool topbar); +static void setup(void); static int textnw(const char *text, unsigned int len); static int textw(const char *text); @@ -70,14 +74,34 @@ /* variables */ static char *maxname = NULL; static char *prompt = NULL; +static char *lastitem = NULL; +static char *nl = ""; +static char **tokens = NULL; static char text[4096]; +static char hitstxt[16]; static int cmdw = 0; static int promptw = 0; static int ret = 0; static int screen; +static int x, y; static unsigned int mw, mh; static unsigned int numlockmask = 0; +static unsigned int hits = 0; +static unsigned int lines = 0; +static unsigned int xoffset = 0; +static unsigned int yoffset = 0; +static unsigned int width = 0; +static unsigned int height = 0; static Bool running = True; +static Bool topbar = True; +static Bool vlist = False; +static Bool hitcounter = False; +static Bool alignright = False; +static Bool multiselect = False; +static Bool resize = False; +static Bool marklastitem = False; +static Bool indicators = True; +static Bool xmms = False; static Display *dpy; static DC dc; static Item *allitems = NULL; /* first of all items */ @@ -89,6 +113,8 @@ static Window root, win; static int (*fstrncmp)(const char *, const char *, size_t n) = strncmp; static char *(*fstrstr)(const char *, const char *) = strstr; +static void (*calcoffsets)(void) = calcoffsetsh; +static void (*drawmenu)(void) = drawmenuh; void appenditem(Item *i, Item **list, Item **last) { @@ -99,12 +125,13 @@ i->left = *last; i->right = NULL; *last = i; + ++hits; } void -calcoffsets(void) { - int tw; - unsigned int w; +calcoffsetsh(void) { + static int tw; + static unsigned int w; if(!curr) return; @@ -128,6 +155,26 @@ } } +void +calcoffsetsv(void) { + static unsigned int w; + + if(!curr) + return; + w = (dc.font.height + 2) * (lines + 1); + for(next = curr; next; next=next->right) { + w -= dc.font.height + 2; + if(w <= 0) + break; + } + w = (dc.font.height + 2) * (lines + 1); + for(prev = curr; prev && prev->left; prev=prev->left) { + w -= dc.font.height + 2; + if(w <= 0) + break; + } +} + char * cistrstr(const char *s, const char *sub) { int c, csub; @@ -169,11 +216,12 @@ XFreeGC(dpy, dc.gc); XDestroyWindow(dpy, win); XUngrabKeyboard(dpy, CurrentTime); + free(tokens); } void -drawmenu(void) { - Item *i; +drawmenuh(void) { + static Item *i; dc.x = 0; dc.y = 0; @@ -213,6 +261,87 @@ } void +drawmenuv(void) { + static Item *i; + + dc.x = 0; + dc.y = 0; + dc.h = mh; + drawtext(NULL, dc.norm); + /* print prompt? */ + if(promptw) { + dc.w = promptw; + drawtext(prompt, dc.sel); + } + dc.x += promptw; + dc.w = mw - promptw - (hitcounter ? textnw(hitstxt, strlen(hitstxt)) : 0); + + drawtext(text[0] ? text : NULL, dc.norm); + if(curr) { + if (hitcounter) { + dc.w = textw(hitstxt); + dc.x = mw - textw(hitstxt); + drawtext(hitstxt, dc.norm); + } + dc.x = 0; + dc.w = mw; + if (indicators) { + dc.y += dc.font.height + 2; + drawtext((curr && curr->left) ? "^" : NULL, dc.norm); + } + dc.y += dc.font.height + 2; + /* determine maximum items */ + for(i = curr; i != next; i=i->right) { + if((sel != i) && marklastitem && lastitem && !strncmp(lastitem, i->text, strlen(i->text))) + drawtext(i->text, dc.last); + else + drawtext(i->text, (sel == i) ? dc.sel : dc.norm); + dc.y += dc.font.height + 2; + } + drawtext(indicators && next ? "v" : NULL, dc.norm); + } else { + if (hitcounter) { + dc.w = textw(hitstxt); + dc.x = mw - textw(hitstxt); + dc.y = 0; + drawtext(hitstxt, dc.norm); + } + dc.x = 0; + dc.w = mw; + dc.h = mh; + dc.y += dc.font.height + 2; + drawtext(NULL, dc.norm); + } + XCopyArea(dpy, dc.drawable, win, dc.gc, 0, 0, mw, mh, 0, 0); + XFlush(dpy); +} + +void +updatemenuv(Bool updown) { + static Item *i; + + if(curr) { + dc.x = 0; + dc.y = (dc.font.height + 2) * (indicators?2:1); + dc.w = mw; + dc.h = mh; + for(i = curr; i != next; i = i->right) { + if(((i == sel->left) && !updown) || (i == sel) + ||((i == sel->right) && updown)) { + if((sel != i) && marklastitem && lastitem && !strncmp(lastitem, i->text, strlen(i->text))) + drawtext(i->text, dc.last); + else + drawtext(i->text, (sel == i) ? dc.sel : dc.norm); + XCopyArea(dpy, dc.drawable, win, dc.gc, dc.x, dc.y, + dc.w, dc.font.height + 2, dc.x, dc.y); + } + dc.y += dc.font.height + 2; + } + } + XFlush(dpy); +} + +void drawtext(const char *text, unsigned long col[ColLast]) { char buf[256]; int i, x, y, h, len, olen; @@ -223,8 +352,8 @@ if(!text) return; olen = strlen(text); - h = dc.font.ascent + dc.font.descent; - y = dc.y + (dc.h / 2) - (h / 2) + dc.font.ascent; + h = dc.font.height; + y = dc.y + ((h + 2) / 2) - (h / 2) + dc.font.ascent; x = dc.x + (h / 2); /* shorten text if necessary */ for(len = MIN(olen, sizeof buf); len && textnw(text, len) > dc.w - h; len--); @@ -430,12 +559,21 @@ calcoffsets(); break; case XK_Left: + case XK_Up: if(!(sel && sel->left)) return; sel=sel->left; if(sel->right == curr) { - curr = prev; + if (vlist) + curr = curr->left; + else + curr = prev; calcoffsets(); + } else { + if (vlist) { + updatemenuv(True); + return; + } } break; case XK_Next: @@ -452,21 +590,32 @@ break; case XK_Return: if((e->state & ShiftMask) && *text) - fprintf(stdout, "%s", text); - else if(sel) - fprintf(stdout, "%s", sel->text); + fprintf(stdout, "%s%s", text, nl); + else if(sel) { + fprintf(stdout, "%s%s", sel->text, nl); + lastitem = sel->text; + } else if(*text) - fprintf(stdout, "%s", text); + fprintf(stdout, "%s%s", text, nl); fflush(stdout); - running = False; + running = multiselect; break; case XK_Right: + case XK_Down: if(!(sel && sel->right)) return; sel=sel->right; if(sel == next) { - curr = next; + if (vlist) + curr = curr->right; + else + curr = next; calcoffsets(); + } else { + if (vlist) { + updatemenuv(False); + return; + } } break; case XK_Tab: @@ -479,22 +628,68 @@ drawmenu(); } +void resizewindow(void) +{ + if (resize) { + static int rlines, ry, rmh; + + rlines = (hits > lines ? lines : hits) + (indicators?3:1); + rmh = vlist ? (dc.font.height + 2) * rlines : mh; + ry = topbar ? y + yoffset : y - rmh + (dc.font.height + 2) - yoffset; + XMoveResizeWindow(dpy, win, x, ry, mw, rmh); + } +} + +unsigned int tokenize(char *pat, char **tok) +{ + unsigned int i = 0; + char tmp[4096] = {0}; + + strncpy(tmp, pat, strlen(pat)); + tok[0] = strtok(tmp, " "); + + while(tok[i] && i < maxtokens) + tok[++i] = strtok(NULL, " "); + return i; +} + void match(char *pattern) { - unsigned int plen; + unsigned int plen, tokencnt = 0; + char append = 0; Item *i, *itemend, *lexact, *lprefix, *lsubstr, *exactend, *prefixend, *substrend; if(!pattern) return; - plen = strlen(pattern); + + if(!xmms) + tokens[(tokencnt = 1)-1] = pattern; + else + if(!(tokencnt = tokenize(pattern, tokens))) + tokens[(tokencnt = 1)-1] = ""; + item = lexact = lprefix = lsubstr = itemend = exactend = prefixend = substrend = NULL; - for(i = allitems; i; i = i->next) - if(!fstrncmp(pattern, i->text, plen + 1)) + for(i = allitems; i; i = i->next) { + for(int j = 0; j < tokencnt; ++j) { + plen = strlen(tokens[j]); + if(!fstrncmp(tokens[j], i->text, plen + 1)) + append = !append || append > 1 ? 1 : append; + else if(!fstrncmp(tokens[j], i->text, plen )) + append = !append || append > 2 ? 2 : append; + else if(fstrstr(i->text, tokens[j])) + append = append > 0 && append < 3 ? append : 3; + else { + append = 0; + break; + } + } + if(append == 1) appenditem(i, &lexact, &exactend); - else if(!fstrncmp(pattern, i->text, plen)) + else if(append == 2) appenditem(i, &lprefix, &prefixend); - else if(fstrstr(i->text, pattern)) + else if(append == 3) appenditem(i, &lsubstr, &substrend); + } if(lexact) { item = lexact; itemend = exactend; @@ -518,6 +713,9 @@ } curr = prev = next = sel = item; calcoffsets(); + resizewindow(); + snprintf(hitstxt, sizeof(hitstxt), "(%d)", hits); + hits = 0; } void @@ -569,8 +767,8 @@ } void -setup(Bool topbar) { - int i, j, x, y; +setup(void) { + int i, j, sy, slines; #if XINERAMA int n; XineramaScreenInfo *info = NULL; @@ -593,6 +791,8 @@ dc.norm[ColFG] = getcolor(normfgcolor); dc.sel[ColBG] = getcolor(selbgcolor); dc.sel[ColFG] = getcolor(selfgcolor); + dc.last[ColBG] = getcolor(lastbgcolor); + dc.last[ColFG] = getcolor(lastfgcolor); initfont(font); /* menu window */ @@ -627,7 +827,15 @@ mw = DisplayWidth(dpy, screen); } - win = XCreateWindow(dpy, root, x, y, mw, mh, 0, + /* update menu window geometry */ + + slines = (lines ? lines : (lines = height / (dc.font.height + 2))) + (indicators?3:1); + mh = vlist ? (dc.font.height + 2) * slines : mh; + sy = topbar ? y + yoffset : y - mh + (dc.font.height + 2) - yoffset; + x = alignright ? mw - (width ? width : mw) - xoffset : xoffset; + mw = width ? width : mw; + + win = XCreateWindow(dpy, root, x, sy, mw, mh, 0, DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen), CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa); @@ -647,6 +855,7 @@ if(promptw > mw / 5) promptw = mw / 5; text[0] = 0; + tokens = malloc((xmms?maxtokens:1)*sizeof(char*)); match(text); XMapRaised(dpy, win); } @@ -670,7 +879,6 @@ int main(int argc, char *argv[]) { unsigned int i; - Bool topbar = True; /* command line args */ for(i = 1; i < argc; i++) @@ -680,6 +888,16 @@ } else if(!strcmp(argv[i], "-b")) topbar = False; + else if(!strcmp(argv[i], "-r")) + alignright = True; + else if(!strcmp(argv[i], "-l")) { + vlist = True; + calcoffsets = calcoffsetsv; + drawmenu = drawmenuv; + if(++i < argc) lines += atoi(argv[i]); + } + else if(!strcmp(argv[i], "-c")) + hitcounter = True; else if(!strcmp(argv[i], "-fn")) { if(++i < argc) font = argv[i]; } @@ -698,11 +916,47 @@ else if(!strcmp(argv[i], "-sf")) { if(++i < argc) selfgcolor = argv[i]; } + else if(!strcmp(argv[i], "-lb")) { + if(++i < argc) lastbgcolor = argv[i]; + } + else if(!strcmp(argv[i], "-lf")) { + if(++i < argc) lastfgcolor = argv[i]; + } + else if(!strcmp(argv[i], "-w")) { + if(++i < argc) width = atoi(argv[i]); + } + else if(!strcmp(argv[i], "-h")) { + vlist = True; + calcoffsets = calcoffsetsv; + drawmenu = drawmenuv; + if(++i < argc) height = atoi(argv[i]); + } + else if(!strcmp(argv[i], "-x")) { + if(++i < argc) xoffset = atoi(argv[i]); + } + else if(!strcmp(argv[i], "-y")) { + if(++i < argc) yoffset = atoi(argv[i]); + } + else if(!strcmp(argv[i], "-nl")) + nl = "\n"; + else if(!strcmp(argv[i], "-rs")) + resize = True; + else if(!strcmp(argv[i], "-ms")) + multiselect = True; + else if(!strcmp(argv[i], "-ml")) + marklastitem = True; + else if(!strcmp(argv[i], "-ni")) + indicators = False; + else if(!strcmp(argv[i], "-xs")) + xmms = True; else if(!strcmp(argv[i], "-v")) eprint("dmenu-"VERSION", © 2006-2008 dmenu engineers, see LICENSE for details\n"); else - eprint("usage: dmenu [-i] [-b] [-fn ] [-nb ] [-nf ]\n" - " [-p ] [-sb ] [-sf ] [-v]\n"); + eprint("usage: dmenu [-i] [-b] [-r] [-x [-y ] [-w ] [-nb ] [-nf ] [-p ] [-sb ]\n" + "[-sf ] [-l <#items>] [-h ] [-c] [-ms]\n" + "[-ml] [-lb ] [-lf ] [-rs] [-ni] [-nl] [-xs] [-v]\n"); + if(!setlocale(LC_CTYPE, "") || !XSupportsLocale()) fprintf(stderr, "warning: no locale support\n"); if(!(dpy = XOpenDisplay(0))) @@ -718,8 +972,8 @@ running = grabkeyboard(); readstdin(); } - - setup(topbar); + + setup(); drawmenu(); XSync(dpy, False); run();