mirror of
				https://github.com/davatorium/rofi.git
				synced 2025-10-30 23:47:19 -04:00 
			
		
		
		
	Merge remote-tracking branch 'upstream/next' into wayland
This commit is contained in:
		
						commit
						cebb0fd1a7
					
				
					 38 changed files with 732 additions and 214 deletions
				
			
		
							
								
								
									
										4
									
								
								.github/workflows/codeql.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/codeql.yml
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -37,7 +37,7 @@ jobs:
 | 
			
		|||
 | 
			
		||||
    # Initializes the CodeQL tools for scanning.
 | 
			
		||||
    - name: Initialize CodeQL
 | 
			
		||||
      uses: github/codeql-action/init@v2
 | 
			
		||||
      uses: github/codeql-action/init@v3
 | 
			
		||||
      with:
 | 
			
		||||
        languages: ${{ matrix.language }}
 | 
			
		||||
        # If you wish to specify custom queries, you can do so here or in a config file.
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +52,6 @@ jobs:
 | 
			
		|||
        cc: gcc
 | 
			
		||||
 | 
			
		||||
    - name: Perform CodeQL Analysis
 | 
			
		||||
      uses: github/codeql-action/analyze@v2
 | 
			
		||||
      uses: github/codeql-action/analyze@v3
 | 
			
		||||
      with:
 | 
			
		||||
        category: "/language:${{matrix.language}}"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -182,6 +182,7 @@ if FOUND_PANDOC
 | 
			
		|||
generate-manpage: doc/rofi.1\
 | 
			
		||||
	doc/rofi-sensible-terminal.1\
 | 
			
		||||
	doc/rofi-theme-selector.1\
 | 
			
		||||
	doc/rofi-actions.5\
 | 
			
		||||
	doc/rofi-debugging.5\
 | 
			
		||||
	doc/rofi-dmenu.5\
 | 
			
		||||
	doc/rofi-keys.5\
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,6 +49,18 @@ Settings config = {
 | 
			
		|||
    /** Custom command to generate preview icons */
 | 
			
		||||
    .preview_cmd = NULL,
 | 
			
		||||
 | 
			
		||||
    /** Custom command to call when menu selection changes */
 | 
			
		||||
    .on_selection_changed = NULL,
 | 
			
		||||
    /** Custom command to call when menu mode changes */
 | 
			
		||||
    .on_mode_changed = NULL,
 | 
			
		||||
    /** Custom command to call when menu entry is accepted */
 | 
			
		||||
    .on_entry_accepted = NULL,
 | 
			
		||||
    /** Custom command to call when menu is canceled */
 | 
			
		||||
    .on_menu_canceled = NULL,
 | 
			
		||||
    /** Custom command to call when menu finds errors */
 | 
			
		||||
    .on_menu_error = NULL,
 | 
			
		||||
    /** Custom command to call when menu screenshot is taken */
 | 
			
		||||
    .on_screenshot_taken = NULL,
 | 
			
		||||
    /** Terminal to use. (for ssh and open in terminal) */
 | 
			
		||||
    .terminal_emulator = "rofi-sensible-terminal",
 | 
			
		||||
    .ssh_client = "ssh",
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +104,8 @@ Settings config = {
 | 
			
		|||
    .sorting_method = "normal",
 | 
			
		||||
    /** Case sensitivity of the search */
 | 
			
		||||
    .case_sensitive = FALSE,
 | 
			
		||||
    /** Case smart of the search */
 | 
			
		||||
    .case_smart = FALSE,
 | 
			
		||||
    /** Cycle through in the element list */
 | 
			
		||||
    .cycle = TRUE,
 | 
			
		||||
    /** Height of an element in #chars */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
AC_INIT([rofi], [1.7.8], [https://github.com/davatorium/rofi/],[],[https://github.com/davatorium/rofi/discussions])
 | 
			
		||||
AC_INIT([rofi], [1.7.8-dev], [https://github.com/davatorium/rofi/],[],[https://github.com/davatorium/rofi/discussions])
 | 
			
		||||
 | 
			
		||||
AC_CONFIG_SRCDIR([source/rofi.c])
 | 
			
		||||
AC_CONFIG_HEADER([config.h])
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -141,10 +141,17 @@ textbox-num-sep {
 | 
			
		|||
    str:        "/";
 | 
			
		||||
}
 | 
			
		||||
inputbar {
 | 
			
		||||
    padding:    1px ;
 | 
			
		||||
    spacing:    0px ;
 | 
			
		||||
    padding:    1px;
 | 
			
		||||
    spacing:    0px;
 | 
			
		||||
    text-color: var(normal-foreground);
 | 
			
		||||
    children:   [ prompt,textbox-prompt-colon,entry, num-filtered-rows, textbox-num-sep, num-rows, case-indicator ];
 | 
			
		||||
    children:   [ prompt,textbox-prompt-colon,entry, overlay,num-filtered-rows, textbox-num-sep, num-rows, case-indicator ];
 | 
			
		||||
}
 | 
			
		||||
overlay {
 | 
			
		||||
    background-color: var(normal-foreground);
 | 
			
		||||
    foreground-color: var(normal-background);
 | 
			
		||||
    text-color: var(normal-background);
 | 
			
		||||
    padding: 0 0.2em;
 | 
			
		||||
    margin: 0 0.2em;
 | 
			
		||||
}
 | 
			
		||||
case-indicator {
 | 
			
		||||
    spacing:    0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ man_files = [
 | 
			
		|||
    'rofi.1',
 | 
			
		||||
    'rofi-sensible-terminal.1',
 | 
			
		||||
    'rofi-theme-selector.1',
 | 
			
		||||
    'rofi-actions.5',
 | 
			
		||||
    'rofi-debugging.5',
 | 
			
		||||
    'rofi-dmenu.5',
 | 
			
		||||
    'rofi-keys.5',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										89
									
								
								doc/rofi-actions.5.markdown
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								doc/rofi-actions.5.markdown
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,89 @@
 | 
			
		|||
# rofi-actions(5)
 | 
			
		||||
 | 
			
		||||
## NAME
 | 
			
		||||
 | 
			
		||||
**rofi-actions** - Custom commands following interaction with rofi menus
 | 
			
		||||
 | 
			
		||||
## DESCRIPTION
 | 
			
		||||
 | 
			
		||||
**rofi** allows to set custom commands or scripts to be executed when some actions are performed in the menu, such as changing selection, accepting an entry or canceling.
 | 
			
		||||
 | 
			
		||||
This makes it possible for example to play sound effects or read aloud menu entries on selection.
 | 
			
		||||
 | 
			
		||||
## USAGE
 | 
			
		||||
 | 
			
		||||
Following is the list of rofi flags for specifying custom commands or scripts to execute on supported actions:
 | 
			
		||||
 | 
			
		||||
`-on-selection-changed` *cmd*
 | 
			
		||||
 | 
			
		||||
Command or script to run when the current selection changes. Selected text is forwarded to the command replacing the pattern *{entry}*.
 | 
			
		||||
 | 
			
		||||
`-on-entry-accepted` *cmd*
 | 
			
		||||
 | 
			
		||||
Command or script to run when a menu entry is accepted. Accepted text is forwarded to the command replacing the pattern *{entry}*.
 | 
			
		||||
 | 
			
		||||
`-on-mode-changed` *cmd*
 | 
			
		||||
 | 
			
		||||
Command or script to run when the menu mode (e.g. drun,window,ssh...) is changed.
 | 
			
		||||
 | 
			
		||||
`-on-menu-canceled` *cmd*
 | 
			
		||||
 | 
			
		||||
Command or script to run when the menu is canceled.
 | 
			
		||||
 | 
			
		||||
`-on-menu-error` *cmd*
 | 
			
		||||
 | 
			
		||||
Command or script to run when an error menu is shown (e.g. `rofi -e "error message"`). Error text is forwarded to the command replacing the pattern *{error}*.
 | 
			
		||||
 | 
			
		||||
`-on-screenshot-taken` *cmd*
 | 
			
		||||
 | 
			
		||||
Command or script to run when a screenshot of rofi is taken. Screenshot path is forwarded to the command replacing the pattern *{path}*.
 | 
			
		||||
 | 
			
		||||
### Example usage
 | 
			
		||||
 | 
			
		||||
Rofi command line:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
rofi -on-selection-changed "/path/to/select.sh {entry}" \
 | 
			
		||||
     -on-entry-accepted "/path/to/accept.sh {entry}" \
 | 
			
		||||
     -on-menu-canceled "/path/to/exit.sh" \
 | 
			
		||||
     -on-mode-changed "/path/to/change.sh" \
 | 
			
		||||
     -on-menu-error "/path/to/error.sh {error}" \
 | 
			
		||||
     -on-screenshot-taken "/path/to/camera.sh {path}" \
 | 
			
		||||
     -show drun
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Rofi config file:
 | 
			
		||||
 | 
			
		||||
```css
 | 
			
		||||
configuration {
 | 
			
		||||
    on-selection-changed: "/path/to/select.sh {entry}";
 | 
			
		||||
    on-entry-accepted: "/path/to/accept.sh {entry}";
 | 
			
		||||
    on-menu-canceled: "/path/to/exit.sh";
 | 
			
		||||
    on-mode-changed: "/path/to/change.sh";
 | 
			
		||||
    on-menu-error: "/path/to/error.sh {error}";
 | 
			
		||||
    on-screenshot-taken: "/path/to/camera.sh {path}";
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Play sound effects
 | 
			
		||||
 | 
			
		||||
Here's an example bash script that plays a sound effect using `aplay` when the current selection is changed:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
coproc aplay -q $HOME/Music/selecting_an_item.wav
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The use of `coproc` for playing sounds is suggested, otherwise the rofi process will wait for sounds to end playback before exiting.
 | 
			
		||||
 | 
			
		||||
### Read aloud
 | 
			
		||||
 | 
			
		||||
Here's an example bash script that reads aloud currently selected entries using `espeak`:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
killall espeak
 | 
			
		||||
echo "selected: $@" | espeak
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +159,7 @@ Hide the input text. This should not be considered secure!
 | 
			
		|||
`-markup-rows`
 | 
			
		||||
 | 
			
		||||
Tell **rofi** that DMenu input is Pango markup encoded, and should be rendered.
 | 
			
		||||
See [here](https://developer.gnome.org/pygtk/stable/pango-markup-language.html)
 | 
			
		||||
See [here](https://docs.gtk.org/Pango/pango_markup.html)
 | 
			
		||||
for details about Pango markup.
 | 
			
		||||
 | 
			
		||||
`-multi-select`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -495,6 +495,18 @@ Go down in the entry history.
 | 
			
		|||
 | 
			
		||||
Default:    Control+Down
 | 
			
		||||
 | 
			
		||||
`kb-matcher-up`
 | 
			
		||||
 | 
			
		||||
Select the next matcher.
 | 
			
		||||
 | 
			
		||||
Default: Super+equal
 | 
			
		||||
 | 
			
		||||
`kb-matcher-down`
 | 
			
		||||
 | 
			
		||||
Select the previous matcher.
 | 
			
		||||
 | 
			
		||||
Default: Super+minus
 | 
			
		||||
 | 
			
		||||
## Mouse Bindings
 | 
			
		||||
 | 
			
		||||
`ml-row-left`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,7 @@ An integer number with the current state:
 | 
			
		|||
- **0**: Initial call of script.
 | 
			
		||||
- **1**: Selected an entry.
 | 
			
		||||
- **2**: Selected a custom entry.
 | 
			
		||||
- **3**: Deleted an entry.
 | 
			
		||||
- **10-28**: Custom keybinding 1-19 ( need to be explicitly enabled by script ).
 | 
			
		||||
 | 
			
		||||
### `ROFI_INFO`
 | 
			
		||||
| 
						 | 
				
			
			@ -109,7 +110,7 @@ The following extra options exists:
 | 
			
		|||
-   **keep-selection**: If set, the selection is not moved to the first entry,
 | 
			
		||||
    but the current position is maintained. The filter is cleared.
 | 
			
		||||
 | 
			
		||||
-   **keep-filter**: If set, the filter is not cleared. 
 | 
			
		||||
-   **keep-filter**: If set, the filter is not cleared.
 | 
			
		||||
 | 
			
		||||
-   **new-selection**: If `keep-selection` is set, this allows you to override
 | 
			
		||||
    the selected entry (absolute position).
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1118,6 +1118,10 @@ The following properties are currently supported:
 | 
			
		|||
- **require-input**:    boolean Listview requires user input to be unhidden.
 | 
			
		||||
    The list is still present and hitting accept will activate the first entry.
 | 
			
		||||
 | 
			
		||||
### Overlay widget
 | 
			
		||||
 | 
			
		||||
- **timeout**: The time the widget is visible when showing a temporary message.
 | 
			
		||||
 | 
			
		||||
## Listview widget
 | 
			
		||||
 | 
			
		||||
The listview widget is special container widget.
 | 
			
		||||
| 
						 | 
				
			
			@ -1658,6 +1662,14 @@ If a filename is provided, it will try to resolve it in the following order:
 | 
			
		|||
A name is resolved (if it has no valid extension) as a filename by appending the `.rasi` and the `.rasinc` extension.
 | 
			
		||||
It will first look for files with `.rasi`, then for files with `.rasinc`.
 | 
			
		||||
 | 
			
		||||
If you want to do an optional import, e.g. no error when the file does not exists, you can do:
 | 
			
		||||
 | 
			
		||||
```css
 | 
			
		||||
?import "myfile"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This still throws an error on syntax error, but won't abort parsing if file does not exists.
 | 
			
		||||
 | 
			
		||||
## Examples
 | 
			
		||||
 | 
			
		||||
Several examples are installed together with **rofi**. These can be found in
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -246,6 +246,12 @@ exec command. For that case, `#` can be used as a separator.
 | 
			
		|||
Start in case-sensitive mode. This option can be changed at run-time using the
 | 
			
		||||
`-kb-toggle-case-sensitivity` key binding.
 | 
			
		||||
 | 
			
		||||
`-case-smart`
 | 
			
		||||
 | 
			
		||||
Start in case-smart mode behave like vim's `smartcase`, which determines
 | 
			
		||||
case-sensitivity by input.  When enabled, this will suppress `-case-sensitive`
 | 
			
		||||
config.
 | 
			
		||||
 | 
			
		||||
`-cycle`
 | 
			
		||||
 | 
			
		||||
Cycle through the result list. Default is 'true'.
 | 
			
		||||
| 
						 | 
				
			
			@ -355,6 +361,9 @@ Currently, the following methods are supported:
 | 
			
		|||
 | 
			
		||||
Default: *normal*
 | 
			
		||||
 | 
			
		||||
Multiple matching methods can be specified in a comma separated list.
 | 
			
		||||
The matching up/down keybinding allows cycling through at runtime.
 | 
			
		||||
 | 
			
		||||
Note: glob matching might be slow for larger lists
 | 
			
		||||
 | 
			
		||||
`-tokenize`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -200,13 +200,15 @@ char *rofi_expand_path(const char *input);
 | 
			
		|||
 * @param needlelen The length of the needle
 | 
			
		||||
 * @param haystack The string to match against
 | 
			
		||||
 * @param haystacklen The length of the haystack
 | 
			
		||||
 * @param case_sensitive Whether case is significant.
 | 
			
		||||
 *
 | 
			
		||||
 * UTF-8 aware levenshtein distance calculation
 | 
			
		||||
 *
 | 
			
		||||
 * @returns the levenshtein distance between needle and haystack
 | 
			
		||||
 */
 | 
			
		||||
unsigned int levenshtein(const char *needle, const glong needlelen,
 | 
			
		||||
                         const char *haystack, const glong haystacklen);
 | 
			
		||||
                         const char *haystack, const glong haystacklen,
 | 
			
		||||
                         const int case_sensitive);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param data the unvalidated character array holding possible UTF-8 data
 | 
			
		||||
| 
						 | 
				
			
			@ -234,6 +236,7 @@ char *rofi_latin_to_utf8_strdup(const char *input, gssize length);
 | 
			
		|||
 * @param plen      Pattern length.
 | 
			
		||||
 * @param str       The input to match against pattern.
 | 
			
		||||
 * @param slen      Length of str.
 | 
			
		||||
 * @param case_sensitive Whether case is significant.
 | 
			
		||||
 *
 | 
			
		||||
 *  rofi_scorer_fuzzy_evaluate implements a global sequence alignment algorithm
 | 
			
		||||
 * to find the maximum accumulated score by aligning `pattern` to `str`. It
 | 
			
		||||
| 
						 | 
				
			
			@ -263,7 +266,7 @@ char *rofi_latin_to_utf8_strdup(const char *input, gssize length);
 | 
			
		|||
 * @returns the sorting weight.
 | 
			
		||||
 */
 | 
			
		||||
int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str,
 | 
			
		||||
                               glong slen);
 | 
			
		||||
                               glong slen, const int case_sensitive);
 | 
			
		||||
/*@}*/
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -353,6 +356,13 @@ cairo_surface_t *cairo_image_surface_create_from_svg(const gchar *file,
 | 
			
		|||
 */
 | 
			
		||||
void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param input String to parse
 | 
			
		||||
 *
 | 
			
		||||
 * @returns String matching should be case sensitive or insensitive
 | 
			
		||||
 */
 | 
			
		||||
int parse_case_sensitivity(const char *input);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param format The format string used. See below for possible syntax.
 | 
			
		||||
 * @param string The selected entry.
 | 
			
		||||
| 
						 | 
				
			
			@ -432,6 +442,19 @@ ConfigEntry *rofi_config_find_widget(const char *name, const char *state,
 | 
			
		|||
 */
 | 
			
		||||
Property *rofi_theme_find_property(ConfigEntry *widget, PropertyType type,
 | 
			
		||||
                                   const char *property, gboolean exact);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @returns get a human readable string with the current matching method.
 | 
			
		||||
 */
 | 
			
		||||
const char *helper_get_matching_mode_str(void);
 | 
			
		||||
/**
 | 
			
		||||
 * Switch to the next matching method.
 | 
			
		||||
 */
 | 
			
		||||
void helper_select_next_matching_mode(void);
 | 
			
		||||
/**
 | 
			
		||||
 * Switch to the previous matching method.
 | 
			
		||||
 */
 | 
			
		||||
void helper_select_previous_matching_mode(void);
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
 | 
			
		||||
/**@} */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -145,6 +145,8 @@ typedef enum {
 | 
			
		|||
  SELECT_ELEMENT_10,
 | 
			
		||||
  ENTRY_HISTORY_UP,
 | 
			
		||||
  ENTRY_HISTORY_DOWN,
 | 
			
		||||
  MATCHER_UP,
 | 
			
		||||
  MATCHER_DOWN
 | 
			
		||||
} KeyBindingAction;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,9 +31,6 @@
 | 
			
		|||
#include <gmodule.h>
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
/** ABI version to check if loaded plugin is compatible. */
 | 
			
		||||
#define ABI_VERSION 7u
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Indicator what type of mode this is.
 | 
			
		||||
 * For now it can be the classic switcher, or also implement a completer.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,7 +29,12 @@
 | 
			
		|||
#define ROFI_MODE_H
 | 
			
		||||
#include "rofi-types.h"
 | 
			
		||||
#include <cairo.h>
 | 
			
		||||
#include <gmodule.h>
 | 
			
		||||
G_BEGIN_DECLS
 | 
			
		||||
 | 
			
		||||
/** ABI version to check if loaded plugin is compatible. */
 | 
			
		||||
#define ABI_VERSION 7u
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @defgroup MODE Mode
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			@ -277,6 +282,28 @@ ModeMode mode_completer_result(Mode *sw, int menu_retv, char **input,
 | 
			
		|||
 * @returns TRUE if mode can be used as completer.
 | 
			
		||||
 */
 | 
			
		||||
gboolean mode_is_completer(const Mode *sw);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param mode The mode to query
 | 
			
		||||
 *
 | 
			
		||||
 * @returns the modes ABI version.
 | 
			
		||||
 */
 | 
			
		||||
int mode_get_abi_version(Mode *const mode);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param mode The mode to query
 | 
			
		||||
 * @param mod The GModule used to load the mode
 | 
			
		||||
 *
 | 
			
		||||
 * Set GModule used to load this plugin, this is used to
 | 
			
		||||
 * unload it on shutdown.
 | 
			
		||||
 */
 | 
			
		||||
void mode_plugin_set_module(Mode *mode, GModule *mod);
 | 
			
		||||
/**
 | 
			
		||||
 * @param mode The mode to query
 | 
			
		||||
 *
 | 
			
		||||
 * @returns the GModule used to load this plugin. NULL if not a plugin.
 | 
			
		||||
 */
 | 
			
		||||
GModule *mode_plugin_get_module(Mode *mode);
 | 
			
		||||
/**@}*/
 | 
			
		||||
G_END_DECLS
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,7 +40,8 @@ typedef enum {
 | 
			
		|||
  MM_REGEX = 1,
 | 
			
		||||
  MM_GLOB = 2,
 | 
			
		||||
  MM_FUZZY = 3,
 | 
			
		||||
  MM_PREFIX = 4
 | 
			
		||||
  MM_PREFIX = 4,
 | 
			
		||||
  MM_NUM_MATCHERS = 5
 | 
			
		||||
} MatchingMethod;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +70,18 @@ typedef struct {
 | 
			
		|||
  /** Custom command to generate preview icons */
 | 
			
		||||
  char *preview_cmd;
 | 
			
		||||
 | 
			
		||||
  /** Custom command to call when menu selection changes */
 | 
			
		||||
  char *on_selection_changed;
 | 
			
		||||
  /** Custom command to call when menu mode changes */
 | 
			
		||||
  char *on_mode_changed;
 | 
			
		||||
  /** Custom command to call when menu entry is accepted */
 | 
			
		||||
  char *on_entry_accepted;
 | 
			
		||||
  /** Custom command to call when menu is canceled */
 | 
			
		||||
  char *on_menu_canceled;
 | 
			
		||||
  /** Custom command to call when menu finds errors */
 | 
			
		||||
  char *on_menu_error;
 | 
			
		||||
  /** Custom command to call when menu screenshot is taken */
 | 
			
		||||
  char *on_screenshot_taken;
 | 
			
		||||
  /** Terminal to use  */
 | 
			
		||||
  char *terminal_emulator;
 | 
			
		||||
  /** SSH client to use */
 | 
			
		||||
| 
						 | 
				
			
			@ -123,6 +136,8 @@ typedef struct {
 | 
			
		|||
 | 
			
		||||
  /** Search case sensitivity */
 | 
			
		||||
  unsigned int case_sensitive;
 | 
			
		||||
  /** Smart case sensitivity like vim */
 | 
			
		||||
  unsigned int case_smart;
 | 
			
		||||
  /** Cycle through in the element list */
 | 
			
		||||
  unsigned int cycle;
 | 
			
		||||
  /** Height of an element in number of rows */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,6 +101,8 @@ struct RofiViewState {
 | 
			
		|||
  int skip_absorb;
 | 
			
		||||
  /** The selected line (in the unfiltered list) */
 | 
			
		||||
  unsigned int selected_line;
 | 
			
		||||
  /** The previously selected line (in the unfiltered list) */
 | 
			
		||||
  unsigned int previous_line;
 | 
			
		||||
  /** The return state of the view */
 | 
			
		||||
  MenuReturn retv;
 | 
			
		||||
  /** Monitor #workarea the view is displayed on */
 | 
			
		||||
| 
						 | 
				
			
			@ -159,12 +161,13 @@ struct RofiViewState {
 | 
			
		|||
 | 
			
		||||
  /** Regexs used for matching */
 | 
			
		||||
  rofi_int_matcher **tokens;
 | 
			
		||||
  /** For case-sensitivity */
 | 
			
		||||
  gboolean case_sensitive;
 | 
			
		||||
};
 | 
			
		||||
/** @} */
 | 
			
		||||
 | 
			
		||||
typedef struct _view_proxy {
 | 
			
		||||
  void (*update)(struct RofiViewState *state, gboolean qr);
 | 
			
		||||
  void (*maybe_update)(struct RofiViewState *state);
 | 
			
		||||
  void (*temp_configure_notify)(struct RofiViewState *state,
 | 
			
		||||
                                xcb_configure_notify_event_t *xce);
 | 
			
		||||
  void (*temp_click_to_exit)(struct RofiViewState *state, xcb_window_t target);
 | 
			
		||||
| 
						 | 
				
			
			@ -222,6 +225,8 @@ struct _rofi_view_cache_state {
 | 
			
		|||
  gboolean delayed_mode;
 | 
			
		||||
  /** timeout handling */
 | 
			
		||||
  guint user_timeout;
 | 
			
		||||
  /** timeout overlay */
 | 
			
		||||
  guint overlay_timeout;
 | 
			
		||||
  /** Entry box */
 | 
			
		||||
  gboolean entry_history_enable;
 | 
			
		||||
  /** Array with history entriy input. */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -287,6 +287,14 @@ void rofi_view_switch_mode(RofiViewState *state, Mode *mode);
 | 
			
		|||
 * Overlays text over the current view. Passing NULL for text hides the overlay.
 | 
			
		||||
 */
 | 
			
		||||
void rofi_view_set_overlay(RofiViewState *state, const char *text);
 | 
			
		||||
/**
 | 
			
		||||
 * @param state The handle to the view
 | 
			
		||||
 * @param text An UTF-8 encoded character array with the text to overlay.
 | 
			
		||||
 *
 | 
			
		||||
 * Overlays text over the current view. Passing NULL for text hides the overlay.
 | 
			
		||||
 * This message is automatically removed after X seconds.
 | 
			
		||||
 */
 | 
			
		||||
void rofi_view_set_overlay_timeout (RofiViewState *state, const char *text);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @param state The handle to the view.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -80,7 +80,9 @@ typedef struct {
 | 
			
		|||
  TBFontConfig *tbfc;
 | 
			
		||||
 | 
			
		||||
  PangoEllipsizeMode emode;
 | 
			
		||||
  //
 | 
			
		||||
  
 | 
			
		||||
  const char *password_mask_char;
 | 
			
		||||
 | 
			
		||||
  const char *theme_name;
 | 
			
		||||
} textbox;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,8 @@
 | 
			
		|||
int last_state = 0;
 | 
			
		||||
extern int rofi_is_in_dmenu_mode;
 | 
			
		||||
 | 
			
		||||
gboolean import_optional = FALSE;
 | 
			
		||||
 | 
			
		||||
const char *rasi_theme_file_extensions[] = {".rasi", ".rasinc", NULL};
 | 
			
		||||
/**
 | 
			
		||||
 * Type of Object to parse.
 | 
			
		||||
| 
						 | 
				
			
			@ -289,6 +291,7 @@ C_COMMENT_OPEN "/*"
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
INCLUDE    "@import"
 | 
			
		||||
OPT_INCLUDE    "?import"
 | 
			
		||||
THEME "@theme"
 | 
			
		||||
DEFAULT (?i:\"default\"?)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -378,6 +381,12 @@ if ( queue == NULL ) {
 | 
			
		|||
     */
 | 
			
		||||
<INITIAL>{INCLUDE} {
 | 
			
		||||
    g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
 | 
			
		||||
    import_optional = FALSE;
 | 
			
		||||
    BEGIN(INCLUDE);
 | 
			
		||||
}
 | 
			
		||||
<INITIAL>{OPT_INCLUDE} {
 | 
			
		||||
    g_queue_push_head ( queue, GINT_TO_POINTER (YY_START) );
 | 
			
		||||
    import_optional = TRUE;
 | 
			
		||||
    BEGIN(INCLUDE);
 | 
			
		||||
}
 | 
			
		||||
<INITIAL>{THEME} {
 | 
			
		||||
| 
						 | 
				
			
			@ -437,10 +446,15 @@ if ( queue == NULL ) {
 | 
			
		|||
        yylloc->first_column = yylloc->last_column = 1;
 | 
			
		||||
        yylloc->filename = current->filename;
 | 
			
		||||
    } else {
 | 
			
		||||
        char *str = g_markup_printf_escaped ( "Failed to open theme: <i>%s</i>\nError: <b>%s</b>",
 | 
			
		||||
                filename, strerror ( errno ) );
 | 
			
		||||
        rofi_add_warning_message ( g_string_new ( str ) );
 | 
			
		||||
        g_free ( str );
 | 
			
		||||
	if ( !import_optional ) {
 | 
			
		||||
		char *str = g_markup_printf_escaped ( "Failed to open theme: <i>%s</i>\nError: <b>%s</b>",
 | 
			
		||||
				filename, strerror ( errno ) );
 | 
			
		||||
		rofi_add_warning_message ( g_string_new ( str ) );
 | 
			
		||||
		g_free ( str );
 | 
			
		||||
	} else {
 | 
			
		||||
		g_warning("Trying to parse optional theme: '%s', Error: %s",
 | 
			
		||||
			filename, strerror(errno));
 | 
			
		||||
	}
 | 
			
		||||
        g_free(filename);
 | 
			
		||||
    }
 | 
			
		||||
    // Pop out of include. */
 | 
			
		||||
| 
						 | 
				
			
			@ -844,7 +858,7 @@ if ( queue == NULL ) {
 | 
			
		|||
 /**
 | 
			
		||||
  * Media defaults.
 | 
			
		||||
  */
 | 
			
		||||
<MEDIA,MEDIA_CONTENT,MEDIA_ENV_VAR,MEDIA_ENV_VAR_DEFAULT,MEDIA_ENV_VAR_CONTENT>{WHITESPACE}+	; // ignore all whitespace 
 | 
			
		||||
<MEDIA,MEDIA_CONTENT,MEDIA_ENV_VAR,MEDIA_ENV_VAR_DEFAULT,MEDIA_ENV_VAR_CONTENT>{WHITESPACE}+	; // ignore all whitespace
 | 
			
		||||
 | 
			
		||||
<MEDIA,MEDIA_CONTENT,MEDIA_ENV_VAR,MEDIA_ENV_VAR_DEFAULT,MEDIA_ENV_VAR_CONTENT>. {
 | 
			
		||||
    yytext[yyleng-1] = '\0';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
project('rofi', 'c',
 | 
			
		||||
    version: '1.7.8+wayland1',
 | 
			
		||||
    version: '1.7.8+wayland1-dev',
 | 
			
		||||
    meson_version: '>=0.59.0',
 | 
			
		||||
    license: [ 'MIT' ],
 | 
			
		||||
    default_options: [
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										72
									
								
								mkdocs/docs/themes/capture.sh
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										72
									
								
								mkdocs/docs/themes/capture.sh
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -3,55 +3,49 @@
 | 
			
		|||
THEMES=../../../themes/*.rasi
 | 
			
		||||
ROFI_BIN=../../../build/rofi
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function generate_options()
 | 
			
		||||
{
 | 
			
		||||
  echo -en "rofi\0icon\x1frofi\n"
 | 
			
		||||
  echo -en "help browser\0icon\x1fhelp-browser\n"
 | 
			
		||||
  echo -en "thunderbird\0icon\x1fthunderbird\n"
 | 
			
		||||
  echo -en "Urgent\0icon\x1femblem-urgent\n"
 | 
			
		||||
  echo -en "Active\0icon\x1fface-wink\n"
 | 
			
		||||
  echo -en "folder\0icon\x1ffolder\n"
 | 
			
		||||
  echo -en "Icon font 🐢 🥳\n"
 | 
			
		||||
  echo -en "Font icon\0icon\x1f<span size='x-large' color='red'>:-)</span>\n"
 | 
			
		||||
  echo -en "Quit\0icon\x1fapplication-exit\n"
 | 
			
		||||
function generate_options() {
 | 
			
		||||
	echo -en "rofi\0icon\x1frofi\n"
 | 
			
		||||
	echo -en "help browser\0icon\x1fhelp-browser\n"
 | 
			
		||||
	echo -en "thunderbird\0icon\x1fthunderbird\n"
 | 
			
		||||
	echo -en "Urgent\0icon\x1femblem-urgent\n"
 | 
			
		||||
	echo -en "Active\0icon\x1fface-wink\n"
 | 
			
		||||
	echo -en "folder\0icon\x1ffolder\n"
 | 
			
		||||
	echo -en "Icon font 🐢 🥳\n"
 | 
			
		||||
	echo -en "Font icon\0icon\x1f<span size='x-large' color='red'>:-)</span>\n"
 | 
			
		||||
	echo -en "Quit\0icon\x1fapplication-exit\n"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function run_theme
 | 
			
		||||
{
 | 
			
		||||
  theme=$1
 | 
			
		||||
  BASE=$(basename ${theme})
 | 
			
		||||
  NAME=${BASE%.rasi}
 | 
			
		||||
  export ROFI_PNG_OUTPUT="${NAME}.png" 
 | 
			
		||||
  if [ ${NAME} = "default" ]
 | 
			
		||||
  then
 | 
			
		||||
	  echo "# Default theme" >> themes.md
 | 
			
		||||
  else
 | 
			
		||||
	  echo "# [${NAME}](https://github.com/davatorium/rofi/blob/next/themes/${BASE})" >> themes.md
 | 
			
		||||
  fi
 | 
			
		||||
  echo "" >> themes.md
 | 
			
		||||
  generate_options | ${ROFI_BIN} -theme-str "@theme \"${theme}\"" \
 | 
			
		||||
    -no-config -dmenu -p "mode" -show-icons \
 | 
			
		||||
      -u 3 -a 4 -mesg "Message box for extra information" \
 | 
			
		||||
      -take-screenshot-quit 1500 
 | 
			
		||||
function run_theme {
 | 
			
		||||
	theme=$1
 | 
			
		||||
	BASE="$(basename ${theme})"
 | 
			
		||||
	NAME=${BASE%.rasi}
 | 
			
		||||
	export ROFI_PNG_OUTPUT="${NAME}.png"
 | 
			
		||||
	if [ "${NAME}" = "default" ]; then
 | 
			
		||||
		echo "# Default theme" >>themes.md
 | 
			
		||||
	else
 | 
			
		||||
		echo "# [${NAME}](https://github.com/davatorium/rofi/blob/next/themes/${BASE})" >>themes.md
 | 
			
		||||
	fi
 | 
			
		||||
	echo "" >>themes.md
 | 
			
		||||
	generate_options | ${ROFI_BIN} -theme-str "@theme \"${theme}\"" \
 | 
			
		||||
		-no-config -dmenu -p "mode" -show-icons \
 | 
			
		||||
		-u 3 -a 4 -mesg "Message box for extra information" \
 | 
			
		||||
		-take-screenshot-quit 1500
 | 
			
		||||
 | 
			
		||||
  echo "" >> themes.md
 | 
			
		||||
  echo "" >> themes.md
 | 
			
		||||
	echo "" >>themes.md
 | 
			
		||||
	echo "" >>themes.md
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
echo "# Included Themes" > themes.md
 | 
			
		||||
 | 
			
		||||
echo "Below is a list of themes shipped with rofi." >> themes.md
 | 
			
		||||
echo "Use \`rofi-theme-selector\` to select and use one of these themes." >> themes.md
 | 
			
		||||
echo "# Included Themes" >themes.md
 | 
			
		||||
 | 
			
		||||
echo "Below is a list of themes shipped with rofi." >>themes.md
 | 
			
		||||
echo "Use \`rofi-theme-selector\` to select and use one of these themes." >>themes.md
 | 
			
		||||
 | 
			
		||||
Xvfb :1234 -screen 0 1920x1080x24 &
 | 
			
		||||
XEPHYR_PID=$!
 | 
			
		||||
export DISPLAY=:1234
 | 
			
		||||
sleep 0.5;
 | 
			
		||||
sleep 0.5
 | 
			
		||||
run_theme "default"
 | 
			
		||||
for theme in ${THEMES}
 | 
			
		||||
do
 | 
			
		||||
  run_theme ${theme}
 | 
			
		||||
for theme in ${THEMES}; do
 | 
			
		||||
	run_theme "${theme}"
 | 
			
		||||
done
 | 
			
		||||
kill ${XEPHYR_PID}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										117
									
								
								source/helper.c
									
										
									
									
									
								
							
							
						
						
									
										117
									
								
								source/helper.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -54,6 +54,16 @@
 | 
			
		|||
#include <sys/types.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
const char *const MatchingMethodStr[MM_NUM_MATCHERS] = {
 | 
			
		||||
    "Normal", "Regex", "Glob", "Fuzzy", "Prefix"};
 | 
			
		||||
 | 
			
		||||
static int MatchingMethodEnabled[MM_NUM_MATCHERS] = {
 | 
			
		||||
    MM_NORMAL,
 | 
			
		||||
    -1,
 | 
			
		||||
};
 | 
			
		||||
static int NUMMatchingMethodEnabled = 1;
 | 
			
		||||
static int CurrentMatchingMethod = 0;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Textual description of positioning rofi.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -67,6 +77,23 @@ char **stored_argv = NULL;
 | 
			
		|||
 | 
			
		||||
char *helper_string_replace_if_exists_v(char *string, GHashTable *h);
 | 
			
		||||
 | 
			
		||||
const char *helper_get_matching_mode_str(void) {
 | 
			
		||||
  return MatchingMethodStr[config.matching_method];
 | 
			
		||||
}
 | 
			
		||||
void helper_select_next_matching_mode(void) {
 | 
			
		||||
 | 
			
		||||
  CurrentMatchingMethod++;
 | 
			
		||||
  CurrentMatchingMethod %= NUMMatchingMethodEnabled;
 | 
			
		||||
  config.matching_method = MatchingMethodEnabled[CurrentMatchingMethod];
 | 
			
		||||
}
 | 
			
		||||
void helper_select_previous_matching_mode(void) {
 | 
			
		||||
  CurrentMatchingMethod--;
 | 
			
		||||
  if (CurrentMatchingMethod < 0) {
 | 
			
		||||
    CurrentMatchingMethod = NUMMatchingMethodEnabled - 1;
 | 
			
		||||
  }
 | 
			
		||||
  config.matching_method = MatchingMethodEnabled[CurrentMatchingMethod];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void cmd_set_arguments(int argc, char **argv) {
 | 
			
		||||
  stored_argc = argc;
 | 
			
		||||
  stored_argv = argv;
 | 
			
		||||
| 
						 | 
				
			
			@ -582,7 +609,7 @@ int create_pid_file(const char *pidfile, gboolean kill_running) {
 | 
			
		|||
      char buffer[64] = {
 | 
			
		||||
          0,
 | 
			
		||||
      };
 | 
			
		||||
      ssize_t l = read(fd, &buffer, 63);
 | 
			
		||||
      ssize_t l = read(fd, &(buffer[0]), 63);
 | 
			
		||||
      if (l > 1) {
 | 
			
		||||
        buffer[l] = 0;
 | 
			
		||||
        pid_t pid = g_ascii_strtoll(buffer, NULL, 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -664,24 +691,40 @@ int config_sanity_check(void) {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  if (config.matching) {
 | 
			
		||||
    if (g_strcmp0(config.matching, "regex") == 0) {
 | 
			
		||||
      config.matching_method = MM_REGEX;
 | 
			
		||||
    } else if (g_strcmp0(config.matching, "glob") == 0) {
 | 
			
		||||
      config.matching_method = MM_GLOB;
 | 
			
		||||
    } else if (g_strcmp0(config.matching, "fuzzy") == 0) {
 | 
			
		||||
      config.matching_method = MM_FUZZY;
 | 
			
		||||
    } else if (g_strcmp0(config.matching, "normal") == 0) {
 | 
			
		||||
      config.matching_method = MM_NORMAL;
 | 
			
		||||
      ;
 | 
			
		||||
    } else if (g_strcmp0(config.matching, "prefix") == 0) {
 | 
			
		||||
      config.matching_method = MM_PREFIX;
 | 
			
		||||
    } else {
 | 
			
		||||
      g_string_append_printf(msg,
 | 
			
		||||
                             "\t<b>config.matching</b>=%s is not a valid "
 | 
			
		||||
                             "matching strategy.\nValid options are: glob, "
 | 
			
		||||
                             "regex, fuzzy, prefix or normal.\n",
 | 
			
		||||
                             config.matching);
 | 
			
		||||
      found_error = 1;
 | 
			
		||||
    char **strv = g_strsplit(config.matching, ",", 0);
 | 
			
		||||
    if (strv) {
 | 
			
		||||
      int matching_method_index = 0;
 | 
			
		||||
      for (char **str = strv; *str && matching_method_index < MM_NUM_MATCHERS;
 | 
			
		||||
           str++) {
 | 
			
		||||
        gboolean found = FALSE;
 | 
			
		||||
        for (unsigned i = 0;
 | 
			
		||||
             i < MM_NUM_MATCHERS && matching_method_index < MM_NUM_MATCHERS;
 | 
			
		||||
             i++) {
 | 
			
		||||
          if (g_ascii_strcasecmp(*str, MatchingMethodStr[i]) == 0) {
 | 
			
		||||
            MatchingMethodEnabled[matching_method_index] = i;
 | 
			
		||||
            matching_method_index++;
 | 
			
		||||
            NUMMatchingMethodEnabled = matching_method_index;
 | 
			
		||||
            if (matching_method_index == MM_NUM_MATCHERS) {
 | 
			
		||||
              found_error = 1;
 | 
			
		||||
              g_string_append_printf(msg,
 | 
			
		||||
                                     "\t<b>config.matching</b> = %s to many "
 | 
			
		||||
                                     "matching options enabled.\n",
 | 
			
		||||
                                     config.matching);
 | 
			
		||||
            }
 | 
			
		||||
            found = TRUE;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (!found) {
 | 
			
		||||
          g_string_append_printf(msg,
 | 
			
		||||
                                 "\t<b>config.matching</b>=%s is not a valid "
 | 
			
		||||
                                 "matching strategy.\nValid options are: glob, "
 | 
			
		||||
                                 "regex, fuzzy, prefix or normal.\n",
 | 
			
		||||
                                 *str);
 | 
			
		||||
          found_error = 1;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      config.matching_method = MatchingMethodEnabled[0];
 | 
			
		||||
      g_strfreev(strv);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -769,7 +812,8 @@ char *rofi_expand_path(const char *input) {
 | 
			
		|||
  ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))
 | 
			
		||||
 | 
			
		||||
unsigned int levenshtein(const char *needle, const glong needlelen,
 | 
			
		||||
                         const char *haystack, const glong haystacklen) {
 | 
			
		||||
                         const char *haystack, const glong haystacklen,
 | 
			
		||||
                         int case_sensitive) {
 | 
			
		||||
  if (needlelen == G_MAXLONG) {
 | 
			
		||||
    // String to long, we cannot handle this.
 | 
			
		||||
    return UINT_MAX;
 | 
			
		||||
| 
						 | 
				
			
			@ -785,12 +829,12 @@ unsigned int levenshtein(const char *needle, const glong needlelen,
 | 
			
		|||
    const char *needles = needle;
 | 
			
		||||
    column[0] = x;
 | 
			
		||||
    gunichar haystackc = g_utf8_get_char(haystack);
 | 
			
		||||
    if (!config.case_sensitive) {
 | 
			
		||||
    if (!case_sensitive) {
 | 
			
		||||
      haystackc = g_unichar_tolower(haystackc);
 | 
			
		||||
    }
 | 
			
		||||
    for (glong y = 1, lastdiag = x - 1; y <= needlelen; y++) {
 | 
			
		||||
      gunichar needlec = g_utf8_get_char(needles);
 | 
			
		||||
      if (!config.case_sensitive) {
 | 
			
		||||
      if (!case_sensitive) {
 | 
			
		||||
        needlec = g_unichar_tolower(needlec);
 | 
			
		||||
      }
 | 
			
		||||
      unsigned int olddiag = column[y];
 | 
			
		||||
| 
						 | 
				
			
			@ -917,7 +961,7 @@ static int rofi_scorer_get_score_for(enum CharClass prev, enum CharClass curr) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str,
 | 
			
		||||
                               glong slen) {
 | 
			
		||||
                               glong slen, int case_sensitive) {
 | 
			
		||||
  if (slen > FUZZY_SCORER_MAX_LENGTH) {
 | 
			
		||||
    return -MIN_SCORE;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -952,9 +996,8 @@ int rofi_scorer_fuzzy_evaluate(const char *pattern, glong plen, const char *str,
 | 
			
		|||
      left = dp[si];
 | 
			
		||||
      lefts = MAX(lefts + GAP_SCORE, left);
 | 
			
		||||
      sc = g_utf8_get_char(sit);
 | 
			
		||||
      if (config.case_sensitive
 | 
			
		||||
              ? pc == sc
 | 
			
		||||
              : g_unichar_tolower(pc) == g_unichar_tolower(sc)) {
 | 
			
		||||
      if (case_sensitive ? pc == sc
 | 
			
		||||
                         : g_unichar_tolower(pc) == g_unichar_tolower(sc)) {
 | 
			
		||||
        int t = score[si] * (pstart ? PATTERN_START_MULTIPLIER
 | 
			
		||||
                                    : PATTERN_NON_START_MULTIPLIER);
 | 
			
		||||
        dp[si] = pfirst ? LEADING_GAP_SCORE * si + t
 | 
			
		||||
| 
						 | 
				
			
			@ -1248,6 +1291,28 @@ void parse_ranges(char *input, rofi_range_pair **list, unsigned int *length) {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int parse_case_sensitivity(const char *input) {
 | 
			
		||||
  int case_sensitive = config.case_sensitive;
 | 
			
		||||
  if (config.case_smart) {
 | 
			
		||||
    // By default case is false, unless the search query has a
 | 
			
		||||
    // uppercase in it?
 | 
			
		||||
    case_sensitive = FALSE;
 | 
			
		||||
    const char *end;
 | 
			
		||||
    if (g_utf8_validate(input, -1, &end)) {
 | 
			
		||||
      for (const char *c = (input); !case_sensitive && c != NULL && *c;
 | 
			
		||||
           c = g_utf8_next_char(c)) {
 | 
			
		||||
        gunichar uc = g_utf8_get_char(c);
 | 
			
		||||
        if (g_unichar_isupper(uc)) {
 | 
			
		||||
          case_sensitive = TRUE;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return case_sensitive;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rofi_output_formatted_line(const char *format, const char *string,
 | 
			
		||||
                                int selected_line, const char *filter) {
 | 
			
		||||
  for (int i = 0; format && format[i]; i++) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -332,6 +332,14 @@ ActionBindingEntry rofi_bindings[] = {
 | 
			
		|||
     .name = "kb-entry-history-down",
 | 
			
		||||
     .binding = "Control+Down",
 | 
			
		||||
     .comment = "Go down in the history of the entry box"},
 | 
			
		||||
    {.id = MATCHER_UP,
 | 
			
		||||
     .name = "kb-matcher-up",
 | 
			
		||||
     .binding = "Super+equal",
 | 
			
		||||
     .comment = "Switch to the previous matcher"},
 | 
			
		||||
    {.id = MATCHER_DOWN,
 | 
			
		||||
     .name = "kb-mather-down",
 | 
			
		||||
     .binding = "Super+minus",
 | 
			
		||||
     .comment = "Switch to the next matcher"},
 | 
			
		||||
 | 
			
		||||
    /* Mouse-aware bindings */
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -159,6 +159,11 @@ const char *mode_get_name(const Mode *mode) {
 | 
			
		|||
  return mode->name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int mode_get_abi_version(Mode *const mode) {
 | 
			
		||||
  g_assert(mode != NULL);
 | 
			
		||||
  return mode->abi_version;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mode_free(Mode **mode) {
 | 
			
		||||
  g_assert(mode != NULL);
 | 
			
		||||
  g_assert((*mode) != NULL);
 | 
			
		||||
| 
						 | 
				
			
			@ -245,4 +250,10 @@ gboolean mode_is_completer(const Mode *mode) {
 | 
			
		|||
  return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void mode_plugin_set_module(Mode *mode, GModule *mod){
 | 
			
		||||
  mode->module = mod;
 | 
			
		||||
}
 | 
			
		||||
GModule *mode_plugin_get_module(Mode *mode){
 | 
			
		||||
  return mode->module;
 | 
			
		||||
}
 | 
			
		||||
/**@}*/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -289,8 +289,8 @@ static gpointer read_input_thread(gpointer userdata) {
 | 
			
		|||
      if (FD_ISSET(fd, &rfds)) {
 | 
			
		||||
        ssize_t readbytes = 0;
 | 
			
		||||
        if ((nread + 1024) > len) {
 | 
			
		||||
          line = g_realloc(line, (nread + 1024));
 | 
			
		||||
          len = nread + 1024;
 | 
			
		||||
          line = g_realloc(line, (len + 2048));
 | 
			
		||||
          len = len + 2048;
 | 
			
		||||
        }
 | 
			
		||||
        readbytes = read(fd, &line[nread], 1023);
 | 
			
		||||
        if (readbytes > 0) {
 | 
			
		||||
| 
						 | 
				
			
			@ -959,7 +959,8 @@ int dmenu_mode_dialog(void) {
 | 
			
		|||
  char *select = NULL;
 | 
			
		||||
  find_arg_str("-select", &select);
 | 
			
		||||
  if (select != NULL) {
 | 
			
		||||
    rofi_int_matcher **tokens = helper_tokenize(select, config.case_sensitive);
 | 
			
		||||
    rofi_int_matcher **tokens =
 | 
			
		||||
        helper_tokenize(select, parse_case_sensitivity(select));
 | 
			
		||||
    unsigned int i = 0;
 | 
			
		||||
    for (i = 0; i < cmd_list_length; i++) {
 | 
			
		||||
      if (helper_token_match(tokens, cmd_list[i].entry)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -970,8 +971,9 @@ int dmenu_mode_dialog(void) {
 | 
			
		|||
    helper_tokenize_free(tokens);
 | 
			
		||||
  }
 | 
			
		||||
  if (find_arg("-dump") >= 0) {
 | 
			
		||||
    rofi_int_matcher **tokens = helper_tokenize(
 | 
			
		||||
        config.filter ? config.filter : "", config.case_sensitive);
 | 
			
		||||
    char *filter = config.filter ? config.filter : "";
 | 
			
		||||
    rofi_int_matcher **tokens =
 | 
			
		||||
        helper_tokenize(filter, parse_case_sensitivity(filter));
 | 
			
		||||
    unsigned int i = 0;
 | 
			
		||||
    for (i = 0; i < cmd_list_length; i++) {
 | 
			
		||||
      if (tokens == NULL || helper_token_match(tokens, cmd_list[i].entry)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -353,7 +353,8 @@ static gboolean recursive_browser_async_read_proc(gint fd,
 | 
			
		|||
      }
 | 
			
		||||
    } else if (command == 'q') {
 | 
			
		||||
      if (pd->loading) {
 | 
			
		||||
        rofi_view_set_overlay(rofi_view_get_active(), NULL);
 | 
			
		||||
	// TODO: add enable.
 | 
			
		||||
        //rofi_view_set_overlay(rofi_view_get_active(), NULL);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -350,6 +350,10 @@ static ModeMode script_mode_result(Mode *sw, int mretv, char **input,
 | 
			
		|||
      retv = (mretv & MENU_LOWER_MASK);
 | 
			
		||||
      return retv;
 | 
			
		||||
    }
 | 
			
		||||
  } else if ((mretv & MENU_ENTRY_DELETE) && selected_line != UINT32_MAX) {
 | 
			
		||||
    script_mode_reset_highlight(sw);
 | 
			
		||||
    new_list = execute_executor(sw, rmpd->cmd_list[selected_line].entry, &new_length,
 | 
			
		||||
                                3, &(rmpd->cmd_list[selected_line]));
 | 
			
		||||
  } else if ((mretv & MENU_OK) && rmpd->cmd_list[selected_line].entry != NULL) {
 | 
			
		||||
    if (rmpd->cmd_list[selected_line].nonselectable) {
 | 
			
		||||
      return RELOAD_DIALOG;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,10 +71,6 @@
 | 
			
		|||
 | 
			
		||||
#include "timings.h"
 | 
			
		||||
 | 
			
		||||
// Plugin abi version.
 | 
			
		||||
// TODO: move this check to mode.c
 | 
			
		||||
#include "mode-private.h"
 | 
			
		||||
 | 
			
		||||
/** Location of pidfile for this instance. */
 | 
			
		||||
char *pidfile = NULL;
 | 
			
		||||
/** Location of Cache directory. */
 | 
			
		||||
| 
						 | 
				
			
			@ -202,7 +198,7 @@ static void run_mode_index(ModeMode mode) {
 | 
			
		|||
  for (unsigned int i = 0; i < num_modes; i++) {
 | 
			
		||||
    if (!mode_init(modes[i])) {
 | 
			
		||||
      GString *str = g_string_new("Failed to initialize the mode: ");
 | 
			
		||||
      g_string_append(str, modes[i]->name);
 | 
			
		||||
      g_string_append(str, mode_get_name(modes[i]));
 | 
			
		||||
      g_string_append(str, "\n");
 | 
			
		||||
 | 
			
		||||
      rofi_view_error_dialog(str->str, ERROR_MSG_MARKUP);
 | 
			
		||||
| 
						 | 
				
			
			@ -303,7 +299,7 @@ static void print_list_of_modes(int is_term) {
 | 
			
		|||
    }
 | 
			
		||||
    printf("        • %s%s%s%s\n", active ? "+" : "",
 | 
			
		||||
           is_term ? (active ? color_green : color_red) : "",
 | 
			
		||||
           available_modes[i]->name, is_term ? color_reset : "");
 | 
			
		||||
           mode_get_name(available_modes[i]), is_term ? color_reset : "");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
static void print_main_application_options(int is_term) {
 | 
			
		||||
| 
						 | 
				
			
			@ -475,7 +471,7 @@ static void help_print_mode_not_found(const char *mode) {
 | 
			
		|||
      }
 | 
			
		||||
    }
 | 
			
		||||
    g_string_append_printf(str, "        * %s%s\n", active ? "+" : "",
 | 
			
		||||
                           available_modes[i]->name);
 | 
			
		||||
                           mode_get_name(available_modes[i]));
 | 
			
		||||
  }
 | 
			
		||||
  rofi_add_error_message(str);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -489,7 +485,7 @@ static void help_print_no_arguments(void) {
 | 
			
		|||
  g_string_append(emesg, "The following modes are enabled:\n");
 | 
			
		||||
  for (unsigned int j = 0; j < num_modes; j++) {
 | 
			
		||||
    g_string_append_printf(emesg, "    • <span color=\"green\">%s</span>\n",
 | 
			
		||||
                           modes[j]->name);
 | 
			
		||||
                           mode_get_name(modes[j]));
 | 
			
		||||
  }
 | 
			
		||||
  g_string_append(emesg, "\nThe following modes can be enabled:\n");
 | 
			
		||||
  for (unsigned int i = 0; i < num_available_modes; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -502,7 +498,7 @@ static void help_print_no_arguments(void) {
 | 
			
		|||
    }
 | 
			
		||||
    if (!active) {
 | 
			
		||||
      g_string_append_printf(emesg, "    • <span color=\"red\">%s</span>\n",
 | 
			
		||||
                             available_modes[i]->name);
 | 
			
		||||
                             mode_get_name(available_modes[i]));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  g_string_append(emesg, "\nTo activate a mode, add it to the list in "
 | 
			
		||||
| 
						 | 
				
			
			@ -565,7 +561,7 @@ static void cleanup(void) {
 | 
			
		|||
 | 
			
		||||
Mode *rofi_collect_modes_search(const char *name) {
 | 
			
		||||
  for (unsigned int i = 0; i < num_available_modes; i++) {
 | 
			
		||||
    if (g_strcmp0(name, available_modes[i]->name) == 0) {
 | 
			
		||||
    if (g_strcmp0(name, mode_get_name(available_modes[i])) == 0) {
 | 
			
		||||
      return available_modes[i];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -577,7 +573,7 @@ Mode *rofi_collect_modes_search(const char *name) {
 | 
			
		|||
 * @returns TRUE when success.
 | 
			
		||||
 */
 | 
			
		||||
static gboolean rofi_collectmodes_add(Mode *mode) {
 | 
			
		||||
  Mode *m = rofi_collect_modes_search(mode->name);
 | 
			
		||||
  Mode *m = rofi_collect_modes_search(mode_get_name(mode));
 | 
			
		||||
  if (m == NULL) {
 | 
			
		||||
    available_modes =
 | 
			
		||||
        g_realloc(available_modes, sizeof(Mode *) * (num_available_modes + 1));
 | 
			
		||||
| 
						 | 
				
			
			@ -605,13 +601,13 @@ static void rofi_collectmodes_dir(const char *base_dir) {
 | 
			
		|||
      if (mod) {
 | 
			
		||||
        Mode *m = NULL;
 | 
			
		||||
        if (g_module_symbol(mod, "mode", (gpointer *)&m)) {
 | 
			
		||||
          if (m->abi_version != ABI_VERSION) {
 | 
			
		||||
          if (mode_get_abi_version(m) != ABI_VERSION) {
 | 
			
		||||
            g_warning("ABI version of plugin: '%s' does not match: %08X "
 | 
			
		||||
                      "expecting: %08X",
 | 
			
		||||
                      dn, m->abi_version, ABI_VERSION);
 | 
			
		||||
                      dn, mode_get_abi_version(m), ABI_VERSION);
 | 
			
		||||
            g_module_close(mod);
 | 
			
		||||
          } else {
 | 
			
		||||
            m->module = mod;
 | 
			
		||||
            mode_plugin_set_module(m, mod);
 | 
			
		||||
            if (!rofi_collectmodes_add(m)) {
 | 
			
		||||
              g_module_close(mod);
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -684,8 +680,8 @@ static void rofi_collectmodes_setup(void) {
 | 
			
		|||
}
 | 
			
		||||
static void rofi_collectmodes_destroy(void) {
 | 
			
		||||
  for (unsigned int i = 0; i < num_available_modes; i++) {
 | 
			
		||||
    if (available_modes[i]->module) {
 | 
			
		||||
      GModule *mod = available_modes[i]->module;
 | 
			
		||||
    if (mode_plugin_get_module(available_modes[i])) {
 | 
			
		||||
      GModule *mod = mode_plugin_get_module(available_modes[i]);
 | 
			
		||||
      available_modes[i] = NULL;
 | 
			
		||||
      g_module_close(mod);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1107,7 +1103,9 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
      extern const char *rasi_theme_file_extensions[];
 | 
			
		||||
      char *file2 =
 | 
			
		||||
          helper_get_theme_path(config_path, rasi_theme_file_extensions, NULL);
 | 
			
		||||
      char *filename = rofi_theme_parse_prepare_file(file2);
 | 
			
		||||
      GFile *gf = g_file_new_for_path(file2);
 | 
			
		||||
      char *filename = g_file_get_path(gf);
 | 
			
		||||
      g_object_unref(gf);
 | 
			
		||||
      g_free(file2);
 | 
			
		||||
      if (filename && g_file_test(filename, G_FILE_TEST_EXISTS)) {
 | 
			
		||||
        if (rofi_theme_parse_file(filename)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										176
									
								
								source/view.c
									
										
									
									
									
								
							
							
						
						
									
										176
									
								
								source/view.c
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -81,14 +81,15 @@ struct _rofi_view_cache_state CacheState = {
 | 
			
		|||
    .max_refilter_time = 0.0,
 | 
			
		||||
    .delayed_mode = FALSE,
 | 
			
		||||
    .user_timeout = 0,
 | 
			
		||||
    .overlay_timeout = 0,
 | 
			
		||||
    .entry_history_enable = TRUE,
 | 
			
		||||
    .entry_history = NULL,
 | 
			
		||||
    .entry_history_length = 0,
 | 
			
		||||
    .entry_history_index = 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static char *get_matching_state(void) {
 | 
			
		||||
  if (config.case_sensitive) {
 | 
			
		||||
static char *get_matching_state(RofiViewState* state) {
 | 
			
		||||
  if (state->case_sensitive) {
 | 
			
		||||
    if (config.sort) {
 | 
			
		||||
      return "±";
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,6 +114,18 @@ static int lev_sort(const void *p1, const void *p2, void *arg) {
 | 
			
		|||
  return distances[*a] - distances[*b];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void screenshot_taken_user_callback(const char *path) {
 | 
			
		||||
  if (config.on_screenshot_taken == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  char **args = NULL;
 | 
			
		||||
  int argv = 0;
 | 
			
		||||
  helper_parse_setup(config.on_screenshot_taken, &args, &argv, "{path}", path,
 | 
			
		||||
                     (char *)0);
 | 
			
		||||
  if (args != NULL)
 | 
			
		||||
    helper_execute(NULL, args, "", config.on_screenshot_taken, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Stores a screenshot of Rofi at that point in time.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -172,6 +185,7 @@ void rofi_capture_screenshot(void) {
 | 
			
		|||
        g_warning("Failed to produce screenshot '%s', got error: '%s'", fpath,
 | 
			
		||||
                  cairo_status_to_string(status));
 | 
			
		||||
      }
 | 
			
		||||
      screenshot_taken_user_callback(fpath);
 | 
			
		||||
    }
 | 
			
		||||
    cairo_destroy(draw);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -444,12 +458,13 @@ static void filter_elements(thread_state *ts,
 | 
			
		|||
        glong slen = g_utf8_strlen(str, -1);
 | 
			
		||||
        switch (config.sorting_method_enum) {
 | 
			
		||||
        case SORT_FZF:
 | 
			
		||||
          t->state->distance[i] =
 | 
			
		||||
              rofi_scorer_fuzzy_evaluate(t->pattern, t->plen, str, slen);
 | 
			
		||||
          t->state->distance[i] = rofi_scorer_fuzzy_evaluate(
 | 
			
		||||
              t->pattern, t->plen, str, slen, t->state->case_sensitive);
 | 
			
		||||
          break;
 | 
			
		||||
        case SORT_NORMAL:
 | 
			
		||||
        default:
 | 
			
		||||
          t->state->distance[i] = levenshtein(t->pattern, t->plen, str, slen);
 | 
			
		||||
          t->state->distance[i] = levenshtein(t->pattern, t->plen, str, slen,
 | 
			
		||||
                                              t->state->case_sensitive);
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        g_free(str);
 | 
			
		||||
| 
						 | 
				
			
			@ -620,9 +635,31 @@ inline static void rofi_view_nav_last(RofiViewState *state) {
 | 
			
		|||
  // state->selected = state->filtered_lines - 1;
 | 
			
		||||
  listview_set_selected(state->list_view, -1);
 | 
			
		||||
}
 | 
			
		||||
static void selection_changed_user_callback(unsigned int index,
 | 
			
		||||
                                            RofiViewState *state) {
 | 
			
		||||
  if (config.on_selection_changed == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  int fstate = 0;
 | 
			
		||||
  char *text = mode_get_display_value(state->sw, state->line_map[index],
 | 
			
		||||
                                      &fstate, NULL, TRUE);
 | 
			
		||||
  char **args = NULL;
 | 
			
		||||
  int argv = 0;
 | 
			
		||||
  helper_parse_setup(config.on_selection_changed, &args, &argv, "{entry}", text,
 | 
			
		||||
                     (char *)0);
 | 
			
		||||
  if (args != NULL)
 | 
			
		||||
    helper_execute(NULL, args, "", config.on_selection_changed, NULL);
 | 
			
		||||
  g_free(text);
 | 
			
		||||
}
 | 
			
		||||
static void selection_changed_callback(G_GNUC_UNUSED listview *lv,
 | 
			
		||||
                                       unsigned int index, void *udata) {
 | 
			
		||||
  RofiViewState *state = (RofiViewState *)udata;
 | 
			
		||||
  if (index < state->filtered_lines) {
 | 
			
		||||
    if (state->previous_line != state->line_map[index]) {
 | 
			
		||||
      selection_changed_user_callback(index, state);
 | 
			
		||||
      state->previous_line = state->line_map[index];
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (state->tb_current_entry) {
 | 
			
		||||
    if (index < state->filtered_lines) {
 | 
			
		||||
      int fstate = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -630,7 +667,6 @@ static void selection_changed_callback(G_GNUC_UNUSED listview *lv,
 | 
			
		|||
                                          &fstate, NULL, TRUE);
 | 
			
		||||
      textbox_text(state->tb_current_entry, text);
 | 
			
		||||
      g_free(text);
 | 
			
		||||
 | 
			
		||||
    } else {
 | 
			
		||||
      textbox_text(state->tb_current_entry, "");
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -742,7 +778,12 @@ static gboolean rofi_view_refilter_real(RofiViewState *state) {
 | 
			
		|||
    unsigned int j = 0;
 | 
			
		||||
    gchar *pattern = mode_preprocess_input(state->sw, state->text->text);
 | 
			
		||||
    glong plen = pattern ? g_utf8_strlen(pattern, -1) : 0;
 | 
			
		||||
    state->tokens = helper_tokenize(pattern, config.case_sensitive);
 | 
			
		||||
    state->case_sensitive = parse_case_sensitivity(state->text->text);
 | 
			
		||||
    state->tokens = helper_tokenize(pattern, state->case_sensitive);
 | 
			
		||||
 | 
			
		||||
    if (config.case_smart && state->case_indicator) {
 | 
			
		||||
      textbox_text(state->case_indicator, get_matching_state(state));
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * On long lists it can be beneficial to parallelize.
 | 
			
		||||
     * If number of threads is 1, no thread is spawn.
 | 
			
		||||
| 
						 | 
				
			
			@ -1008,7 +1049,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
 | 
			
		|||
    if (state->case_indicator != NULL) {
 | 
			
		||||
      config.sort = !config.sort;
 | 
			
		||||
      state->refilter = TRUE;
 | 
			
		||||
      textbox_text(state->case_indicator, get_matching_state());
 | 
			
		||||
      textbox_text(state->case_indicator, get_matching_state(state));
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  case MODE_PREVIOUS:
 | 
			
		||||
| 
						 | 
				
			
			@ -1038,7 +1079,7 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
 | 
			
		|||
      config.case_sensitive = !config.case_sensitive;
 | 
			
		||||
      (state->selected_line) = 0;
 | 
			
		||||
      state->refilter = TRUE;
 | 
			
		||||
      textbox_text(state->case_indicator, get_matching_state());
 | 
			
		||||
      textbox_text(state->case_indicator, get_matching_state(state));
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  // Special delete entry command.
 | 
			
		||||
| 
						 | 
				
			
			@ -1214,7 +1255,6 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
 | 
			
		|||
      // Nothing entered and nothing selected.
 | 
			
		||||
      state->retv = MENU_CUSTOM_INPUT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    state->quit = TRUE;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -1273,6 +1313,16 @@ static void rofi_view_trigger_global_action(KeyBindingAction action) {
 | 
			
		|||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
  case MATCHER_UP:
 | 
			
		||||
    helper_select_next_matching_mode();
 | 
			
		||||
    rofi_view_refilter(state);
 | 
			
		||||
    rofi_view_set_overlay_timeout(state, helper_get_matching_mode_str());
 | 
			
		||||
    break;
 | 
			
		||||
  case MATCHER_DOWN:
 | 
			
		||||
    helper_select_previous_matching_mode();
 | 
			
		||||
    rofi_view_refilter(state);
 | 
			
		||||
    rofi_view_set_overlay_timeout(state, helper_get_matching_mode_str());
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1410,6 +1460,62 @@ void rofi_view_handle_mouse_motion(RofiViewState *state, gint x, gint y,
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void rofi_quit_user_callback(RofiViewState *state) {
 | 
			
		||||
  if (state->retv & MENU_OK) {
 | 
			
		||||
    if (config.on_entry_accepted == NULL)
 | 
			
		||||
      return;
 | 
			
		||||
    int fstate = 0;
 | 
			
		||||
    unsigned int selected = listview_get_selected(state->list_view);
 | 
			
		||||
    // TODO: handle custom text
 | 
			
		||||
    if (selected >= state->filtered_lines)
 | 
			
		||||
      return;
 | 
			
		||||
    // Pass selected text to custom command
 | 
			
		||||
    char *text = mode_get_display_value(state->sw, state->line_map[selected],
 | 
			
		||||
                                        &fstate, NULL, TRUE);
 | 
			
		||||
    char **args = NULL;
 | 
			
		||||
    int argv = 0;
 | 
			
		||||
    helper_parse_setup(config.on_entry_accepted, &args, &argv, "{entry}", text,
 | 
			
		||||
                       (char *)0);
 | 
			
		||||
    if (args != NULL)
 | 
			
		||||
      helper_execute(NULL, args, "", config.on_entry_accepted, NULL);
 | 
			
		||||
    g_free(text);
 | 
			
		||||
  } else if (state->retv & MENU_CANCEL) {
 | 
			
		||||
    if (config.on_menu_canceled == NULL)
 | 
			
		||||
      return;
 | 
			
		||||
    helper_execute_command(NULL, config.on_menu_canceled, FALSE, NULL);
 | 
			
		||||
  } else if (state->retv & MENU_NEXT || state->retv & MENU_PREVIOUS ||
 | 
			
		||||
             state->retv & MENU_QUICK_SWITCH || state->retv & MENU_COMPLETE) {
 | 
			
		||||
    if (config.on_mode_changed == NULL)
 | 
			
		||||
      return;
 | 
			
		||||
    // TODO: pass mode name to custom command
 | 
			
		||||
    helper_execute_command(NULL, config.on_mode_changed, FALSE, NULL);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rofi_view_maybe_update(RofiViewState *state) {
 | 
			
		||||
  if (rofi_view_get_completed(state)) {
 | 
			
		||||
    // Exec custom user commands
 | 
			
		||||
    rofi_quit_user_callback(state);
 | 
			
		||||
    // This menu is done.
 | 
			
		||||
    rofi_view_finalize(state);
 | 
			
		||||
    // If there a state. (for example error) reload it.
 | 
			
		||||
    state = rofi_view_get_active();
 | 
			
		||||
 | 
			
		||||
    // cleanup, if no more state to display.
 | 
			
		||||
    if (state == NULL) {
 | 
			
		||||
      // Quit main-loop.
 | 
			
		||||
      rofi_quit_main_loop();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Update if requested.
 | 
			
		||||
  if (state->refilter) {
 | 
			
		||||
    rofi_view_refilter(state);
 | 
			
		||||
  }
 | 
			
		||||
  rofi_view_update(state, TRUE);
 | 
			
		||||
  return;
 | 
			
		||||
}
 | 
			
		||||
WidgetTriggerActionResult textbox_button_trigger_action(
 | 
			
		||||
    widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x,
 | 
			
		||||
    G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1558,7 +1664,7 @@ static void rofi_view_add_widget(RofiViewState *state, widget *parent_widget,
 | 
			
		|||
                       TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "*", 0, 0);
 | 
			
		||||
    // Add small separator between case indicator and text box.
 | 
			
		||||
    box_add((box *)parent_widget, WIDGET(state->case_indicator), FALSE);
 | 
			
		||||
    textbox_text(state->case_indicator, get_matching_state());
 | 
			
		||||
    textbox_text(state->case_indicator, get_matching_state(state));
 | 
			
		||||
  }
 | 
			
		||||
  /**
 | 
			
		||||
   * ENTRY BOX
 | 
			
		||||
| 
						 | 
				
			
			@ -1694,6 +1800,7 @@ RofiViewState *rofi_view_create(Mode *sw, const char *input,
 | 
			
		|||
  state->menu_flags = menu_flags;
 | 
			
		||||
  state->sw = sw;
 | 
			
		||||
  state->selected_line = UINT32_MAX;
 | 
			
		||||
  state->previous_line = UINT32_MAX;
 | 
			
		||||
  state->retv = MENU_CANCEL;
 | 
			
		||||
  state->distance = NULL;
 | 
			
		||||
  state->quit = FALSE;
 | 
			
		||||
| 
						 | 
				
			
			@ -1793,6 +1900,18 @@ RofiViewState *rofi_view_create(Mode *sw, const char *input,
 | 
			
		|||
  return state;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void rofi_error_user_callback(const char *msg) {
 | 
			
		||||
  if (config.on_menu_error == NULL)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  char **args = NULL;
 | 
			
		||||
  int argv = 0;
 | 
			
		||||
  helper_parse_setup(config.on_menu_error, &args, &argv, "{error}", msg,
 | 
			
		||||
                     (char *)0);
 | 
			
		||||
  if (args != NULL)
 | 
			
		||||
    helper_execute(NULL, args, "", config.on_menu_error, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int rofi_view_error_dialog(const char *msg, int markup) {
 | 
			
		||||
  RofiViewState *state = __rofi_view_state_create();
 | 
			
		||||
  state->retv = MENU_CANCEL;
 | 
			
		||||
| 
						 | 
				
			
			@ -1836,6 +1955,9 @@ int rofi_view_error_dialog(const char *msg, int markup) {
 | 
			
		|||
  }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  // Exec custom command
 | 
			
		||||
  rofi_error_user_callback(msg);
 | 
			
		||||
 | 
			
		||||
  // Set it as current window.
 | 
			
		||||
  rofi_view_set_active(state);
 | 
			
		||||
  return TRUE;
 | 
			
		||||
| 
						 | 
				
			
			@ -1905,10 +2027,38 @@ void rofi_view_workers_finalize(void) {
 | 
			
		|||
}
 | 
			
		||||
Mode *rofi_view_get_mode(RofiViewState *state) { return state->sw; }
 | 
			
		||||
 | 
			
		||||
static gboolean rofi_view_overlay_timeout(G_GNUC_UNUSED gpointer user_data) {
 | 
			
		||||
  RofiViewState *state = rofi_view_get_active();
 | 
			
		||||
  if (state) {
 | 
			
		||||
    widget_disable(WIDGET(state->overlay));
 | 
			
		||||
  }
 | 
			
		||||
  CacheState.overlay_timeout = 0;
 | 
			
		||||
  rofi_view_queue_redraw();
 | 
			
		||||
  return G_SOURCE_REMOVE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rofi_view_set_overlay_timeout(RofiViewState *state, const char *text) {
 | 
			
		||||
  if (state->overlay == NULL || state->list_view == NULL) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (text == NULL) {
 | 
			
		||||
    widget_disable(WIDGET(state->overlay));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  rofi_view_set_overlay(state, text);
 | 
			
		||||
  int timeout = rofi_theme_get_integer(WIDGET(state->overlay), "timeout", 3);
 | 
			
		||||
  CacheState.overlay_timeout =
 | 
			
		||||
      g_timeout_add_seconds(timeout, rofi_view_overlay_timeout, state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rofi_view_set_overlay(RofiViewState *state, const char *text) {
 | 
			
		||||
  if (state->overlay == NULL || state->list_view == NULL) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.overlay_timeout > 0) {
 | 
			
		||||
    g_source_remove(CacheState.overlay_timeout);
 | 
			
		||||
    CacheState.overlay_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (text == NULL) {
 | 
			
		||||
    widget_disable(WIDGET(state->overlay));
 | 
			
		||||
    return;
 | 
			
		||||
| 
						 | 
				
			
			@ -1965,10 +2115,6 @@ void rofi_view_update(RofiViewState *state, gboolean qr) {
 | 
			
		|||
  proxy->update(state, qr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rofi_view_maybe_update(RofiViewState *state) {
 | 
			
		||||
  proxy->maybe_update(state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rofi_view_temp_configure_notify(RofiViewState *state,
 | 
			
		||||
                                     xcb_configure_notify_event_t *xce) {
 | 
			
		||||
  proxy->temp_configure_notify(state, xce);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -64,8 +64,6 @@
 | 
			
		|||
 */
 | 
			
		||||
static void wayland_rofi_view_update(RofiViewState *state, gboolean qr);
 | 
			
		||||
 | 
			
		||||
static void wayland_rofi_view_maybe_update(RofiViewState *state);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Structure holding some state
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -119,7 +117,7 @@ static gboolean wayland_rofi_view_repaint(G_GNUC_UNUSED void *data) {
 | 
			
		|||
    // Repaint the view (if needed).
 | 
			
		||||
    // After a resize the edit_pixmap surface might not contain anything
 | 
			
		||||
    // anymore. If we already re-painted, this does nothing.
 | 
			
		||||
    wayland_rofi_view_maybe_update(state);
 | 
			
		||||
    rofi_view_maybe_update(state);
 | 
			
		||||
    WlState.repaint_source = 0;
 | 
			
		||||
  }
 | 
			
		||||
  return G_SOURCE_REMOVE;
 | 
			
		||||
| 
						 | 
				
			
			@ -191,7 +189,7 @@ static gboolean wayland_rofi_view_reload_idle(G_GNUC_UNUSED gpointer data) {
 | 
			
		|||
    state->reload = TRUE;
 | 
			
		||||
    state->refilter = TRUE;
 | 
			
		||||
 | 
			
		||||
    wayland_rofi_view_maybe_update(state);
 | 
			
		||||
    rofi_view_maybe_update(state);
 | 
			
		||||
  }
 | 
			
		||||
  WlState.idle_timeout = 0;
 | 
			
		||||
  return G_SOURCE_REMOVE;
 | 
			
		||||
| 
						 | 
				
			
			@ -369,29 +367,6 @@ static void wayland_rofi_view_update(RofiViewState *state, gboolean qr) {
 | 
			
		|||
 */
 | 
			
		||||
void process_result(RofiViewState *state);
 | 
			
		||||
 | 
			
		||||
static void wayland_rofi_view_maybe_update(RofiViewState *state) {
 | 
			
		||||
  if (rofi_view_get_completed(state)) {
 | 
			
		||||
    // This menu is done.
 | 
			
		||||
    rofi_view_finalize(state);
 | 
			
		||||
    // If there a state. (for example error) reload it.
 | 
			
		||||
    state = rofi_view_get_active();
 | 
			
		||||
 | 
			
		||||
    // cleanup, if no more state to display.
 | 
			
		||||
    if (state == NULL) {
 | 
			
		||||
      // Quit main-loop.
 | 
			
		||||
      rofi_quit_main_loop();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Update if requested.
 | 
			
		||||
  if (state->refilter) {
 | 
			
		||||
    rofi_view_refilter(state);
 | 
			
		||||
  }
 | 
			
		||||
  wayland_rofi_view_update(state, TRUE);
 | 
			
		||||
  return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void wayland_rofi_view_frame_callback(void) {
 | 
			
		||||
  if (WlState.repaint_source == 0) {
 | 
			
		||||
    WlState.repaint_source = g_idle_add_full(
 | 
			
		||||
| 
						 | 
				
			
			@ -427,14 +402,18 @@ static void wayland_rofi_view_cleanup(void) {
 | 
			
		|||
    g_source_remove(WlState.idle_timeout);
 | 
			
		||||
    WlState.idle_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.user_timeout > 0) {
 | 
			
		||||
    g_source_remove(CacheState.user_timeout);
 | 
			
		||||
    CacheState.user_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.refilter_timeout > 0) {
 | 
			
		||||
    g_source_remove(CacheState.refilter_timeout);
 | 
			
		||||
    CacheState.refilter_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.overlay_timeout) {
 | 
			
		||||
    g_source_remove(CacheState.overlay_timeout);
 | 
			
		||||
    CacheState.overlay_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.user_timeout > 0) {
 | 
			
		||||
    g_source_remove(CacheState.user_timeout);
 | 
			
		||||
    CacheState.user_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (WlState.repaint_source > 0) {
 | 
			
		||||
    g_source_remove(WlState.repaint_source);
 | 
			
		||||
    WlState.repaint_source = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -458,7 +437,6 @@ static void wayland_rofi_view_pool_refresh(void) {
 | 
			
		|||
 | 
			
		||||
static view_proxy view_ = {
 | 
			
		||||
    .update = wayland_rofi_view_update,
 | 
			
		||||
    .maybe_update = wayland_rofi_view_maybe_update,
 | 
			
		||||
    .temp_configure_notify = NULL,
 | 
			
		||||
    .temp_click_to_exit = NULL,
 | 
			
		||||
    .frame_callback = wayland_rofi_view_frame_callback,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -250,6 +250,15 @@ textbox *textbox_create(widget *parent, WidgetType type, const char *name,
 | 
			
		|||
      tb->placeholder = g_markup_escape_text(placeholder, -1);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const char *password_mask_char =
 | 
			
		||||
      rofi_theme_get_string(WIDGET(tb), "password-mask", NULL);
 | 
			
		||||
  if (password_mask_char == NULL || (*password_mask_char) == '\0') {
 | 
			
		||||
    tb->password_mask_char = "*";
 | 
			
		||||
  } else {
 | 
			
		||||
    tb->password_mask_char = password_mask_char;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  textbox_text(tb, txt ? txt : "");
 | 
			
		||||
  textbox_cursor_end(tb);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -336,11 +345,14 @@ static void __textbox_update_pango_text(textbox *tb) {
 | 
			
		|||
  }
 | 
			
		||||
  tb->show_placeholder = FALSE;
 | 
			
		||||
  if ((tb->flags & TB_PASSWORD) == TB_PASSWORD) {
 | 
			
		||||
    size_t l = g_utf8_strlen(tb->text, -1);
 | 
			
		||||
    char string[l + 1];
 | 
			
		||||
    memset(string, '*', l);
 | 
			
		||||
    string[l] = '\0';
 | 
			
		||||
    pango_layout_set_text(tb->layout, string, l);
 | 
			
		||||
    size_t text_len = g_utf8_strlen(tb->text, -1);
 | 
			
		||||
    size_t mask_len = strlen(tb->password_mask_char);
 | 
			
		||||
    char string[text_len * mask_len + 1];
 | 
			
		||||
    for (size_t offset = 0; offset < text_len * mask_len; offset += mask_len) {
 | 
			
		||||
      memcpy(string + offset, tb->password_mask_char, mask_len);
 | 
			
		||||
    }
 | 
			
		||||
    string[text_len * mask_len] = '\0';
 | 
			
		||||
    pango_layout_set_text(tb->layout, string, -1);
 | 
			
		||||
  } else if (tb->flags & TB_MARKUP || tb->tbft & MARKUP) {
 | 
			
		||||
    pango_layout_set_markup(tb->layout, tb->text, -1);
 | 
			
		||||
  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -488,7 +500,6 @@ static void textbox_draw(widget *wid, cairo_t *draw) {
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  textbox *tb = (textbox *)wid;
 | 
			
		||||
  int dot_offset = 0;
 | 
			
		||||
 | 
			
		||||
  if (tb->changed) {
 | 
			
		||||
    __textbox_update_pango_text(tb);
 | 
			
		||||
| 
						 | 
				
			
			@ -521,7 +532,7 @@ static void textbox_draw(widget *wid, cairo_t *draw) {
 | 
			
		|||
  {
 | 
			
		||||
    int rem =
 | 
			
		||||
        MAX(0, tb->widget.w - widget_padding_get_padding_width(WIDGET(tb)) -
 | 
			
		||||
                   line_width - dot_offset);
 | 
			
		||||
                   line_width);
 | 
			
		||||
    switch (pango_layout_get_alignment(tb->layout)) {
 | 
			
		||||
    case PANGO_ALIGN_CENTER:
 | 
			
		||||
      x = rem * (tb->xalign - 0.5);
 | 
			
		||||
| 
						 | 
				
			
			@ -530,7 +541,7 @@ static void textbox_draw(widget *wid, cairo_t *draw) {
 | 
			
		|||
      x = rem * (tb->xalign - 1.0);
 | 
			
		||||
      break;
 | 
			
		||||
    default:
 | 
			
		||||
      x = rem * tb->xalign + dot_offset;
 | 
			
		||||
      x = rem * tb->xalign;
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    x += widget_padding_get_left(WIDGET(tb));
 | 
			
		||||
| 
						 | 
				
			
			@ -541,11 +552,20 @@ static void textbox_draw(widget *wid, cairo_t *draw) {
 | 
			
		|||
    // We want to place the cursor based on the text shown.
 | 
			
		||||
    const char *text = pango_layout_get_text(tb->layout);
 | 
			
		||||
    // Clamp the position, should not be needed, but we are paranoid.
 | 
			
		||||
    int cursor_offset = MIN(tb->cursor, g_utf8_strlen(text, -1));
 | 
			
		||||
    size_t cursor_offset;
 | 
			
		||||
 | 
			
		||||
    if ((tb->flags & TB_PASSWORD) == TB_PASSWORD) {
 | 
			
		||||
      // Calculate cursor position based on mask length
 | 
			
		||||
      size_t mask_len = strlen(tb->password_mask_char);
 | 
			
		||||
      cursor_offset = MIN(tb->cursor * mask_len, strlen(text));
 | 
			
		||||
    } else {
 | 
			
		||||
      cursor_offset = MIN(tb->cursor, g_utf8_strlen(text, -1));
 | 
			
		||||
      // convert to byte location.
 | 
			
		||||
      char *offset = g_utf8_offset_to_pointer(text, cursor_offset);
 | 
			
		||||
      cursor_offset = offset - text;
 | 
			
		||||
    }
 | 
			
		||||
    PangoRectangle pos;
 | 
			
		||||
    // convert to byte location.
 | 
			
		||||
    char *offset = g_utf8_offset_to_pointer(text, cursor_offset);
 | 
			
		||||
    pango_layout_get_cursor_pos(tb->layout, offset - text, &pos, NULL);
 | 
			
		||||
    pango_layout_get_cursor_pos(tb->layout, cursor_offset, &pos, NULL);
 | 
			
		||||
    int cursor_x = pos.x / PANGO_SCALE;
 | 
			
		||||
    int cursor_y = pos.y / PANGO_SCALE;
 | 
			
		||||
    int cursor_height = pos.height / PANGO_SCALE;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -310,28 +310,6 @@ static void xcb_rofi_view_update(RofiViewState *state, gboolean qr) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void xcb_rofi_view_maybe_update(RofiViewState *state) {
 | 
			
		||||
  if (rofi_view_get_completed(state)) {
 | 
			
		||||
    // This menu is done.
 | 
			
		||||
    rofi_view_finalize(state);
 | 
			
		||||
    // If there a state. (for example error) reload it.
 | 
			
		||||
    state = rofi_view_get_active();
 | 
			
		||||
 | 
			
		||||
    // cleanup, if no more state to display.
 | 
			
		||||
    if (state == NULL) {
 | 
			
		||||
      // Quit main-loop.
 | 
			
		||||
      rofi_quit_main_loop();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Update if requested.
 | 
			
		||||
  if (state->refilter) {
 | 
			
		||||
    rofi_view_refilter(state);
 | 
			
		||||
  }
 | 
			
		||||
  rofi_view_update(state, TRUE);
 | 
			
		||||
  return;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Calculates the window position
 | 
			
		||||
| 
						 | 
				
			
			@ -962,14 +940,18 @@ static void xcb_rofi_view_cleanup(void) {
 | 
			
		|||
    g_source_remove(XcbState.idle_timeout);
 | 
			
		||||
    XcbState.idle_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.user_timeout > 0) {
 | 
			
		||||
    g_source_remove(CacheState.user_timeout);
 | 
			
		||||
    CacheState.user_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.refilter_timeout > 0) {
 | 
			
		||||
    g_source_remove(CacheState.refilter_timeout);
 | 
			
		||||
    CacheState.refilter_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.overlay_timeout) {
 | 
			
		||||
    g_source_remove(CacheState.overlay_timeout);
 | 
			
		||||
    CacheState.overlay_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (CacheState.user_timeout > 0) {
 | 
			
		||||
    g_source_remove(CacheState.user_timeout);
 | 
			
		||||
    CacheState.user_timeout = 0;
 | 
			
		||||
  }
 | 
			
		||||
  if (XcbState.repaint_source > 0) {
 | 
			
		||||
    g_source_remove(XcbState.repaint_source);
 | 
			
		||||
    XcbState.repaint_source = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -1020,7 +1002,6 @@ static void xcb_rofi_view_set_window_title(const char *title) {
 | 
			
		|||
 | 
			
		||||
static view_proxy view_ = {
 | 
			
		||||
    .update = xcb_rofi_view_update,
 | 
			
		||||
    .maybe_update = xcb_rofi_view_maybe_update,
 | 
			
		||||
    .temp_configure_notify = xcb_rofi_view_temp_configure_notify,
 | 
			
		||||
    .temp_click_to_exit = xcb_rofi_view_temp_click_to_exit,
 | 
			
		||||
    .frame_callback = xcb_rofi_view_frame_callback,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -132,6 +132,43 @@ static XrmOption xrmOptions[] = {
 | 
			
		|||
     "Custom command to generate preview icons",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
 | 
			
		||||
    {xrm_String,
 | 
			
		||||
     "on-selection-changed",
 | 
			
		||||
     {.str = &config.on_selection_changed},
 | 
			
		||||
     NULL,
 | 
			
		||||
     "Custom command to call when menu selection changes",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
    {xrm_String,
 | 
			
		||||
     "on-mode-changed",
 | 
			
		||||
     {.str = &config.on_mode_changed},
 | 
			
		||||
     NULL,
 | 
			
		||||
     "Custom command to call when menu mode changes",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
    {xrm_String,
 | 
			
		||||
     "on-entry-accepted",
 | 
			
		||||
     {.str = &config.on_entry_accepted},
 | 
			
		||||
     NULL,
 | 
			
		||||
     "Custom command to call when menu entry is accepted",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
    {xrm_String,
 | 
			
		||||
     "on-menu-canceled",
 | 
			
		||||
     {.str = &config.on_menu_canceled},
 | 
			
		||||
     NULL,
 | 
			
		||||
     "Custom command to call when menu is canceled",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
    {xrm_String,
 | 
			
		||||
     "on-menu-error",
 | 
			
		||||
     {.str = &config.on_menu_error},
 | 
			
		||||
     NULL,
 | 
			
		||||
     "Custom command to call when menu finds errors",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
    {xrm_String,
 | 
			
		||||
     "on-screenshot-taken",
 | 
			
		||||
     {.str = &config.on_screenshot_taken},
 | 
			
		||||
     NULL,
 | 
			
		||||
     "Custom command to call when menu screenshot is taken",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
 | 
			
		||||
    {xrm_String,
 | 
			
		||||
     "terminal",
 | 
			
		||||
     {.str = &config.terminal_emulator},
 | 
			
		||||
| 
						 | 
				
			
			@ -249,6 +286,12 @@ static XrmOption xrmOptions[] = {
 | 
			
		|||
     NULL,
 | 
			
		||||
     "Set case-sensitivity",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
    {xrm_Boolean,
 | 
			
		||||
     "case-smart",
 | 
			
		||||
     {.num = &config.case_smart},
 | 
			
		||||
     NULL,
 | 
			
		||||
     "Set smartcase like vim (determine case-sensitivity by input)",
 | 
			
		||||
     CONFIG_DEFAULT},
 | 
			
		||||
    {xrm_Boolean,
 | 
			
		||||
     "cycle",
 | 
			
		||||
     {.num = &config.cycle},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
Subproject commit 72bd7fb07f627a864e724639eea9fab6cccbd77c
 | 
			
		||||
Subproject commit 2f220a40ad32cf51b6b7d7ae83ab641a3ae76693
 | 
			
		||||
| 
						 | 
				
			
			@ -139,25 +139,25 @@ int main(int argc, char **argv) {
 | 
			
		|||
   */
 | 
			
		||||
 | 
			
		||||
  TASSERT(levenshtein("aap", g_utf8_strlen("aap", -1), "aap",
 | 
			
		||||
                      g_utf8_strlen("aap", -1)) == 0);
 | 
			
		||||
                      g_utf8_strlen("aap", -1), 0) == 0);
 | 
			
		||||
  TASSERT(levenshtein("aap", g_utf8_strlen("aap", -1), "aap ",
 | 
			
		||||
                      g_utf8_strlen("aap ", -1)) == 1);
 | 
			
		||||
                      g_utf8_strlen("aap ", -1), 0) == 1);
 | 
			
		||||
  TASSERT(levenshtein("aap ", g_utf8_strlen("aap ", -1), "aap",
 | 
			
		||||
                      g_utf8_strlen("aap", -1)) == 1);
 | 
			
		||||
                      g_utf8_strlen("aap", -1), 0) == 1);
 | 
			
		||||
  TASSERTE(levenshtein("aap", g_utf8_strlen("aap", -1), "aap noot",
 | 
			
		||||
                       g_utf8_strlen("aap noot", -1)),
 | 
			
		||||
                       g_utf8_strlen("aap noot", -1), 0),
 | 
			
		||||
           5u);
 | 
			
		||||
  TASSERTE(levenshtein("aap", g_utf8_strlen("aap", -1), "noot aap",
 | 
			
		||||
                       g_utf8_strlen("noot aap", -1)),
 | 
			
		||||
                       g_utf8_strlen("noot aap", -1), 0),
 | 
			
		||||
           5u);
 | 
			
		||||
  TASSERTE(levenshtein("aap", g_utf8_strlen("aap", -1), "noot aap mies",
 | 
			
		||||
                       g_utf8_strlen("noot aap mies", -1)),
 | 
			
		||||
                       g_utf8_strlen("noot aap mies", -1), 0),
 | 
			
		||||
           10u);
 | 
			
		||||
  TASSERTE(levenshtein("noot aap mies", g_utf8_strlen("noot aap mies", -1),
 | 
			
		||||
                       "aap", g_utf8_strlen("aap", -1)),
 | 
			
		||||
                       "aap", g_utf8_strlen("aap", -1), 0),
 | 
			
		||||
           10u);
 | 
			
		||||
  TASSERTE(levenshtein("otp", g_utf8_strlen("otp", -1), "noot aap",
 | 
			
		||||
                       g_utf8_strlen("noot aap", -1)),
 | 
			
		||||
                       g_utf8_strlen("noot aap", -1), 0),
 | 
			
		||||
           5u);
 | 
			
		||||
  /**
 | 
			
		||||
   * Quick converision check.
 | 
			
		||||
| 
						 | 
				
			
			@ -189,20 +189,48 @@ int main(int argc, char **argv) {
 | 
			
		|||
  }
 | 
			
		||||
  {
 | 
			
		||||
    TASSERTL(
 | 
			
		||||
        rofi_scorer_fuzzy_evaluate("aap noot mies", 12, "aap noot mies", 12),
 | 
			
		||||
        rofi_scorer_fuzzy_evaluate("aap noot mies", 12, "aap noot mies", 12, 0),
 | 
			
		||||
        -605);
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("anm", 3, "aap noot mies", 12), -155);
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("blu", 3, "aap noot mies", 12),
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("anm", 3, "aap noot mies", 12, 0),
 | 
			
		||||
             -155);
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("blu", 3, "aap noot mies", 12, 0),
 | 
			
		||||
             1073741824);
 | 
			
		||||
    config.case_sensitive = TRUE;
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("Anm", 3, "aap noot mies", 12),
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("Anm", 3, "aap noot mies", 12, 1),
 | 
			
		||||
             1073741754);
 | 
			
		||||
    config.case_sensitive = FALSE;
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("Anm", 3, "aap noot mies", 12), -155);
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("aap noot mies", 12, "Anm", 3),
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("Anm", 3, "aap noot mies", 12, 0),
 | 
			
		||||
             -155);
 | 
			
		||||
    TASSERTL(rofi_scorer_fuzzy_evaluate("aap noot mies", 12, "Anm", 3, 0),
 | 
			
		||||
             1073741824);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Case sensitivity
 | 
			
		||||
   */
 | 
			
		||||
  {
 | 
			
		||||
    int case_smart = config.case_smart;
 | 
			
		||||
    int case_sensitive = config.case_sensitive;
 | 
			
		||||
    {
 | 
			
		||||
      config.case_smart = FALSE;
 | 
			
		||||
      config.case_sensitive = FALSE;
 | 
			
		||||
      TASSERT(parse_case_sensitivity("all lower case 你好") == 0);
 | 
			
		||||
      TASSERT(parse_case_sensitivity("not All lowEr Case 你好") == 0);
 | 
			
		||||
      config.case_sensitive = TRUE;
 | 
			
		||||
      TASSERT(parse_case_sensitivity("all lower case 你好") == 1);
 | 
			
		||||
      TASSERT(parse_case_sensitivity("not All lowEr Case 你好") == 1);
 | 
			
		||||
    }
 | 
			
		||||
    {
 | 
			
		||||
      config.case_smart = TRUE;
 | 
			
		||||
      config.case_sensitive = TRUE;
 | 
			
		||||
      TASSERT(parse_case_sensitivity("all lower case") == 0);
 | 
			
		||||
      TASSERT(parse_case_sensitivity("AAAAAAAAAAAA") == 1);
 | 
			
		||||
      config.case_sensitive = FALSE;
 | 
			
		||||
      TASSERT(parse_case_sensitivity("all lower case 你好") == 0);
 | 
			
		||||
      TASSERT(parse_case_sensitivity("not All lowEr Case 你好") == 1);
 | 
			
		||||
    }
 | 
			
		||||
    config.case_smart = case_smart;
 | 
			
		||||
    config.case_sensitive = case_sensitive;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char *a;
 | 
			
		||||
  a = helper_string_replace_if_exists(
 | 
			
		||||
      "{terminal} [-t {title} blub ]-e {cmd}", "{cmd}", "aap", "{title}",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -125,7 +125,7 @@ END_TEST
 | 
			
		|||
 | 
			
		||||
START_TEST(test_mode_num_items) {
 | 
			
		||||
  unsigned int rows = mode_get_num_entries(&help_keys_mode);
 | 
			
		||||
  ck_assert_int_eq(rows, 79);
 | 
			
		||||
  ck_assert_int_eq(rows, 81);
 | 
			
		||||
  for (unsigned int i = 0; i < rows; i++) {
 | 
			
		||||
    int state = 0;
 | 
			
		||||
    GList *list = NULL;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue