Hi, On Thu, Jul 29, 2010 at 12:26:54PM +0200, Catalin David wrote: > Hello! > > On Sun, Jul 18, 2010 at 9:49 PM, Sean Neilan <[email protected]> wrote: > > I'm sorry, I found what I was looking for a couple minutes after I sent this > > email. > > http://lists.suckless.org/dwm/0901/7355.html > > This patch will let you use tab completion to select a file. Wow. > > Unfortunately, i was not able to patch the latest version of dmenu > with it... Can you please tell me what version of dmenu you used? > > Thanks, > > Catalin >
The attached patch is what I did to get it to work on a reasonably recent version of dmenu. I suggest you apply the changes in this manually to get it to work. It's pretty neat :-) S -- Sean Whitton / <[email protected]> OpenPGP KeyID: 0x3B6D411B http://seanwhitton.com/
diff --git a/dmenu/dmenu.1 b/dmenu/dmenu.1
index 2279835..4e669a2 100644
--- a/dmenu/dmenu.1
+++ b/dmenu/dmenu.1
@@ -14,6 +14,7 @@ dmenu \- dynamic menu
.RB [ \-sf " <color>"]
.RB [ \-xs ]
.RB [ \-v ]
+.RB [ \-c ]
.SH DESCRIPTION
.SS Overview
dmenu is a generic menu for X, originally designed for
@@ -55,6 +56,9 @@ xmms-like pattern matching.
.TP
.B \-v
prints version information to standard output, then exits.
+.TP
+.B \-c
+enables filename completion for text after a space (useful with the dmenu_run
script).
.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
@@ -76,7 +80,9 @@ Select the first item of the previous/next 'page' of items.
Select the first/last item.
.TP
.B Tab (Control\-i)
-Copy the selected item to the input field.
+Copy the selected item to the input field. Also, if the -c option is given
and there
+is a space in the input, will try to expand and complete text after the space
into a
+valid filename. (First Tab - Longest Completion, Multiple Tabs - cycle through
files)
.TP
.B Return (Control\-j)
Confirm selection and quit (print the selected item to standard output).
Returns
diff --git a/dmenu/dmenu.c b/dmenu/dmenu.c
index b30b92a..8d1f33c 100644
--- a/dmenu/dmenu.c
+++ b/dmenu/dmenu.c
@@ -7,6 +7,7 @@
#include <string.h>
#include <strings.h>
#include <unistd.h>
+#include <wordexp.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -61,6 +62,7 @@ static Bool grabkeyboard(void);
static void initfont(const char *fontstr);
static void kpress(XKeyEvent * e);
static void match(char *pattern);
+static void matchfile(char *filestart, Bool cycling);
static void readstdin(void);
static void run(void);
static void setup(Bool topbar);
@@ -81,7 +83,7 @@ static int cursor = 0;
static int screen;
static unsigned int mw, mh;
static unsigned int numlockmask = 0;
-static Bool running = True;
+static Bool running = True, filecomplete = False;
static Bool xmms = False;
static Display *dpy;
static DC dc;
@@ -384,6 +386,7 @@ initfont(const char *fontstr) {
void
kpress(XKeyEvent * e) {
+ static KeySym lastkey=0;
char buf[32];
int i, num;
unsigned int len;
@@ -498,7 +501,11 @@ kpress(XKeyEvent * e) {
if(num && !iscntrl((int) buf[0])) {
buf[num] = 0;
memmove(text + cursor + num, text + cursor, sizeof text
- cursor);
- strncpy(text + cursor, buf, sizeof text - cursor);
+ //strncpy(text + cursor, buf, sizeof text - cursor);
+ if (len > 0)
+ strncat(text, buf, sizeof text);
+ else
+ strncpy(text, buf, sizeof text);
cursor+=num;
match(text);
}
@@ -581,6 +588,11 @@ kpress(XKeyEvent * e) {
return;
break;
case XK_Tab:
+ if( filecomplete && strchr(text, ' ')!=NULL ) {
+ matchfile( strchr(text, ' ')+1, lastkey==XK_Tab );
+ cursor = strlen(text);
+ break;
+ }
if(!sel)
return;
strncpy(text, sel->text, sizeof text);
@@ -591,6 +603,7 @@ kpress(XKeyEvent * e) {
len = strlen(text);
cursor = MIN(cursor, len);
cursor = MAX(cursor, 0);
+ lastkey=ksym;
drawmenu();
}
@@ -669,6 +682,44 @@ match(char *pattern) {
}
void
+matchfile(char *filestart, Bool cycling ) {
+ static int try=0, p=0;
+ wordexp_t exp;
+ int i, j, k;
+
+ if( !cycling ) {
+ p = strlen(filestart);
+ try=0;
+ }
+ filestart[ p+1 ] = 0;
+ filestart[ p ] = '*';
+
+ wordexp(filestart, &exp, 0);
+ if( exp.we_wordc > 0 ) {
+ for(j=0,i=0; exp.we_wordv[try][i]!=0; i++,j++) {
+ if( exp.we_wordv[try][i]==' ' ) filestart[j++]='\\';
+ filestart[j]=exp.we_wordv[try][i];
+ }
+ filestart[j]=0;
+
+ if( cycling )
+ try = (try+1)%exp.we_wordc;
+ else
+ for(k=1; k<exp.we_wordc; k++)
+ for(j=0, i=0; exp.we_wordv[k][i];
i++,j++) {
+ if( filestart[j]=='\\' ) j++;
+ if(
filestart[j]!=exp.we_wordv[k][i] ) {
+ filestart[j]=0;
+ break;
+ }
+ }
+ } else {
+ filestart[ p ] = 0;
+ }
+ wordfree(&exp);
+}
+
+void
readstdin(void) {
char *p, buf[1024];
unsigned int len = 0, max = 0;
@@ -830,6 +881,8 @@ main(int argc, char *argv[]) {
}
else if(!strcmp(argv[i], "-b"))
topbar = False;
+ else if(!strcmp(argv[i], "-c"))
+ filecomplete = True;
else if(!strcmp(argv[i], "-e")) {
if(++i < argc) root = atoi(argv[i]);
}
@@ -863,7 +916,7 @@ main(int argc, char *argv[]) {
eprint("dmenu-"VERSION", © 2006-2009 dmenu engineers,
see LICENSE for details\n");
else
eprint("usage: dmenu [-i] [-b] [-l <lines>] [-fn
<font>] [-nb <color>] [-nf <color>]\n"
- " [-p <prompt>] [-sb <color>] [-sf
<color>] [-xs] [-v]\n");
+ " [-p <prompt>] [-sb <color>] [-sf
<color>] [-xs] [-v] [-c]\n");
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fprintf(stderr, "warning: no locale support\n");
if(!(dpy = XOpenDisplay(NULL)))
diff --git a/dmenu/dmenu_run b/dmenu/dmenu_run
index 3e1e6e4..a4c09e5 100755
--- a/dmenu/dmenu_run
+++ b/dmenu/dmenu_run
@@ -1,2 +1,2 @@
#!/bin/sh
-exe=`dmenu_path | dmenu ${1+"$@"}` && exec $exe
+exe=`dmenu_path | dmenu ${1+"$@"}` && exe2=${exe//\\ / } && exec ${exe2%% *}
"${exe2#* }"
diff --git a/dmenu/filecompletion2.diff b/dmenu/filecompletion2.diff
new file mode 100644
index 0000000..080bc80
--- /dev/null
+++ b/dmenu/filecompletion2.diff
@@ -0,0 +1,170 @@
+diff -r 13402291bc76 dmenu.1
+--- a/dmenu.1 Fri Dec 12 19:58:52 2008 +0000
++++ b/dmenu.1 Wed Jan 28 18:15:59 2009 -0500
+@@ -12,6 +12,7 @@
+ .RB [ \-sb " <color>"]
+ .RB [ \-sf " <color>"]
+ .RB [ \-v ]
++.RB [ \-c ]
+ .SH DESCRIPTION
+ .SS Overview
+ dmenu is a generic menu for X, originally designed for
+@@ -46,6 +47,9 @@
+ .TP
+ .B \-v
+ prints version information to standard output, then exits.
++.TP
++.B \-c
++enables filename completion for text after a space (useful with the dmenu_run
script).
+ .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
+@@ -67,7 +71,9 @@
+ Select the first/last item.
+ .TP
+ .B Tab (Control\-i)
+-Copy the selected item to the input field.
++Copy the selected item to the input field. Also, if the -c option is given
and there
++is a space in the input, will try to expand and complete text after the space
into a
++valid filename. (First Tab - Longest Completion, Multiple Tabs - cycle
through files)
+ .TP
+ .B Return (Control\-j)
+ Confirm selection and quit (print the selected item to standard output).
Returns
+diff -r 13402291bc76 dmenu.c
+--- a/dmenu.c Fri Dec 12 19:58:52 2008 +0000
++++ b/dmenu.c Wed Jan 28 18:15:59 2009 -0500
+@@ -8,6 +8,7 @@
+ #include <string.h>
+ #include <strings.h>
+ #include <unistd.h>
++#include <wordexp.h>
+ #include <X11/keysym.h>
+ #include <X11/Xlib.h>
+ #include <X11/Xutil.h>
+@@ -59,6 +60,7 @@
+ static void initfont(const char *fontstr);
+ static void kpress(XKeyEvent * e);
+ static void match(char *pattern);
++static void matchfile(char *filestart, Bool cycling);
+ static void readstdin(void);
+ static void run(void);
+ static void setup(Bool topbar);
+@@ -77,7 +79,7 @@
+ static int screen;
+ static unsigned int mw, mh;
+ static unsigned int numlockmask = 0;
+-static Bool running = True;
++static Bool running = True, filecomplete = False;
+ static Display *dpy;
+ static DC dc;
+ static Item *allitems = NULL; /* first of all items */
+@@ -311,6 +313,7 @@
+
+ void
+ kpress(XKeyEvent * e) {
++ static KeySym lastkey=0;
+ char buf[32];
+ int i, num;
+ unsigned int len;
+@@ -396,7 +399,10 @@
+ default:
+ if(num && !iscntrl((int) buf[0])) {
+ buf[num] = 0;
+- strncpy(text + len, buf, sizeof text - len);
++ if(len > 0)
++ strncat(text, buf, sizeof text);
++ else
++ strncpy(text, buf, sizeof text);
+ match(text);
+ }
+ break;
+@@ -467,12 +473,17 @@
+ }
+ break;
+ case XK_Tab:
++ if( filecomplete && strchr(text, ' ')!=NULL ) {
++ matchfile( strchr(text, ' ')+1, lastkey==XK_Tab );
++ break;
++ }
+ if(!sel)
+ return;
+ strncpy(text, sel->text, sizeof text);
+ match(text);
+ break;
+ }
++ lastkey=ksym;
+ drawmenu();
+ }
+
+@@ -518,6 +529,44 @@
+ }
+
+ void
++matchfile(char *filestart, Bool cycling ) {
++ static int try=0, p=0;
++ wordexp_t exp;
++ int i, j, k;
++
++ if( !cycling ) {
++ p = strlen(filestart);
++ try=0;
++ }
++ filestart[ p+1 ] = 0;
++ filestart[ p ] = '*';
++
++ wordexp(filestart, &exp, 0);
++ if( exp.we_wordc > 0 ) {
++ for(j=0,i=0; exp.we_wordv[try][i]!=0; i++,j++) {
++ if( exp.we_wordv[try][i]==' ' ) filestart[j++]='\\';
++ filestart[j]=exp.we_wordv[try][i];
++ }
++ filestart[j]=0;
++
++ if( cycling )
++ try = (try+1)%exp.we_wordc;
++ else
++ for(k=1; k<exp.we_wordc; k++)
++ for(j=0, i=0; exp.we_wordv[k][i]; i++,j++) {
++ if( filestart[j]=='\\' ) j++;
++ if( filestart[j]!=exp.we_wordv[k][i] ) {
++ filestart[j]=0;
++ break;
++ }
++ }
++ } else {
++ filestart[ p ] = 0;
++ }
++ wordfree(&exp);
++}
++
++void
+ readstdin(void) {
+ char *p, buf[1024];
+ unsigned int len = 0, max = 0;
+@@ -677,6 +726,8 @@
+ }
+ else if(!strcmp(argv[i], "-b"))
+ topbar = False;
++ else if(!strcmp(argv[i], "-c"))
++ filecomplete = True;
+ else if(!strcmp(argv[i], "-fn")) {
+ if(++i < argc) font = argv[i];
+ }
+@@ -699,7 +750,7 @@
+ eprint("dmenu-"VERSION", © 2006-2008 dmenu engineers,
see LICENSE for details\n");
+ else
+ eprint("usage: dmenu [-i] [-b] [-fn <font>] [-nb
<color>] [-nf <color>]\n"
+- " [-p <prompt>] [-sb <color>] [-sf
<color>] [-v]\n");
++ " [-p <prompt>] [-sb <color>] [-sf
<color>] [-v] [-c]\n");
+ if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
+ fprintf(stderr, "warning: no locale support\n");
+ if(!(dpy = XOpenDisplay(0)))
+diff -r 13402291bc76 dmenu_run
+--- a/dmenu_run Fri Dec 12 19:58:52 2008 +0000
++++ b/dmenu_run Wed Jan 28 18:15:59 2009 -0500
+@@ -1,2 +1,2 @@
+-#!/bin/sh
+-exe=`dmenu_path | dmenu ${1+"$@"}` && exec $exe
++#!/bin/zsh
++exe=`dmenu_path | dmenu ${1+"$@"}` && exe2=${exe//\\ / } && exec ${exe2%% *}
"${exe2#* }"
+
pgpSGcdCQs5fn.pgp
Description: PGP signature
