Adding multi-select patch

This commit is contained in:
bakkeby 2020-08-08 13:30:00 +02:00
parent 841a0cff2b
commit 240cce1c1a
6 changed files with 110 additions and 8 deletions

View File

@ -15,7 +15,7 @@ Refer to [https://tools.suckless.org/dmenu/](https://tools.suckless.org/dmenu/)
### Changelog: ### Changelog:
2020-08-08 - Added the symbols, managed, morecolor and preselect patches 2020-08-08 - Added the symbols, managed, morecolor, multi-selection and preselect patches
2020-08-05 - Added the grid, highlight, highpriority, dynamic options and numbers patches 2020-08-05 - Added the grid, highlight, highpriority, dynamic options and numbers patches
@ -95,6 +95,11 @@ Refer to [https://tools.suckless.org/dmenu/](https://tools.suckless.org/dmenu/)
- [mouse-support](https://tools.suckless.org/dmenu/patches/mouse-support/) - [mouse-support](https://tools.suckless.org/dmenu/patches/mouse-support/)
- adds basic mouse support for dmenu - adds basic mouse support for dmenu
- [multi-selection](https://tools.suckless.org/dmenu/patches/multi-selection/)
- without this patch when you press `Ctrl+Enter` dmenu just outputs current item and it is not possible to undo that
- with this patch dmenu will output all selected items only on exit
- it is also possible to deselect any selected item
- [navhistory](https://tools.suckless.org/dmenu/patches/navhistory/) - [navhistory](https://tools.suckless.org/dmenu/patches/navhistory/)
- provides dmenu the ability for history navigation similar to that of bash - provides dmenu the ability for history navigation similar to that of bash

View File

@ -93,7 +93,7 @@ static int histnodup = 1; /* if 0, record repeated histories */
* Characters not considered part of a word while deleting words * Characters not considered part of a word while deleting words
* for example: " /?\"&[]" * for example: " /?\"&[]"
*/ */
#if PIPEOUT_PATCH #if PIPEOUT_PATCH && !MULTI_SELECT_PATCH
static const char startpipe[] = "#"; static const char startpipe[] = "#";
#endif // PIPEOUT_PATCH #endif // PIPEOUT_PATCH
static const char worddelimiters[] = " "; static const char worddelimiters[] = " ";

37
dmenu.c
View File

@ -59,7 +59,11 @@ struct item {
#if NON_BLOCKING_STDIN_PATCH #if NON_BLOCKING_STDIN_PATCH
struct item *next; struct item *next;
#endif // NON_BLOCKING_STDIN_PATCH #endif // NON_BLOCKING_STDIN_PATCH
#if MULTI_SELECT_PATCH
int id; /* for multiselect */
#else
int out; int out;
#endif // MULTI_SELECT_PATCH
#if HIGHPRIORITY_PATCH #if HIGHPRIORITY_PATCH
int hp; int hp;
#endif // HIGHPRIORITY_PATCH #endif // HIGHPRIORITY_PATCH
@ -69,7 +73,7 @@ struct item {
}; };
static char text[BUFSIZ] = ""; static char text[BUFSIZ] = "";
#if PIPEOUT_PATCH #if PIPEOUT_PATCH && !MULTI_SELECT_PATCH
static char pipeout[8] = " | dmenu"; static char pipeout[8] = " | dmenu";
#endif // PIPEOUT_PATCH #endif // PIPEOUT_PATCH
static char *embed; static char *embed;
@ -94,7 +98,11 @@ static int mon = -1, screen;
#if MANAGED_PATCH #if MANAGED_PATCH
static int managed = 0; static int managed = 0;
#endif // MANAGED_PATCH #endif // MANAGED_PATCH
#if PRINTINPUTTEXT_PATCH #if MULTI_SELECT_PATCH
static int *selid = NULL;
static unsigned int selidsize = 0;
#endif // MULTI_SELECT_PATCH
#if PRINTINPUTTEXT_PATCH && !MULTI_SELECT_PATCH
static int use_text_input = 0; static int use_text_input = 0;
#endif // PRINTINPUTTEXT_PATCH #endif // PRINTINPUTTEXT_PATCH
#if PRESELECT_PATCH #if PRESELECT_PATCH
@ -217,6 +225,9 @@ cleanup(void)
drw_free(drw); drw_free(drw);
XSync(dpy, False); XSync(dpy, False);
XCloseDisplay(dpy); XCloseDisplay(dpy);
#if MULTI_SELECT_PATCH
free(selid);
#endif // MULTI_SELECT_PATCH
} }
static char * static char *
@ -244,7 +255,11 @@ drawitem(struct item *item, int x, int y, int w)
else if (item->left == sel || item->right == sel) else if (item->left == sel || item->right == sel)
drw_setscheme(drw, scheme[SchemeMid]); drw_setscheme(drw, scheme[SchemeMid]);
#endif // MORECOLOR_PATCH #endif // MORECOLOR_PATCH
#if MULTI_SELECT_PATCH
else if (issel(item->id))
#else
else if (item->out) else if (item->out)
#endif // MULTI_SELECT_PATCH
drw_setscheme(drw, scheme[SchemeOut]); drw_setscheme(drw, scheme[SchemeOut]);
else else
drw_setscheme(drw, scheme[SchemeNorm]); drw_setscheme(drw, scheme[SchemeNorm]);
@ -755,6 +770,9 @@ keypress(XKeyEvent *ev)
goto draw; goto draw;
case XK_Return: case XK_Return:
case XK_KP_Enter: case XK_KP_Enter:
#if MULTI_SELECT_PATCH
selsel();
#endif // MULTI_SELECT_PATCH
break; break;
case XK_bracketleft: case XK_bracketleft:
cleanup(); cleanup();
@ -856,6 +874,7 @@ insert:
break; break;
case XK_Return: case XK_Return:
case XK_KP_Enter: case XK_KP_Enter:
#if !MULTI_SELECT_PATCH
#if PIPEOUT_PATCH #if PIPEOUT_PATCH
#if PRINTINPUTTEXT_PATCH #if PRINTINPUTTEXT_PATCH
if (sel && ( if (sel && (
@ -886,16 +905,22 @@ insert:
#else #else
puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); puts((sel && !(ev->state & ShiftMask)) ? sel->text : text);
#endif // PIPEOUT_PATCH #endif // PIPEOUT_PATCH
#endif // MULTI_SELECT_PATCH
if (!(ev->state & ControlMask)) { if (!(ev->state & ControlMask)) {
#if NAVHISTORY_PATCH #if NAVHISTORY_PATCH
savehistory((sel && !(ev->state & ShiftMask)) savehistory((sel && !(ev->state & ShiftMask))
? sel->text : text); ? sel->text : text);
#endif // NAVHISTORY_PATCH #endif // NAVHISTORY_PATCH
#if MULTI_SELECT_PATCH
printsel(ev->state);
#endif // MULTI_SELECT_PATCH
cleanup(); cleanup();
exit(0); exit(0);
} }
#if !MULTI_SELECT_PATCH
if (sel) if (sel)
sel->out = 1; sel->out = 1;
#endif // MULTI_SELECT_PATCH
break; break;
case XK_Right: case XK_Right:
if (text[cursor] != '\0') { if (text[cursor] != '\0') {
@ -1026,7 +1051,11 @@ readstdin(void)
*p = '\0'; *p = '\0';
if (!(items[i].text = strdup(buf))) if (!(items[i].text = strdup(buf)))
die("cannot strdup %u bytes:", strlen(buf) + 1); die("cannot strdup %u bytes:", strlen(buf) + 1);
#if MULTI_SELECT_PATCH
items[i].id = i; /* for multiselect */
#else
items[i].out = 0; items[i].out = 0;
#endif // items[i].out = 0;
#if HIGHPRIORITY_PATCH #if HIGHPRIORITY_PATCH
items[i].hp = arrayhas(hpitems, hplength, items[i].text); items[i].hp = arrayhas(hpitems, hplength, items[i].text);
#endif // HIGHPRIORITY_PATCH #endif // HIGHPRIORITY_PATCH
@ -1359,7 +1388,7 @@ usage(void)
#if INSTANT_PATCH #if INSTANT_PATCH
"n" "n"
#endif // INSTANT_PATCH #endif // INSTANT_PATCH
#if PRINTINPUTTEXT_PATCH #if PRINTINPUTTEXT_PATCH && !MULTI_SELECT_PATCH
"t" "t"
#endif // PRINTINPUTTEXT_PATCH #endif // PRINTINPUTTEXT_PATCH
#if PREFIXCOMPLETION_PATCH #if PREFIXCOMPLETION_PATCH
@ -1465,7 +1494,7 @@ main(int argc, char *argv[])
} else if (!strcmp(argv[i], "-n")) { /* instant select only match */ } else if (!strcmp(argv[i], "-n")) { /* instant select only match */
instant = !instant; instant = !instant;
#endif // INSTANT_PATCH #endif // INSTANT_PATCH
#if PRINTINPUTTEXT_PATCH #if PRINTINPUTTEXT_PATCH && !MULTI_SELECT_PATCH
} else if (!strcmp(argv[i], "-t")) { /* favors text input over selection */ } else if (!strcmp(argv[i], "-t")) { /* favors text input over selection */
use_text_input = 1; use_text_input = 1;
#endif // PRINTINPUTTEXT_PATCH #endif // PRINTINPUTTEXT_PATCH

View File

@ -15,6 +15,9 @@
#if HIGHPRIORITY_PATCH #if HIGHPRIORITY_PATCH
#include "highpriority.c" #include "highpriority.c"
#endif #endif
#if MULTI_SELECT_PATCH
#include "multiselect.c"
#endif
#if MOUSE_SUPPORT_PATCH #if MOUSE_SUPPORT_PATCH
#include "mousesupport.c" #include "mousesupport.c"
#endif #endif

View File

@ -21,6 +21,16 @@ buttonpress(XEvent *e)
/* left-click on input: clear input, /* left-click on input: clear input,
* NOTE: if there is no left-arrow the space for < is reserved so * NOTE: if there is no left-arrow the space for < is reserved so
* add that to the input width */ * add that to the input width */
#if SYMBOLS_PATCH
if (ev->button == Button1 &&
((lines <= 0 && ev->x >= 0 && ev->x <= x + w +
((!prev || !curr->left) ? TEXTW(symbol_1) : 0)) ||
(lines > 0 && ev->y >= y && ev->y <= y + h))) {
insert(NULL, -cursor);
drawmenu();
return;
}
#else
if (ev->button == Button1 && if (ev->button == Button1 &&
((lines <= 0 && ev->x >= 0 && ev->x <= x + w + ((lines <= 0 && ev->x >= 0 && ev->x <= x + w +
((!prev || !curr->left) ? TEXTW("<") : 0)) || ((!prev || !curr->left) ? TEXTW("<") : 0)) ||
@ -29,6 +39,7 @@ buttonpress(XEvent *e)
drawmenu(); drawmenu();
return; return;
} }
#endif // SYMBOLS_PATCH
/* middle-mouse click: paste selection */ /* middle-mouse click: paste selection */
if (ev->button == Button2) { if (ev->button == Button2) {
XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
@ -60,12 +71,24 @@ buttonpress(XEvent *e)
for (item = curr; item != next; item = item->right) { for (item = curr; item != next; item = item->right) {
y += h; y += h;
if (ev->y >= y && ev->y <= (y + h)) { if (ev->y >= y && ev->y <= (y + h)) {
#if !MULTI_SELECT_PATCH
puts(item->text); puts(item->text);
if (!(ev->state & ControlMask)) #endif // MULTI_SELECT_PATCH
if (!(ev->state & ControlMask)) {
#if MULTI_SELECT_PATCH
sel = item;
selsel();
printsel(ev->state);
#endif // MULTI_SELECT_PATCH
exit(0); exit(0);
}
sel = item; sel = item;
if (sel) { if (sel) {
#if MULTI_SELECT_PATCH
selsel();
#else
sel->out = 1; sel->out = 1;
#endif // MULTI_SELECT_PATCH
drawmenu(); drawmenu();
} }
return; return;
@ -74,7 +97,11 @@ buttonpress(XEvent *e)
} else if (matches) { } else if (matches) {
/* left-click on left arrow */ /* left-click on left arrow */
x += inputw; x += inputw;
#if SYMBOLS_PATCH
w = TEXTW(symbol_1);
#else
w = TEXTW("<"); w = TEXTW("<");
#endif // SYMBOLS_PATCH
if (prev && curr->left) { if (prev && curr->left) {
if (ev->x >= x && ev->x <= x + w) { if (ev->x >= x && ev->x <= x + w) {
sel = curr = prev; sel = curr = prev;
@ -86,21 +113,41 @@ buttonpress(XEvent *e)
/* horizontal list: (ctrl)left-click on item */ /* horizontal list: (ctrl)left-click on item */
for (item = curr; item != next; item = item->right) { for (item = curr; item != next; item = item->right) {
x += w; x += w;
#if SYMBOLS_PATCH
w = MIN(TEXTW(item->text), mw - x - TEXTW(symbol_2));
#else
w = MIN(TEXTW(item->text), mw - x - TEXTW(">")); w = MIN(TEXTW(item->text), mw - x - TEXTW(">"));
#endif // SYMBOLS_PATCH
if (ev->x >= x && ev->x <= x + w) { if (ev->x >= x && ev->x <= x + w) {
#if !MULTI_SELECT_PATCH
puts(item->text); puts(item->text);
if (!(ev->state & ControlMask)) #endif // MULTI_SELECT_PATCH
if (!(ev->state & ControlMask)) {
#if MULTI_SELECT_PATCH
sel = item;
selsel();
printsel(ev->state);
#endif // MULTI_SELECT_PATCH
exit(0); exit(0);
}
sel = item; sel = item;
if (sel) { if (sel) {
#if MULTI_SELECT_PATCH
selsel();
#else
sel->out = 1; sel->out = 1;
#endif // MULTI_SELECT_PATCH
drawmenu(); drawmenu();
} }
return; return;
} }
} }
/* left-click on right arrow */ /* left-click on right arrow */
#if SYMBOLS_PATCH
w = TEXTW(symbol_2);
#else
w = TEXTW(">"); w = TEXTW(">");
#endif // SYMBOLS_PATCH
x = mw - w; x = mw - w;
if (next && ev->x >= x && ev->x <= x + w) { if (next && ev->x >= x && ev->x <= x + w) {
sel = curr = next; sel = curr = next;

View File

@ -110,6 +110,18 @@
*/ */
#define MOUSE_SUPPORT_PATCH 0 #define MOUSE_SUPPORT_PATCH 0
/* Without this patch when you press Ctrl+Enter dmenu just outputs current item and it is not
* possible to undo that.
* With this patch dmenu will output all selected items only on exit. It is also possible to
* deselect any selected item.
* Also refer to the dmenu_run replacement on the below URL that supports multiple selections.
*
* This patch is not compatible with the printinputtext and pipeout patches.
*
* https://tools.suckless.org/dmenu/patches/multi-selection/
*/
#define MULTI_SELECT_PATCH 0
/* This patch provides dmenu the ability for history navigation similar to that of bash. /* This patch provides dmenu the ability for history navigation similar to that of bash.
* https://tools.suckless.org/dmenu/patches/navhistory/ * https://tools.suckless.org/dmenu/patches/navhistory/
*/ */
@ -160,6 +172,9 @@
/* This patch allows the selected text to be piped back out with dmenu. This can be useful if you /* This patch allows the selected text to be piped back out with dmenu. This can be useful if you
* want to display the output of a command on the screen. * want to display the output of a command on the screen.
* Only text starting with the character '#' is piped out by default. * Only text starting with the character '#' is piped out by default.
*
* This patch is not compatible with the multi-select patch.
*
* https://tools.suckless.org/dmenu/patches/pipeout/ * https://tools.suckless.org/dmenu/patches/pipeout/
*/ */
#define PIPEOUT_PATCH 0 #define PIPEOUT_PATCH 0
@ -177,6 +192,9 @@
/* This patch adds a flag (-t) which makes Return key to ignore selection and print the input /* This patch adds a flag (-t) which makes Return key to ignore selection and print the input
* text to stdout. The flag basically swaps the functions of Return and Shift+Return hotkeys. * text to stdout. The flag basically swaps the functions of Return and Shift+Return hotkeys.
*
* This patch is not compatible with the multi-select patch.
*
* https://tools.suckless.org/dmenu/patches/printinputtext/ * https://tools.suckless.org/dmenu/patches/printinputtext/
*/ */
#define PRINTINPUTTEXT_PATCH 0 #define PRINTINPUTTEXT_PATCH 0