[menu] Add proper caret support

This commit is contained in:
Kris Maglione 2008-10-14 16:57:48 -04:00
parent 5db3cf60bd
commit 318f180173
6 changed files with 210 additions and 65 deletions

View File

@ -13,6 +13,7 @@ LDFLAGS += -lm $(LIBX11) -lXext -lXrandr -lXinerama \
CFLAGS += $(INCX11) -DVERSION=\"$(VERSION)\" \ CFLAGS += $(INCX11) -DVERSION=\"$(VERSION)\" \
-DIXP_NEEDAPI=86 -DIXP_NEEDAPI=86
OBJ = main \ OBJ = main \
caret \
event \ event \
menu \ menu \
../wmii/geom \ ../wmii/geom \

140
cmd/menu/caret.c Normal file
View File

@ -0,0 +1,140 @@
#include "dat.h"
#include <ctype.h>
#include <string.h>
#include "fns.h"
static int
iswordrune(Rune r) {
if(isalpharune(r))
return 1;
return r < 0x80 && (r == '_' || isdigit(r));
}
static char*
prev_rune(char *start, char *p, Rune *r) {
*r = 0;
if(p == start)
return p;
while(p > start && (*(--p)&0xC0) == 0x80)
;
chartorune(r, p);
return p;
}
static char*
next_rune(char *p, Rune *r) {
int i;
*r = 0;
if(!*p)
return p;
i = chartorune(r, p);
return p + i;
}
char*
caret_find(int dir, int type) {
char *end;
char *next, *p;
Rune r;
int res;
p = input.pos;
if(dir == FORWARD) {
end = input.end;
switch(type) {
case LINE:
return end;
case WORD:
chartorune(&r, p);
res = iswordrune(r);
while(next=next_rune(p, &r), r && iswordrune(r) == res && !isspacerune(r))
p = next;
while(next=next_rune(p, &r), r && isspacerune(r))
p = next;
return p;
case CHAR:
if(p < end)
return p+1;
return p;
}
}
else if(dir == BACKWARD) {
end = input.string;
switch(type) {
case LINE:
return end;
case WORD:
while(next=prev_rune(end, p, &r), r && isspacerune(r))
p = next;
prev_rune(end, p, &r);
res = iswordrune(r);
while(next=prev_rune(end, p, &r), r && iswordrune(r) == res && !isspacerune(r))
p = next;
return p;
case CHAR:
if(p > end)
return p-1;
return end;
}
}
die("not reached");
return nil; /* shut up ken */
}
void
caret_move(int dir, int type) {
input.pos = caret_find(dir, type);
}
void
caret_delete(int dir, int type) {
char *pos, *p;
int n;
p = caret_find(dir, type);
pos = input.pos;
if(p == input.end)
input.end = pos;
else {
if(p < pos) {
pos = p;
p = input.pos;
}
n = input.end - p;
memmove(pos, p, n);
input.pos = pos;
input.end = pos + n;
}
*input.end = '\0';
}
void
caret_insert(char *s, bool clear) {
int pos, end, len, size;
if(clear) {
input.pos = input.string;
input.end = input.string;
}
len = strlen(s);
pos = input.pos - input.string;
end = input.end - input.string;
size = input.size;
if(input.size == 0)
input.size = 1;
while(input.size < end + len + 1)
input.size <<= 2;
if(input.size != size)
input.string = erealloc(input.string, input.size);
input.pos = input.string + pos;
input.end = input.string + end + len;
*input.end = '\0';
memmove(input.pos + len, input.pos, end - pos);
memmove(input.pos, s, len);
input.pos += len;
}

View File

@ -15,6 +15,15 @@
# define EXTERN extern # define EXTERN extern
#endif #endif
enum {
FORWARD,
BACKWARD,
LINE,
WORD,
CHAR,
CARET_LAST,
};
typedef struct Item Item; typedef struct Item Item;
struct Item { struct Item {
@ -27,6 +36,14 @@ struct Item {
int width; int width;
}; };
EXTERN struct {
char* string;
char* end;
char* pos;
int len;
int size;
} input;
EXTERN long xtime; EXTERN long xtime;
EXTERN Image* ibuf; EXTERN Image* ibuf;
EXTERN Font* font; EXTERN Font* font;
@ -46,8 +63,6 @@ EXTERN Item* matchidx;
EXTERN Item* histidx; EXTERN Item* histidx;
EXTERN char filter[1024];
EXTERN int maxwidth; EXTERN int maxwidth;
EXTERN int result; EXTERN int result;

View File

@ -1,4 +1,8 @@
void caret_delete(int, int);
char* caret_find(int, int);
void caret_insert(char*, bool);
void caret_move(int, int);
void check_x_event(IxpConn*); void check_x_event(IxpConn*);
void debug(int, const char*, ...); void debug(int, const char*, ...);
void dispatch_event(XEvent*); void dispatch_event(XEvent*);

View File

@ -151,7 +151,7 @@ update_filter(void) {
/* TODO: Perhaps filter only previous matches unless filter /* TODO: Perhaps filter only previous matches unless filter
* has been truncated. * has been truncated.
*/ */
matchfirst = matchstart = matchidx = filter_list(items, filter); matchfirst = matchstart = matchidx = filter_list(items, input.string);
} }
/* /*
@ -264,6 +264,7 @@ main(int argc, char *argv[]) {
inbuf = Bfdopen(0, OREAD); inbuf = Bfdopen(0, OREAD);
items = populate_list(inbuf, false); items = populate_list(inbuf, false);
caret_insert("", true);
update_filter(); update_filter();
Bterm(inbuf); Bterm(inbuf);

View File

@ -12,13 +12,11 @@ static int numlock;
static void menu_draw(void); static void menu_draw(void);
enum { enum {
ACCEPT, ACCEPT = CARET_LAST,
REJECT, REJECT,
HIST_NEXT, HIST_NEXT,
HIST_PREV, HIST_PREV,
KILL_CHAR, KILL,
KILL_WORD,
KILL_LINE,
CMPL_NEXT, CMPL_NEXT,
CMPL_PREV, CMPL_PREV,
CMPL_FIRST, CMPL_FIRST,
@ -57,47 +55,29 @@ histtext(Item *i) {
if(!histidx->string) { if(!histidx->string) {
free(orig); free(orig);
orig = strdup(filter); orig = strdup(input.string);
} }
return i->string ? i->string : orig; return i->string ? i->string : orig;
} }
static void static void
menu_cmd(int op) { menu_cmd(int op, int motion) {
bool res;
int i;
i = strlen(filter);
switch(op) { switch(op) {
case HIST_NEXT: case HIST_NEXT:
if(histidx->next) { if(histidx->next) {
strncpy(filter, histtext(histidx->next), sizeof filter); caret_insert(histtext(histidx->next), true);
histidx = histidx->next; histidx = histidx->next;
} }
break; break;
case HIST_PREV: case HIST_PREV:
if(histidx->prev) { if(histidx->prev) {
strncpy(filter, histtext(histidx->prev), sizeof filter); caret_insert(histtext(histidx->prev), true);
histidx = histidx->prev; histidx = histidx->prev;
} }
break; break;
case KILL_CHAR: case KILL:
if(i > 0) caret_delete(BACKWARD, motion);
filter[i-1] = '\0';
break;
case KILL_WORD:
if(i == 0)
break;
for(i--; i >= 0 && isspace(filter[i]); i--)
filter[i] = '\0';
if(i >= 0)
res = !isalnum(filter[i]);
for(; i >= 0 && !isalnum(filter[i]) == res && !isspace(filter[i]); i--)
filter[i] = '\0';
break;
case KILL_LINE:
/* TODO: Add a caret. */
filter[0] = '\0';
break; break;
default: default:
goto next; goto next;
@ -116,6 +96,10 @@ next:
srv.running = false; srv.running = false;
result = 1; result = 1;
break; break;
case BACKWARD:
case FORWARD:
caret_move(op, motion);
break;
case CMPL_NEXT: case CMPL_NEXT:
matchidx = matchidx->next; matchidx = matchidx->next;
break; break;
@ -203,9 +187,9 @@ menu_draw(void) {
drawstring(ibuf, font, r2, East, ">", cnorm.fg); drawstring(ibuf, font, r2, East, ">", cnorm.fg);
r2 = r; r2 = r;
r2.max.x = inputw; r2.max.x = inputw;
drawstring(ibuf, font, r2, West, filter, cnorm.fg); drawstring(ibuf, font, r2, West, input.string, cnorm.fg);
r2.min.x = textwidth(font, filter) + pad/2; r2.min.x = textwidth_l(font, input.string, input.pos - input.string) + pad/2;
r2.max.x = r2.min.x + 2; r2.max.x = r2.min.x + 2;
r2.min.y++; r2.min.y++;
r2.max.y--; r2.max.y--;
@ -244,7 +228,7 @@ menu_show(void) {
static void static void
kdown_event(Window *w, XKeyEvent *e) { kdown_event(Window *w, XKeyEvent *e) {
char buf[32]; char buf[32];
int num, i; int num;
KeySym ksym; KeySym ksym;
buf[0] = 0; buf[0] = 0;
@ -268,37 +252,41 @@ kdown_event(Window *w, XKeyEvent *e) {
default: default:
return; return;
case XK_bracketleft: /* Esc */ case XK_bracketleft: /* Esc */
menu_cmd(REJECT); menu_cmd(REJECT, 0);
return; return;
case XK_j: case XK_j:
case XK_J: case XK_J:
case XK_m: case XK_m:
case XK_M: case XK_M:
menu_cmd(ACCEPT); menu_cmd(ACCEPT, 0);
return; return;
case XK_n: case XK_n:
case XK_N: case XK_N:
menu_cmd(HIST_NEXT); menu_cmd(HIST_NEXT, 0);
return; return;
case XK_p: case XK_p:
case XK_P: case XK_P:
menu_cmd(HIST_PREV); menu_cmd(HIST_PREV, 0);
return; return;
case XK_i: /* Tab */ case XK_i: /* Tab */
case XK_I: case XK_I:
menu_cmd(CMPL_NEXT); if(e->state & ShiftMask)
menu_cmd(CMPL_PREV, 0);
else
menu_cmd(CMPL_NEXT, 0);
return; return;
case XK_h: case XK_h:
case XK_H: case XK_H:
menu_cmd(KILL_CHAR); menu_cmd(KILL, CHAR);
return; return;
case XK_BackSpace:
case XK_w: case XK_w:
case XK_W: case XK_W:
menu_cmd(KILL_WORD); menu_cmd(KILL, WORD);
return; return;
case XK_u: case XK_u:
case XK_U: case XK_U:
menu_cmd(KILL_LINE); menu_cmd(KILL, LINE);
return; return;
} }
} }
@ -308,74 +296,70 @@ kdown_event(Window *w, XKeyEvent *e) {
default: default:
return; return;
case XK_h: case XK_h:
menu_cmd(CMPL_PREV); menu_cmd(CMPL_PREV, 0);
return; return;
case XK_l: case XK_l:
menu_cmd(CMPL_NEXT); menu_cmd(CMPL_NEXT, 0);
return; return;
case XK_j: case XK_j:
menu_cmd(CMPL_NEXT_PAGE); menu_cmd(CMPL_NEXT_PAGE, 0);
return; return;
case XK_k: case XK_k:
menu_cmd(CMPL_PREV_PAGE); menu_cmd(CMPL_PREV_PAGE, 0);
return; return;
case XK_g: case XK_g:
menu_cmd(CMPL_FIRST); menu_cmd(CMPL_FIRST, 0);
return; return;
case XK_G: case XK_G:
menu_cmd(CMPL_LAST); menu_cmd(CMPL_LAST, 0);
return; return;
} }
} }
switch(ksym) { switch(ksym) {
default: default:
if(num && !iscntrl(buf[0])) { if(num && !iscntrl(buf[0])) {
i = strlen(filter); caret_insert(buf, false);
if(i < sizeof filter - 1) {
filter[i] = buf[0];
filter[i+1] = '\0';
}
update_filter(); update_filter();
menu_draw(); menu_draw();
} }
break; break;
case XK_Escape: case XK_Escape:
menu_cmd(REJECT); menu_cmd(REJECT, 0);
return; return;
case XK_Return: case XK_Return:
menu_cmd(ACCEPT); menu_cmd(ACCEPT, 0);
return; return;
case XK_BackSpace: case XK_BackSpace:
menu_cmd(KILL_CHAR); menu_cmd(KILL, CHAR);
return; return;
case XK_Up: case XK_Up:
menu_cmd(HIST_PREV); menu_cmd(HIST_PREV, 0);
return; return;
case XK_Down: case XK_Down:
menu_cmd(HIST_NEXT); menu_cmd(HIST_NEXT, 0);
return; return;
case XK_Home: case XK_Home:
/* TODO: Caret. */ /* TODO: Caret. */
menu_cmd(CMPL_FIRST); menu_cmd(CMPL_FIRST, 0);
return; return;
case XK_End: case XK_End:
/* TODO: Caret. */ /* TODO: Caret. */
menu_cmd(CMPL_LAST); menu_cmd(CMPL_LAST, 0);
return; return;
case XK_Left: case XK_Left:
menu_cmd(CMPL_PREV); menu_cmd(BACKWARD, CHAR);
return; return;
case XK_Right: case XK_Right:
menu_cmd(CMPL_NEXT); menu_cmd(FORWARD, CHAR);
return; return;
case XK_Next: case XK_Next:
menu_cmd(CMPL_NEXT_PAGE); menu_cmd(CMPL_NEXT_PAGE, 0);
return; return;
case XK_Prior: case XK_Prior:
menu_cmd(CMPL_PREV_PAGE); menu_cmd(CMPL_PREV_PAGE, 0);
return; return;
case XK_Tab: case XK_Tab:
menu_cmd(CMPL_NEXT); menu_cmd(CMPL_NEXT, 0);
return; return;
} }
} }