From 2003b7bd5288846bc03bdd76751542661c53c244 Mon Sep 17 00:00:00 2001 From: Dave Davenport Date: Sat, 11 Feb 2023 12:42:35 +0100 Subject: [Dmenu] Implement a select rows for dmenu multi-select. Issue: #1802 --- doc/rofi-dmenu.5 | 7 ++++ doc/rofi-dmenu.5.markdown | 5 +++ doc/rofi-theme.5 | 5 +-- source/modes/dmenu.c | 82 ++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 92 insertions(+), 7 deletions(-) diff --git a/doc/rofi-dmenu.5 b/doc/rofi-dmenu.5 index c11dc96c..a462389b 100644 --- a/doc/rofi-dmenu.5 +++ b/doc/rofi-dmenu.5 @@ -147,6 +147,13 @@ Or any combination: '5,-3:,7:11,2,0,-9' .PP Urgent row, mark \fIX\fP as urgent. See \fB\fC-a\fR option for details. +.PP +\fB\fC-select-rows\fR \fIX\fP + +.PP +If multi-select is enabled, pre-select rows, See \fB\fC-a\fR option for format details. +If same row is specified multiple times, it state is toggled on subsequential sets. + .PP \fB\fC-only-match\fR diff --git a/doc/rofi-dmenu.5.markdown b/doc/rofi-dmenu.5.markdown index ff2f0203..2c79b675 100644 --- a/doc/rofi-dmenu.5.markdown +++ b/doc/rofi-dmenu.5.markdown @@ -93,6 +93,11 @@ Active row, mark *X* as active. Where *X* is a comma-separated list of python(1) Urgent row, mark *X* as urgent. See `-a` option for details. +`-select-rows` *X* + +If multi-select is enabled, pre-select rows, See `-a` option for format details. +If same row is specified multiple times, it state is toggled on subsequential sets. + `-only-match` Only return a selected item, do not allow custom entry. diff --git a/doc/rofi-theme.5 b/doc/rofi-theme.5 index a5887a46..187c2102 100644 --- a/doc/rofi-theme.5 +++ b/doc/rofi-theme.5 @@ -1615,7 +1615,7 @@ property. These cannot be changed using the \fB\fCchildren\fR property. .PP -Each entries displayed by listview are captured by a \fB\fCbox\fR called \fB\fCelement\fR\&. +Each Entry displayed by listview is captured by a \fB\fCbox\fR called \fB\fCelement\fR\&. An \fB\fCelement\fR widget can contain the following special child widgets: .RS @@ -1630,7 +1630,8 @@ An \fB\fCelement\fR widget can contain the following special child widgets: .PP By default the \fB\fCelement-icon\fR and \fB\fCelement-text\fR child widgets are added to the -\fB\fCelement\fR\&. This can be modified using the \fB\fCchildren\fR property. +\fB\fCelement\fR\&. This can be modified using the \fB\fCchildren\fR property or the +\fB\fC[no]-show-icons\fR option. .PP A child added with another name is seen as a \fB\fCbox\fR, this can be used as dynamic diff --git a/source/modes/dmenu.c b/source/modes/dmenu.c index 0b604e78..91bdfdc9 100644 --- a/source/modes/dmenu.c +++ b/source/modes/dmenu.c @@ -123,6 +123,66 @@ typedef struct { DmenuModePrivateData *pd; } Block; + +static void dmenu_parse_multi_select_range ( DmenuModePrivateData *pd, const char *entries) +{ + if ( entries == NULL ) { + return; + } + // Pre-alloc array. + if (pd->selected_list == NULL) { + pd->selected_list = + g_malloc0(sizeof(uint32_t) * (pd->cmd_list_length / 32 + 1)); + } + char *entries_cp = g_strdup ( entries ); + char *endp; + const char *const sep = ","; + for (char *token = strtok_r(entries_cp, sep, &endp); token != NULL; + token = strtok_r(NULL, sep, &endp)) { + const char *sep[] = {"-", ":"}; + int pythonic = (strchr(token, ':') || token[0] == '-') ? 1 : 0; + int index = 0; + + int start = -1; + int stop = -1; + for (char *inner_token = strsep(&token, sep[pythonic]); inner_token != NULL; + inner_token = strsep(&token, sep[pythonic])) { + if (index == 0) { + start = stop = (int)strtol(inner_token, NULL, 10); + index++; + continue; + } + + if (inner_token[0] == '\0') { + stop = -1; + continue; + } + + stop = (int)strtol(inner_token, NULL, 10); + if (pythonic) { + --stop; + } + } + // Fix negative numbers. + if ( start < 0 ) { + start = pd->cmd_list_length + start; + } + if ( stop < 0 ) { + stop = pd->cmd_list_length + stop; + } + // Fix starting + for ( int index = start; index <= stop; index++ ){ + if ( index < 0 ) { + index = pd->cmd_list_length - index; + } + if ( index < (int)pd->cmd_list_length ) { + bittoggle(pd->selected_list, index); + } + } + } + g_free ( entries_cp ); +} + static void read_add_block(DmenuModePrivateData *pd, Block **block, char *data, gsize len) { @@ -514,14 +574,20 @@ static int dmenu_mode_init(Mode *sw) { DmenuModePrivateData *pd = (DmenuModePrivateData *)mode_get_private_data(sw); pd->async = TRUE; + pd->multi_select = FALSE; // For now these only work in sync mode. if (find_arg("-sync") >= 0 || find_arg("-dump") >= 0 || find_arg("-select") >= 0 || find_arg("-no-custom") >= 0 || find_arg("-only-match") >= 0 || config.auto_select || - find_arg("-selected-row") >= 0) { + find_arg("-selected-row") >= 0 ) { pd->async = FALSE; } + // In multi-select mode we should disable async mode. + if ( find_arg("-multi-select") >= 0 ) { + pd->async = FALSE; + pd->multi_select = TRUE; + } pd->separator = '\n'; pd->selected_line = UINT32_MAX; @@ -634,6 +700,14 @@ static int dmenu_mode_init(Mode *sw) { read_input_sync(pd, -1); } + + if ( pd->multi_select ) { + char *entries = NULL; + if ( find_arg_str ( "-select-rows", &entries ) >= 0 ) { + dmenu_parse_multi_select_range(pd, entries); + } + } + gchar *columns = NULL; if (find_arg_str("-display-columns", &columns)) { pd->columns = g_strsplit(columns, ",", 0); @@ -907,14 +981,10 @@ int dmenu_mode_dialog(void) { DmenuScriptEntry *cmd_list = pd->cmd_list; pd->only_selected = FALSE; - pd->multi_select = FALSE; pd->ballot_selected = "☑ "; pd->ballot_unselected = "☐ "; find_arg_str("-ballot-selected-str", &(pd->ballot_selected)); find_arg_str("-ballot-unselected-str", &(pd->ballot_unselected)); - if (find_arg("-multi-select") >= 0) { - pd->multi_select = TRUE; - } if (find_arg("-markup-rows") >= 0) { pd->do_markup = TRUE; } @@ -1006,6 +1076,8 @@ void print_dmenu_options(void) { is_term); print_help_msg("-a", "[list]", "List of row indexes to mark active", NULL, is_term); + print_help_msg("-select-rows", "[list]", "List of row indexes to select when multi-select is enabled", NULL, + is_term); print_help_msg("-l", "[integer] ", "Number of rows to display", NULL, is_term); print_help_msg("-window-title", "[string] ", "Set the dmenu window title", -- cgit v1.2.3