Switch completion movement keys
This commit is contained in:
parent
00819865a9
commit
24281f7120
|
@ -0,0 +1,53 @@
|
|||
diff --git a/dmenu.1 b/dmenu.1
|
||||
index 323f93c..3e3b31b 100644
|
||||
--- a/dmenu.1
|
||||
+++ b/dmenu.1
|
||||
@@ -3,7 +3,7 @@
|
||||
dmenu \- dynamic menu
|
||||
.SH SYNOPSIS
|
||||
.B dmenu
|
||||
-.RB [ \-bfiv ]
|
||||
+.RB [ \-bfsv ]
|
||||
.RB [ \-l
|
||||
.IR lines ]
|
||||
.RB [ \-m
|
||||
@@ -44,8 +44,8 @@ dmenu appears at the bottom of the screen.
|
||||
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
|
||||
is faster, but will lock up X until stdin reaches end\-of\-file.
|
||||
.TP
|
||||
-.B \-i
|
||||
-dmenu matches menu items case insensitively.
|
||||
+.B \-s
|
||||
+dmenu matches menu items case sensitively.
|
||||
.TP
|
||||
.BI \-l " lines"
|
||||
dmenu lists items vertically, with the given number of lines.
|
||||
diff --git a/dmenu.c b/dmenu.c
|
||||
index 65f25ce..855df59 100644
|
||||
--- a/dmenu.c
|
||||
+++ b/dmenu.c
|
||||
@@ -55,8 +55,9 @@ static Clr *scheme[SchemeLast];
|
||||
|
||||
#include "config.h"
|
||||
|
||||
-static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
|
||||
-static char *(*fstrstr)(const char *, const char *) = strstr;
|
||||
+static char * cistrstr(const char *s, const char *sub);
|
||||
+static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
|
||||
+static char *(*fstrstr)(const char *, const char *) = cistrstr;
|
||||
|
||||
static void
|
||||
appenditem(struct item *item, struct item **list, struct item **last)
|
||||
@@ -709,9 +710,9 @@ main(int argc, char *argv[])
|
||||
topbar = 0;
|
||||
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
|
||||
fast = 1;
|
||||
- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
|
||||
- fstrncmp = strncasecmp;
|
||||
- fstrstr = cistrstr;
|
||||
+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
|
||||
+ fstrncmp = strncmp;
|
||||
+ fstrstr = strstr;
|
||||
} else if (i + 1 == argc)
|
||||
usage();
|
||||
/* these options take one argument */
|
6
dmenu.1
6
dmenu.1
|
@ -3,7 +3,7 @@
|
|||
dmenu \- dynamic menu
|
||||
.SH SYNOPSIS
|
||||
.B dmenu
|
||||
.RB [ \-bfiv ]
|
||||
.RB [ \-bfsv ]
|
||||
.RB [ \-l
|
||||
.IR lines ]
|
||||
.RB [ \-m
|
||||
|
@ -44,8 +44,8 @@ dmenu appears at the bottom of the screen.
|
|||
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
|
||||
is faster, but will lock up X until stdin reaches end\-of\-file.
|
||||
.TP
|
||||
.B \-i
|
||||
dmenu matches menu items case insensitively.
|
||||
.B \-s
|
||||
dmenu matches menu items case sensitively.
|
||||
.TP
|
||||
.BI \-l " lines"
|
||||
dmenu lists items vertically, with the given number of lines.
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
.TH DMENU 1 dmenu\-VERSION
|
||||
.SH NAME
|
||||
dmenu \- dynamic menu
|
||||
.SH SYNOPSIS
|
||||
.B dmenu
|
||||
.RB [ \-bfiv ]
|
||||
.RB [ \-l
|
||||
.IR lines ]
|
||||
.RB [ \-m
|
||||
.IR monitor ]
|
||||
.RB [ \-p
|
||||
.IR prompt ]
|
||||
.RB [ \-fn
|
||||
.IR font ]
|
||||
.RB [ \-nb
|
||||
.IR color ]
|
||||
.RB [ \-nf
|
||||
.IR color ]
|
||||
.RB [ \-sb
|
||||
.IR color ]
|
||||
.RB [ \-sf
|
||||
.IR color ]
|
||||
.RB [ \-w
|
||||
.IR windowid ]
|
||||
.P
|
||||
.BR dmenu_run " ..."
|
||||
.SH DESCRIPTION
|
||||
.B dmenu
|
||||
is a dynamic menu for X, which reads a list of newline\-separated items from
|
||||
stdin. When the user selects an item and presses Return, their choice is printed
|
||||
to stdout and dmenu terminates. Entering text will narrow the items to those
|
||||
matching the tokens in the input.
|
||||
.P
|
||||
.B dmenu_run
|
||||
is a script used by
|
||||
.IR dwm (1)
|
||||
which lists programs in the user's $PATH and runs the result in their $SHELL.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B \-b
|
||||
dmenu appears at the bottom of the screen.
|
||||
.TP
|
||||
.B \-f
|
||||
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
|
||||
is faster, but will lock up X until stdin reaches end\-of\-file.
|
||||
.TP
|
||||
.B \-i
|
||||
dmenu matches menu items case insensitively.
|
||||
.TP
|
||||
.BI \-l " lines"
|
||||
dmenu lists items vertically, with the given number of lines.
|
||||
.TP
|
||||
.BI \-m " monitor"
|
||||
dmenu is displayed on the monitor number supplied. Monitor numbers are starting
|
||||
from 0.
|
||||
.TP
|
||||
.BI \-p " prompt"
|
||||
defines the prompt to be displayed to the left of the input field.
|
||||
.TP
|
||||
.BI \-fn " font"
|
||||
defines the font or font set used.
|
||||
.TP
|
||||
.BI \-nb " color"
|
||||
defines the normal background color.
|
||||
.IR #RGB ,
|
||||
.IR #RRGGBB ,
|
||||
and X color names are supported.
|
||||
.TP
|
||||
.BI \-nf " color"
|
||||
defines the normal foreground color.
|
||||
.TP
|
||||
.BI \-sb " color"
|
||||
defines the selected background color.
|
||||
.TP
|
||||
.BI \-sf " color"
|
||||
defines the selected foreground color.
|
||||
.TP
|
||||
.B \-v
|
||||
prints version information to stdout, then exits.
|
||||
.TP
|
||||
.BI \-w " windowid"
|
||||
embed into windowid.
|
||||
.SH USAGE
|
||||
dmenu is completely controlled by the keyboard. Items are selected using the
|
||||
arrow keys, page up, page down, home, and end.
|
||||
.TP
|
||||
.B Tab
|
||||
Copy the selected item to the input field.
|
||||
.TP
|
||||
.B Return
|
||||
Confirm selection. Prints the selected item to stdout and exits, returning
|
||||
success.
|
||||
.TP
|
||||
.B Ctrl-Return
|
||||
Confirm selection. Prints the selected item to stdout and continues.
|
||||
.TP
|
||||
.B Shift\-Return
|
||||
Confirm input. Prints the input text to stdout and exits, returning success.
|
||||
.TP
|
||||
.B Escape
|
||||
Exit without selecting an item, returning failure.
|
||||
.TP
|
||||
.B Ctrl-Left
|
||||
Move cursor to the start of the current word
|
||||
.TP
|
||||
.B Ctrl-Right
|
||||
Move cursor to the end of the current word
|
||||
.TP
|
||||
.B C\-a
|
||||
Home
|
||||
.TP
|
||||
.B C\-b
|
||||
Left
|
||||
.TP
|
||||
.B C\-c
|
||||
Escape
|
||||
.TP
|
||||
.B C\-d
|
||||
Delete
|
||||
.TP
|
||||
.B C\-e
|
||||
End
|
||||
.TP
|
||||
.B C\-f
|
||||
Right
|
||||
.TP
|
||||
.B C\-g
|
||||
Escape
|
||||
.TP
|
||||
.B C\-h
|
||||
Backspace
|
||||
.TP
|
||||
.B C\-i
|
||||
Tab
|
||||
.TP
|
||||
.B C\-j
|
||||
Return
|
||||
.TP
|
||||
.B C\-J
|
||||
Shift-Return
|
||||
.TP
|
||||
.B C\-k
|
||||
Delete line right
|
||||
.TP
|
||||
.B C\-m
|
||||
Return
|
||||
.TP
|
||||
.B C\-M
|
||||
Shift-Return
|
||||
.TP
|
||||
.B C\-n
|
||||
Down
|
||||
.TP
|
||||
.B C\-p
|
||||
Up
|
||||
.TP
|
||||
.B C\-u
|
||||
Delete line left
|
||||
.TP
|
||||
.B C\-w
|
||||
Delete word left
|
||||
.TP
|
||||
.B C\-y
|
||||
Paste from primary X selection
|
||||
.TP
|
||||
.B C\-Y
|
||||
Paste from X clipboard
|
||||
.TP
|
||||
.B M\-b
|
||||
Move cursor to the start of the current word
|
||||
.TP
|
||||
.B M\-f
|
||||
Move cursor to the end of the current word
|
||||
.TP
|
||||
.B M\-g
|
||||
Home
|
||||
.TP
|
||||
.B M\-G
|
||||
End
|
||||
.TP
|
||||
.B M\-h
|
||||
Up
|
||||
.TP
|
||||
.B M\-j
|
||||
Page down
|
||||
.TP
|
||||
.B M\-k
|
||||
Page up
|
||||
.TP
|
||||
.B M\-l
|
||||
Down
|
||||
.SH SEE ALSO
|
||||
.IR dwm (1),
|
||||
.IR stest (1)
|
15
dmenu.c
15
dmenu.c
|
@ -60,8 +60,9 @@ static Clr *scheme[SchemeLast];
|
|||
|
||||
#include "config.h"
|
||||
|
||||
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
|
||||
static char *(*fstrstr)(const char *, const char *) = strstr;
|
||||
static char * cistrstr(const char *s, const char *sub);
|
||||
static int (*fstrncmp)(const char *, const char *, size_t) = strncasecmp;
|
||||
static char *(*fstrstr)(const char *, const char *) = cistrstr;
|
||||
|
||||
static unsigned int
|
||||
textw_clamp(const char *str, unsigned int n)
|
||||
|
@ -413,13 +414,13 @@ vi_keypress(KeySym ksym, const XKeyEvent *ev)
|
|||
if (cursor)
|
||||
cursor = nextrune(-1);
|
||||
break;
|
||||
case XK_t:
|
||||
case XK_h:
|
||||
if (sel && sel->right && (sel = sel->right) == next) {
|
||||
curr = next;
|
||||
calcoffsets();
|
||||
}
|
||||
break;
|
||||
case XK_h:
|
||||
case XK_t:
|
||||
if (sel && sel->left && (sel = sel->left)->right == curr) {
|
||||
curr = prev;
|
||||
calcoffsets();
|
||||
|
@ -937,9 +938,9 @@ main(int argc, char *argv[])
|
|||
topbar = 0;
|
||||
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
|
||||
fast = 1;
|
||||
else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
|
||||
fstrncmp = strncasecmp;
|
||||
fstrstr = cistrstr;
|
||||
else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
|
||||
fstrncmp = strncmp;
|
||||
fstrstr = strstr;
|
||||
} else if (!strcmp(argv[i], "-vi")) {
|
||||
vi_mode = 1;
|
||||
using_vi_mode = start_mode;
|
||||
|
|
212
dmenu.c.orig
212
dmenu.c.orig
|
@ -25,7 +25,7 @@
|
|||
#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
|
||||
|
||||
/* enums */
|
||||
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */
|
||||
enum { SchemeNorm, SchemeSel, SchemeOut, SchemeCursor, SchemeLast }; /* color schemes */
|
||||
|
||||
struct item {
|
||||
char *text;
|
||||
|
@ -33,6 +33,11 @@ struct item {
|
|||
int out;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
KeySym ksym;
|
||||
unsigned int state;
|
||||
} Key;
|
||||
|
||||
static char text[BUFSIZ] = "";
|
||||
static char *embed;
|
||||
static int bh, mw, mh;
|
||||
|
@ -43,6 +48,7 @@ static struct item *items = NULL;
|
|||
static struct item *matches, *matchend;
|
||||
static struct item *prev, *curr, *next, *sel;
|
||||
static int mon = -1, screen;
|
||||
static unsigned int using_vi_mode = 0;
|
||||
|
||||
static Atom clip, utf8;
|
||||
static Display *dpy;
|
||||
|
@ -162,8 +168,16 @@ drawmenu(void)
|
|||
drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
|
||||
|
||||
curpos = TEXTW(text) - TEXTW(&text[cursor]);
|
||||
if ((curpos += lrpad / 2 - 1) < w) {
|
||||
curpos += lrpad / 2 - 1;
|
||||
if (using_vi_mode && text[0] != '\0') {
|
||||
drw_setscheme(drw, scheme[SchemeCursor]);
|
||||
char vi_char[] = {text[cursor], '\0'};
|
||||
drw_text(drw, x + curpos, 0, TEXTW(vi_char) - lrpad, bh, 0, vi_char, 0);
|
||||
} else if (using_vi_mode) {
|
||||
drw_setscheme(drw, scheme[SchemeNorm]);
|
||||
drw_rect(drw, x + curpos, 2, lrpad / 2, bh - 4, 1, 0);
|
||||
} else if (curpos < w) {
|
||||
drw_setscheme(drw, scheme[SchemeSel]);
|
||||
drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
|
||||
}
|
||||
|
||||
|
@ -320,6 +334,181 @@ movewordedge(int dir)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
vi_keypress(KeySym ksym, const XKeyEvent *ev)
|
||||
{
|
||||
static const size_t quit_len = LENGTH(quit_keys);
|
||||
if (ev->state & ControlMask) {
|
||||
switch(ksym) {
|
||||
/* movement */
|
||||
case XK_d: /* fallthrough */
|
||||
if (next) {
|
||||
sel = curr = next;
|
||||
calcoffsets();
|
||||
goto draw;
|
||||
} else
|
||||
ksym = XK_G;
|
||||
break;
|
||||
case XK_u:
|
||||
if (prev) {
|
||||
sel = curr = prev;
|
||||
calcoffsets();
|
||||
goto draw;
|
||||
} else
|
||||
ksym = XK_g;
|
||||
break;
|
||||
case XK_p: /* fallthrough */
|
||||
case XK_P: break;
|
||||
case XK_c:
|
||||
cleanup();
|
||||
exit(1);
|
||||
case XK_Return: /* fallthrough */
|
||||
case XK_KP_Enter: break;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
|
||||
switch(ksym) {
|
||||
/* movement */
|
||||
case XK_0:
|
||||
cursor = 0;
|
||||
break;
|
||||
case XK_dollar:
|
||||
if (text[cursor + 1] != '\0') {
|
||||
cursor = strlen(text) - 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case XK_b:
|
||||
movewordedge(-1);
|
||||
break;
|
||||
case XK_e:
|
||||
cursor = nextrune(+1);
|
||||
movewordedge(+1);
|
||||
if (text[cursor] == '\0')
|
||||
--cursor;
|
||||
else
|
||||
cursor = nextrune(-1);
|
||||
break;
|
||||
case XK_g:
|
||||
if (sel == matches) {
|
||||
break;
|
||||
}
|
||||
sel = curr = matches;
|
||||
calcoffsets();
|
||||
break;
|
||||
case XK_G:
|
||||
if (next) {
|
||||
/* jump to end of list and position items in reverse */
|
||||
curr = matchend;
|
||||
calcoffsets();
|
||||
curr = prev;
|
||||
calcoffsets();
|
||||
while (next && (curr = curr->right))
|
||||
calcoffsets();
|
||||
}
|
||||
sel = matchend;
|
||||
break;
|
||||
case XK_d:
|
||||
if (cursor)
|
||||
cursor = nextrune(-1);
|
||||
break;
|
||||
case XK_t:
|
||||
if (sel && sel->right && (sel = sel->right) == next) {
|
||||
curr = next;
|
||||
calcoffsets();
|
||||
}
|
||||
break;
|
||||
case XK_h:
|
||||
if (sel && sel->left && (sel = sel->left)->right == curr) {
|
||||
curr = prev;
|
||||
calcoffsets();
|
||||
}
|
||||
break;
|
||||
case XK_n:
|
||||
if (text[cursor] != '\0' && text[cursor + 1] != '\0')
|
||||
cursor = nextrune(+1);
|
||||
else if (text[cursor] == '\0' && cursor)
|
||||
--cursor;
|
||||
break;
|
||||
case XK_w:
|
||||
movewordedge(+1);
|
||||
if (text[cursor] != '\0' && text[cursor + 1] != '\0')
|
||||
cursor = nextrune(+1);
|
||||
else if (cursor)
|
||||
--cursor;
|
||||
break;
|
||||
/* insertion */
|
||||
case XK_a:
|
||||
cursor = nextrune(+1);
|
||||
/* fallthrough */
|
||||
case XK_i:
|
||||
using_vi_mode = 0;
|
||||
break;
|
||||
case XK_A:
|
||||
if (text[cursor] != '\0')
|
||||
cursor = strlen(text);
|
||||
using_vi_mode = 0;
|
||||
break;
|
||||
case XK_I:
|
||||
cursor = using_vi_mode = 0;
|
||||
break;
|
||||
case XK_p:
|
||||
if (text[cursor] != '\0')
|
||||
cursor = nextrune(+1);
|
||||
XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
|
||||
utf8, utf8, win, CurrentTime);
|
||||
return;
|
||||
case XK_P:
|
||||
XConvertSelection(dpy, (ev->state & ControlMask) ? clip : XA_PRIMARY,
|
||||
utf8, utf8, win, CurrentTime);
|
||||
return;
|
||||
/* deletion */
|
||||
case XK_K:
|
||||
text[cursor] = '\0';
|
||||
if (cursor)
|
||||
cursor = nextrune(-1);
|
||||
match();
|
||||
break;
|
||||
case XK_x:
|
||||
cursor = nextrune(+1);
|
||||
insert(NULL, nextrune(-1) - cursor);
|
||||
if (text[cursor] == '\0' && text[0] != '\0')
|
||||
--cursor;
|
||||
match();
|
||||
break;
|
||||
/* misc. */
|
||||
case XK_Return:
|
||||
case XK_KP_Enter:
|
||||
puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
|
||||
if (!(ev->state & ControlMask)) {
|
||||
cleanup();
|
||||
exit(0);
|
||||
}
|
||||
if (sel)
|
||||
sel->out = 1;
|
||||
break;
|
||||
case XK_Tab:
|
||||
if (!sel)
|
||||
return;
|
||||
strncpy(text, sel->text, sizeof text - 1);
|
||||
text[sizeof text - 1] = '\0';
|
||||
cursor = strlen(text) - 1;
|
||||
match();
|
||||
break;
|
||||
default:
|
||||
for (size_t i = 0; i < quit_len; ++i)
|
||||
if (quit_keys[i].ksym == ksym &&
|
||||
(quit_keys[i].state & ev->state) == quit_keys[i].state) {
|
||||
cleanup();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
draw:
|
||||
drawmenu();
|
||||
}
|
||||
|
||||
static void
|
||||
keypress(XKeyEvent *ev)
|
||||
{
|
||||
|
@ -339,6 +528,18 @@ keypress(XKeyEvent *ev)
|
|||
break;
|
||||
}
|
||||
|
||||
if (using_vi_mode) {
|
||||
vi_keypress(ksym, ev);
|
||||
return;
|
||||
} else if (vi_mode &&
|
||||
(ksym == global_esc.ksym &&
|
||||
(ev->state & global_esc.state) == global_esc.state)) {
|
||||
using_vi_mode = 1;
|
||||
if (cursor)
|
||||
cursor = nextrune(-1);
|
||||
goto draw;
|
||||
}
|
||||
|
||||
if (ev->state & ControlMask) {
|
||||
switch(ksym) {
|
||||
case XK_a: ksym = XK_Home; break;
|
||||
|
@ -542,6 +743,8 @@ paste(void)
|
|||
insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
|
||||
XFree(p);
|
||||
}
|
||||
if (using_vi_mode && text[cursor] == '\0')
|
||||
--cursor;
|
||||
drawmenu();
|
||||
}
|
||||
|
||||
|
@ -737,6 +940,11 @@ main(int argc, char *argv[])
|
|||
else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
|
||||
fstrncmp = strncasecmp;
|
||||
fstrstr = cistrstr;
|
||||
} else if (!strcmp(argv[i], "-vi")) {
|
||||
vi_mode = 1;
|
||||
using_vi_mode = start_mode;
|
||||
global_esc.ksym = XK_Escape;
|
||||
global_esc.state = 0;
|
||||
} else if (i + 1 == argc)
|
||||
usage();
|
||||
/* these options take one argument */
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
@@ -714,9 +715,9 @@
|
||||
topbar = 0;
|
||||
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
|
||||
fast = 1;
|
||||
- else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */
|
||||
- fstrncmp = strncasecmp;
|
||||
- fstrstr = cistrstr;
|
||||
+ else if (!strcmp(argv[i], "-s")) { /* case-sensitive item matching */
|
||||
+ fstrncmp = strncmp;
|
||||
+ fstrstr = strstr;
|
||||
} else if (i + 1 == argc)
|
||||
usage();
|
||||
/* these options take one argument */
|
Loading…
Reference in New Issue