keybindings: Rework the matching

Now we check all bindings in one place, allowing for future naughty
matching.

Signed-off-by: Quentin Glidic <sardemff7+git@sardemff7.net>
This commit is contained in:
Quentin Glidic 2016-04-07 15:32:22 +02:00
parent 65447b496a
commit 54f85fabaa
6 changed files with 167 additions and 135 deletions

View File

@ -95,10 +95,10 @@ void setup_abe ( void );
void cleanup_abe ( void );
/**
* Check if this key has been triggered.
* @returns TRUE if key combo matches, FALSE otherwise.
* Find if a binding has been triggered.
* @returns NUM_ABE if no key combo matches, a valid action otherwise.
*/
int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key );
KeyBindingAction abe_find_action ( unsigned int mask, xkb_keysym_t key );
/*@}*/
#endif // ROFI_KEYB_H

View File

@ -8,6 +8,7 @@
#include <cairo.h>
#include "widget.h"
#include "x11-helper.h"
#include "keyb.h"
/**
* @defgroup Textbox Textbox
@ -101,15 +102,7 @@ void textbox_text ( textbox *tb, const char *text );
*/
void textbox_draw ( textbox *tb, cairo_t *draw );
/**
* @param tb Handle to the textbox
* @param ev XEvent key inputs to textbox
*
* Let the textbox handle the input event.
*
* @returns if the key was handled (1), unhandled(0) or handled and return was pressed (-1)
*/
int textbox_keybinding ( textbox *tb, unsigned int modstate, xkb_keysym_t key );
int textbox_keybinding ( textbox *tb, KeyBindingAction action );
gboolean textbox_append ( textbox *tb, char *pad, int pad_len );
/**

View File

@ -3,6 +3,7 @@
#include "widget.h"
#include "textbox.h"
#include "scrollbar.h"
#include "keyb.h"
#include "x11-helper.h"
/**
@ -47,7 +48,7 @@ struct RofiViewState
// Last offset in paginating.
unsigned int last_offset;
xkb_keysym_t prev_key;
KeyBindingAction prev_action;
xcb_timestamp_t last_button_press;
int quit;

View File

@ -135,7 +135,7 @@ void cleanup_abe ( void )
}
}
int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key )
static gboolean abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t key )
{
ActionBindingEntry *akb = &( abe[action] );
@ -148,3 +148,16 @@ int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t k
return FALSE;
}
KeyBindingAction abe_find_action ( unsigned int mask, xkb_keysym_t key )
{
KeyBindingAction action;
for ( action = 0 ; action < NUM_ABE ; ++action ) {
if ( abe_test_action ( action, mask, key ) ) {
break;
}
}
return action;
}

View File

@ -525,78 +525,67 @@ static void textbox_cursor_del_word ( textbox *tb )
// 0 = unhandled
// 1 = handled
// -1 = handled and return pressed (finished)
int textbox_keybinding ( textbox *tb, unsigned int modstate, xkb_keysym_t key )
int textbox_keybinding ( textbox *tb, KeyBindingAction action )
{
if ( !( tb->flags & TB_EDITABLE ) ) {
g_return_val_if_reached(0);
}
int old_blink = tb->blink;
tb->blink = 2;
switch ( action )
{
// Left or Ctrl-b
if ( abe_test_action ( MOVE_CHAR_BACK, modstate, key ) ) {
case MOVE_CHAR_BACK:
textbox_cursor_dec ( tb );
return 2;
}
// Right or Ctrl-F
if ( abe_test_action ( MOVE_CHAR_FORWARD, modstate, key ) ) {
case MOVE_CHAR_FORWARD:
textbox_cursor_inc ( tb );
return 2;
}
// Ctrl-U: Kill from the beginning to the end of the line.
if ( abe_test_action ( CLEAR_LINE, modstate, key ) ) {
case CLEAR_LINE:
textbox_text ( tb, "" );
return 1;
}
// Ctrl-A
if ( abe_test_action ( MOVE_FRONT, modstate, key ) ) {
case MOVE_FRONT:
textbox_cursor ( tb, 0 );
return 2;
}
// Ctrl-E
if ( abe_test_action ( MOVE_END, modstate, key ) ) {
case MOVE_END:
textbox_cursor_end ( tb );
return 2;
}
// Ctrl-Alt-h
if ( abe_test_action ( REMOVE_WORD_BACK, modstate, key ) ) {
case REMOVE_WORD_BACK:
textbox_cursor_bkspc_word ( tb );
return 1;
}
// Ctrl-Alt-d
if ( abe_test_action ( REMOVE_WORD_FORWARD, modstate, key ) ) {
case REMOVE_WORD_FORWARD:
textbox_cursor_del_word ( tb );
return 1;
} // Delete or Ctrl-D
if ( abe_test_action ( REMOVE_CHAR_FORWARD, modstate, key ) ) {
// Delete or Ctrl-D
case REMOVE_CHAR_FORWARD:
textbox_cursor_del ( tb );
return 1;
}
// Alt-B
if ( abe_test_action ( MOVE_WORD_BACK, modstate, key ) ) {
case MOVE_WORD_BACK:
textbox_cursor_dec_word ( tb );
return 2;
}
// Alt-F
if ( abe_test_action ( MOVE_WORD_FORWARD, modstate, key ) ) {
case MOVE_WORD_FORWARD:
textbox_cursor_inc_word ( tb );
return 2;
}
// BackSpace, Ctrl-h
if ( abe_test_action ( REMOVE_CHAR_BACK, modstate, key ) ) {
case REMOVE_CHAR_BACK:
textbox_cursor_bkspc ( tb );
return 1;
}
if ( abe_test_action ( ACCEPT_CUSTOM, modstate, key ) ) {
case ACCEPT_CUSTOM:
return -2;
}
if ( abe_test_action ( ACCEPT_ENTRY, modstate, key ) ) {
case ACCEPT_ENTRY:
return -1;
default:
g_return_val_if_reached(0);
}
tb->blink = old_blink;
return 0;
g_return_val_if_reached(0);
}
gboolean textbox_append ( textbox *tb, char *pad, int pad_len )

View File

@ -1087,30 +1087,30 @@ static void rofi_view_paste ( RofiViewState *state, xcb_selection_notify_event_t
*
* Keyboard navigation through the elements.
*/
static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t key, unsigned int modstate )
static void rofi_view_keyboard_navigation ( RofiViewState *state, KeyBindingAction action )
{
switch ( action )
{
// pressing one of the global key bindings closes the switcher. This allows fast closing of the
// menu if an item is not selected
if ( abe_test_action ( CANCEL, modstate, key ) ) {
case CANCEL:
state->retv = MENU_CANCEL;
state->quit = TRUE;
return 1;
}
break;
// Up, Ctrl-p or Shift-Tab
if ( abe_test_action ( ROW_UP, modstate, key ) ) {
case ROW_UP:
rofi_view_nav_up ( state );
return 1;
}
if ( abe_test_action ( ROW_TAB, modstate, key ) ) {
break;
case ROW_TAB:
if ( state->filtered_lines == 1 ) {
state->retv = MENU_OK;
( state->selected_line ) = state->line_map[state->selected];
state->quit = 1;
return 1;
break;
}
// Double tab!
if ( state->filtered_lines == 0 && key == state->prev_key ) {
if ( state->filtered_lines == 0 && action == state->prev_action ) {
state->retv = MENU_NEXT;
( state->selected_line ) = 0;
state->quit = TRUE;
@ -1118,39 +1118,31 @@ static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t ke
else {
rofi_view_nav_down ( state );
}
state->prev_key = key;
return 1;
}
state->prev_action = action;
break;
// Down, Ctrl-n
if ( abe_test_action ( ROW_DOWN, modstate, key ) ) {
case ROW_DOWN:
rofi_view_nav_down ( state );
return 1;
}
if ( abe_test_action ( ROW_LEFT, modstate, key ) ) {
break;
case ROW_LEFT:
rofi_view_nav_left ( state );
return 1;
}
if ( abe_test_action ( ROW_RIGHT, modstate, key ) ) {
break;
case ROW_RIGHT:
rofi_view_nav_right ( state );
return 1;
}
if ( abe_test_action ( PAGE_PREV, modstate, key ) ) {
break;
case PAGE_PREV:
rofi_view_nav_page_prev ( state );
return 1;
}
if ( abe_test_action ( PAGE_NEXT, modstate, key ) ) {
break;
case PAGE_NEXT:
rofi_view_nav_page_next ( state );
return 1;
}
if ( abe_test_action ( ROW_FIRST, modstate, key ) ) {
break;
case ROW_FIRST:
rofi_view_nav_first ( state );
return 1;
}
if ( abe_test_action ( ROW_LAST, modstate, key ) ) {
break;
case ROW_LAST:
rofi_view_nav_last ( state );
return 1;
}
if ( abe_test_action ( ROW_SELECT, modstate, key ) ) {
break;
case ROW_SELECT:
// If a valid item is selected, return that..
if ( state->selected < state->filtered_lines ) {
char *str = mode_get_completion ( state->sw, state->line_map[state->selected] );
@ -1160,9 +1152,10 @@ static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t ke
state->update = TRUE;
state->refilter = TRUE;
}
return 1;
break;
default:
g_return_if_reached();
}
return 0;
}
static void rofi_view_mouse_navigation ( RofiViewState *state, xcb_button_press_event_t *xbe )
@ -1356,106 +1349,149 @@ static void rofi_view_handle_keypress ( RofiViewState *state, xkb_stuff *xkb, xc
unsigned int modstate = x11_canonalize_mask ( xkpe->state );
if ( key != XKB_KEY_NoSymbol ) {
KeyBindingAction action;
action = abe_find_action ( modstate, key );
switch ( action )
{
// Handling of paste
if ( abe_test_action ( PASTE_PRIMARY, modstate, key ) ) {
case PASTE_PRIMARY:
xcb_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_PRIMARY,
xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME );
xcb_flush ( xcb->connection );
return;
}
if ( abe_test_action ( PASTE_SECONDARY, modstate, key ) ) {
case PASTE_SECONDARY:
xcb_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_SECONDARY,
xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME );
xcb_flush ( xcb->connection );
return;
}
if ( abe_test_action ( SCREENSHOT, modstate, key ) ) {
case SCREENSHOT:
menu_capture_screenshot ( );
return;
}
if ( abe_test_action ( TOGGLE_SORT, modstate, key ) ) {
case TOGGLE_SORT:
config.levenshtein_sort = !config.levenshtein_sort;
state->refilter = TRUE;
state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () );
return;
}
if ( abe_test_action ( MODE_PREVIOUS, modstate, key ) ) {
case MODE_PREVIOUS:
state->retv = MENU_PREVIOUS;
( state->selected_line ) = 0;
state->quit = TRUE;
return;
}
// Menu navigation.
if ( abe_test_action ( MODE_NEXT, modstate, key ) ) {
case MODE_NEXT:
state->retv = MENU_NEXT;
( state->selected_line ) = 0;
state->quit = TRUE;
return;
}
// Toggle case sensitivity.
if ( abe_test_action ( TOGGLE_CASE_SENSITIVITY, modstate, key ) ) {
case TOGGLE_CASE_SENSITIVITY:
config.case_sensitive = !config.case_sensitive;
( state->selected_line ) = 0;
state->refilter = TRUE;
state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () );
return;
}
// Special delete entry command.
if ( abe_test_action ( DELETE_ENTRY, modstate, key ) ) {
case DELETE_ENTRY:
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
state->retv = MENU_ENTRY_DELETE;
state->quit = TRUE;
return;
}
}
for ( unsigned int a = CUSTOM_1; a <= CUSTOM_19; a++ ) {
if ( abe_test_action ( a, modstate, key ) ) {
state->selected_line = UINT32_MAX;
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
}
state->retv = MENU_QUICK_SWITCH | ( ( a - CUSTOM_1 ) & MENU_LOWER_MASK );
state->quit = TRUE;
return;
}
}
if ( rofi_view_keyboard_navigation ( state, key, modstate ) ) {
return;
}
int rc = textbox_keybinding ( state->text, modstate, key );
// Row is accepted.
if ( rc < 0 ) {
// If a valid item is selected, return that..
break;
case CUSTOM_1:
case CUSTOM_2:
case CUSTOM_3:
case CUSTOM_4:
case CUSTOM_5:
case CUSTOM_6:
case CUSTOM_7:
case CUSTOM_8:
case CUSTOM_9:
case CUSTOM_10:
case CUSTOM_11:
case CUSTOM_12:
case CUSTOM_13:
case CUSTOM_14:
case CUSTOM_15:
case CUSTOM_16:
case CUSTOM_17:
case CUSTOM_18:
case CUSTOM_19:
state->selected_line = UINT32_MAX;
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
state->retv = MENU_OK;
}
else {
// Nothing entered and nothing selected.
state->retv = MENU_CUSTOM_INPUT;
}
if ( rc == -2 ) {
state->retv |= MENU_CUSTOM_ACTION;
}
state->retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK );
state->quit = TRUE;
return;
}
// Key press is handled by entry box.
else if ( rc == 1 ) {
state->refilter = TRUE;
state->update = TRUE;
// If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too
case CANCEL:
case ROW_UP:
case ROW_TAB:
case ROW_DOWN:
case ROW_LEFT:
case ROW_RIGHT:
case PAGE_PREV:
case PAGE_NEXT:
case ROW_FIRST:
case ROW_LAST:
case ROW_SELECT:
rofi_view_keyboard_navigation ( state, action );
return;
// If you add a binding here, make sure to add it to textbox_keybinding too
case MOVE_CHAR_BACK:
case MOVE_CHAR_FORWARD:
case CLEAR_LINE:
case MOVE_FRONT:
case MOVE_END:
case REMOVE_WORD_BACK:
case REMOVE_WORD_FORWARD:
case REMOVE_CHAR_FORWARD:
case MOVE_WORD_BACK:
case MOVE_WORD_FORWARD:
case REMOVE_CHAR_BACK:
case ACCEPT_CUSTOM:
case ACCEPT_ENTRY:
{
int rc = textbox_keybinding ( state->text, action );
// Row is accepted.
if ( rc < 0 ) {
// If a valid item is selected, return that..
state->selected_line = UINT32_MAX;
if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected];
state->retv = MENU_OK;
}
else {
// Nothing entered and nothing selected.
state->retv = MENU_CUSTOM_INPUT;
}
if ( rc == -2 ) {
state->retv |= MENU_CUSTOM_ACTION;
}
state->quit = TRUE;
return;
}
// Key press is handled by entry box.
else if ( rc == 1 ) {
state->refilter = TRUE;
state->update = TRUE;
return;
}
else if ( rc == 2 ) {
// redraw.
state->update = TRUE;
return;
}
break;
}
else if ( rc == 2 ) {
// redraw.
state->update = TRUE;
return;
case NUM_ABE:
break;
}
}