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 ); void cleanup_abe ( void );
/** /**
* Check if this key has been triggered. * Find if a binding has been triggered.
* @returns TRUE if key combo matches, FALSE otherwise. * @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 #endif // ROFI_KEYB_H

View File

@ -8,6 +8,7 @@
#include <cairo.h> #include <cairo.h>
#include "widget.h" #include "widget.h"
#include "x11-helper.h" #include "x11-helper.h"
#include "keyb.h"
/** /**
* @defgroup Textbox Textbox * @defgroup Textbox Textbox
@ -101,15 +102,7 @@ void textbox_text ( textbox *tb, const char *text );
*/ */
void textbox_draw ( textbox *tb, cairo_t *draw ); void textbox_draw ( textbox *tb, cairo_t *draw );
/** int textbox_keybinding ( textbox *tb, KeyBindingAction action );
* @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 );
gboolean textbox_append ( textbox *tb, char *pad, int pad_len ); gboolean textbox_append ( textbox *tb, char *pad, int pad_len );
/** /**

View File

@ -3,6 +3,7 @@
#include "widget.h" #include "widget.h"
#include "textbox.h" #include "textbox.h"
#include "scrollbar.h" #include "scrollbar.h"
#include "keyb.h"
#include "x11-helper.h" #include "x11-helper.h"
/** /**
@ -47,7 +48,7 @@ struct RofiViewState
// Last offset in paginating. // Last offset in paginating.
unsigned int last_offset; unsigned int last_offset;
xkb_keysym_t prev_key; KeyBindingAction prev_action;
xcb_timestamp_t last_button_press; xcb_timestamp_t last_button_press;
int quit; 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] ); ActionBindingEntry *akb = &( abe[action] );
@ -148,3 +148,16 @@ int abe_test_action ( KeyBindingAction action, unsigned int mask, xkb_keysym_t k
return FALSE; 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 // 0 = unhandled
// 1 = handled // 1 = handled
// -1 = handled and return pressed (finished) // -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 ) ) { if ( !( tb->flags & TB_EDITABLE ) ) {
g_return_val_if_reached(0); g_return_val_if_reached(0);
} }
int old_blink = tb->blink;
tb->blink = 2;
switch ( action )
{
// Left or Ctrl-b // Left or Ctrl-b
if ( abe_test_action ( MOVE_CHAR_BACK, modstate, key ) ) { case MOVE_CHAR_BACK:
textbox_cursor_dec ( tb ); textbox_cursor_dec ( tb );
return 2; return 2;
}
// Right or Ctrl-F // Right or Ctrl-F
if ( abe_test_action ( MOVE_CHAR_FORWARD, modstate, key ) ) { case MOVE_CHAR_FORWARD:
textbox_cursor_inc ( tb ); textbox_cursor_inc ( tb );
return 2; return 2;
}
// Ctrl-U: Kill from the beginning to the end of the line. // 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, "" ); textbox_text ( tb, "" );
return 1; return 1;
}
// Ctrl-A // Ctrl-A
if ( abe_test_action ( MOVE_FRONT, modstate, key ) ) { case MOVE_FRONT:
textbox_cursor ( tb, 0 ); textbox_cursor ( tb, 0 );
return 2; return 2;
}
// Ctrl-E // Ctrl-E
if ( abe_test_action ( MOVE_END, modstate, key ) ) { case MOVE_END:
textbox_cursor_end ( tb ); textbox_cursor_end ( tb );
return 2; return 2;
}
// Ctrl-Alt-h // Ctrl-Alt-h
if ( abe_test_action ( REMOVE_WORD_BACK, modstate, key ) ) { case REMOVE_WORD_BACK:
textbox_cursor_bkspc_word ( tb ); textbox_cursor_bkspc_word ( tb );
return 1; return 1;
}
// Ctrl-Alt-d // Ctrl-Alt-d
if ( abe_test_action ( REMOVE_WORD_FORWARD, modstate, key ) ) { case REMOVE_WORD_FORWARD:
textbox_cursor_del_word ( tb ); textbox_cursor_del_word ( tb );
return 1; return 1;
} // Delete or Ctrl-D // Delete or Ctrl-D
if ( abe_test_action ( REMOVE_CHAR_FORWARD, modstate, key ) ) { case REMOVE_CHAR_FORWARD:
textbox_cursor_del ( tb ); textbox_cursor_del ( tb );
return 1; return 1;
}
// Alt-B // Alt-B
if ( abe_test_action ( MOVE_WORD_BACK, modstate, key ) ) { case MOVE_WORD_BACK:
textbox_cursor_dec_word ( tb ); textbox_cursor_dec_word ( tb );
return 2; return 2;
}
// Alt-F // Alt-F
if ( abe_test_action ( MOVE_WORD_FORWARD, modstate, key ) ) { case MOVE_WORD_FORWARD:
textbox_cursor_inc_word ( tb ); textbox_cursor_inc_word ( tb );
return 2; return 2;
}
// BackSpace, Ctrl-h // BackSpace, Ctrl-h
if ( abe_test_action ( REMOVE_CHAR_BACK, modstate, key ) ) { case REMOVE_CHAR_BACK:
textbox_cursor_bkspc ( tb ); textbox_cursor_bkspc ( tb );
return 1; return 1;
} case ACCEPT_CUSTOM:
if ( abe_test_action ( ACCEPT_CUSTOM, modstate, key ) ) {
return -2; return -2;
} case ACCEPT_ENTRY:
if ( abe_test_action ( ACCEPT_ENTRY, modstate, key ) ) {
return -1; return -1;
default:
g_return_val_if_reached(0);
} }
tb->blink = old_blink; g_return_val_if_reached(0);
return 0;
} }
gboolean textbox_append ( textbox *tb, char *pad, int pad_len ) 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. * 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 // pressing one of the global key bindings closes the switcher. This allows fast closing of the
// menu if an item is not selected // menu if an item is not selected
if ( abe_test_action ( CANCEL, modstate, key ) ) { case CANCEL:
state->retv = MENU_CANCEL; state->retv = MENU_CANCEL;
state->quit = TRUE; state->quit = TRUE;
return 1; break;
}
// Up, Ctrl-p or Shift-Tab // Up, Ctrl-p or Shift-Tab
if ( abe_test_action ( ROW_UP, modstate, key ) ) { case ROW_UP:
rofi_view_nav_up ( state ); rofi_view_nav_up ( state );
return 1; break;
} case ROW_TAB:
if ( abe_test_action ( ROW_TAB, modstate, key ) ) {
if ( state->filtered_lines == 1 ) { if ( state->filtered_lines == 1 ) {
state->retv = MENU_OK; state->retv = MENU_OK;
( state->selected_line ) = state->line_map[state->selected]; ( state->selected_line ) = state->line_map[state->selected];
state->quit = 1; state->quit = 1;
return 1; break;
} }
// Double tab! // 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->retv = MENU_NEXT;
( state->selected_line ) = 0; ( state->selected_line ) = 0;
state->quit = TRUE; state->quit = TRUE;
@ -1118,39 +1118,31 @@ static int rofi_view_keyboard_navigation ( RofiViewState *state, xkb_keysym_t ke
else { else {
rofi_view_nav_down ( state ); rofi_view_nav_down ( state );
} }
state->prev_key = key; state->prev_action = action;
return 1; break;
}
// Down, Ctrl-n // Down, Ctrl-n
if ( abe_test_action ( ROW_DOWN, modstate, key ) ) { case ROW_DOWN:
rofi_view_nav_down ( state ); rofi_view_nav_down ( state );
return 1; break;
} case ROW_LEFT:
if ( abe_test_action ( ROW_LEFT, modstate, key ) ) {
rofi_view_nav_left ( state ); rofi_view_nav_left ( state );
return 1; break;
} case ROW_RIGHT:
if ( abe_test_action ( ROW_RIGHT, modstate, key ) ) {
rofi_view_nav_right ( state ); rofi_view_nav_right ( state );
return 1; break;
} case PAGE_PREV:
if ( abe_test_action ( PAGE_PREV, modstate, key ) ) {
rofi_view_nav_page_prev ( state ); rofi_view_nav_page_prev ( state );
return 1; break;
} case PAGE_NEXT:
if ( abe_test_action ( PAGE_NEXT, modstate, key ) ) {
rofi_view_nav_page_next ( state ); rofi_view_nav_page_next ( state );
return 1; break;
} case ROW_FIRST:
if ( abe_test_action ( ROW_FIRST, modstate, key ) ) {
rofi_view_nav_first ( state ); rofi_view_nav_first ( state );
return 1; break;
} case ROW_LAST:
if ( abe_test_action ( ROW_LAST, modstate, key ) ) {
rofi_view_nav_last ( state ); rofi_view_nav_last ( state );
return 1; break;
} case ROW_SELECT:
if ( abe_test_action ( ROW_SELECT, modstate, key ) ) {
// If a valid item is selected, return that.. // If a valid item is selected, return that..
if ( state->selected < state->filtered_lines ) { if ( state->selected < state->filtered_lines ) {
char *str = mode_get_completion ( state->sw, state->line_map[state->selected] ); 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->update = TRUE;
state->refilter = 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 ) 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 ); unsigned int modstate = x11_canonalize_mask ( xkpe->state );
if ( key != XKB_KEY_NoSymbol ) { if ( key != XKB_KEY_NoSymbol ) {
KeyBindingAction action;
action = abe_find_action ( modstate, key );
switch ( action )
{
// Handling of paste // 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_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_PRIMARY,
xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME ); xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME );
xcb_flush ( xcb->connection ); xcb_flush ( xcb->connection );
return; return;
} case PASTE_SECONDARY:
if ( abe_test_action ( PASTE_SECONDARY, modstate, key ) ) {
xcb_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_SECONDARY, xcb_convert_selection ( xcb->connection, CacheState.main_window, XCB_ATOM_SECONDARY,
xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME ); xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME );
xcb_flush ( xcb->connection ); xcb_flush ( xcb->connection );
return; return;
} case SCREENSHOT:
if ( abe_test_action ( SCREENSHOT, modstate, key ) ) {
menu_capture_screenshot ( ); menu_capture_screenshot ( );
return; return;
} case TOGGLE_SORT:
if ( abe_test_action ( TOGGLE_SORT, modstate, key ) ) {
config.levenshtein_sort = !config.levenshtein_sort; config.levenshtein_sort = !config.levenshtein_sort;
state->refilter = TRUE; state->refilter = TRUE;
state->update = TRUE; state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () ); textbox_text ( state->case_indicator, get_matching_state () );
return; return;
} case MODE_PREVIOUS:
if ( abe_test_action ( MODE_PREVIOUS, modstate, key ) ) {
state->retv = MENU_PREVIOUS; state->retv = MENU_PREVIOUS;
( state->selected_line ) = 0; ( state->selected_line ) = 0;
state->quit = TRUE; state->quit = TRUE;
return; return;
}
// Menu navigation. // Menu navigation.
if ( abe_test_action ( MODE_NEXT, modstate, key ) ) { case MODE_NEXT:
state->retv = MENU_NEXT; state->retv = MENU_NEXT;
( state->selected_line ) = 0; ( state->selected_line ) = 0;
state->quit = TRUE; state->quit = TRUE;
return; return;
}
// Toggle case sensitivity. // Toggle case sensitivity.
if ( abe_test_action ( TOGGLE_CASE_SENSITIVITY, modstate, key ) ) { case TOGGLE_CASE_SENSITIVITY:
config.case_sensitive = !config.case_sensitive; config.case_sensitive = !config.case_sensitive;
( state->selected_line ) = 0; ( state->selected_line ) = 0;
state->refilter = TRUE; state->refilter = TRUE;
state->update = TRUE; state->update = TRUE;
textbox_text ( state->case_indicator, get_matching_state () ); textbox_text ( state->case_indicator, get_matching_state () );
return; return;
}
// Special delete entry command. // Special delete entry command.
if ( abe_test_action ( DELETE_ENTRY, modstate, key ) ) { case DELETE_ENTRY:
if ( state->selected < state->filtered_lines ) { if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected]; ( state->selected_line ) = state->line_map[state->selected];
state->retv = MENU_ENTRY_DELETE; state->retv = MENU_ENTRY_DELETE;
state->quit = TRUE; state->quit = TRUE;
return; return;
} }
} break;
for ( unsigned int a = CUSTOM_1; a <= CUSTOM_19; a++ ) { case CUSTOM_1:
if ( abe_test_action ( a, modstate, key ) ) { case CUSTOM_2:
state->selected_line = UINT32_MAX; case CUSTOM_3:
if ( state->selected < state->filtered_lines ) { case CUSTOM_4:
( state->selected_line ) = state->line_map[state->selected]; case CUSTOM_5:
} case CUSTOM_6:
state->retv = MENU_QUICK_SWITCH | ( ( a - CUSTOM_1 ) & MENU_LOWER_MASK ); case CUSTOM_7:
state->quit = TRUE; case CUSTOM_8:
return; case CUSTOM_9:
} case CUSTOM_10:
} case CUSTOM_11:
if ( rofi_view_keyboard_navigation ( state, key, modstate ) ) { case CUSTOM_12:
return; case CUSTOM_13:
} case CUSTOM_14:
case CUSTOM_15:
int rc = textbox_keybinding ( state->text, modstate, key ); case CUSTOM_16:
// Row is accepted. case CUSTOM_17:
if ( rc < 0 ) { case CUSTOM_18:
// If a valid item is selected, return that.. case CUSTOM_19:
state->selected_line = UINT32_MAX; state->selected_line = UINT32_MAX;
if ( state->selected < state->filtered_lines ) { if ( state->selected < state->filtered_lines ) {
( state->selected_line ) = state->line_map[state->selected]; ( state->selected_line ) = state->line_map[state->selected];
state->retv = MENU_OK;
} }
else { state->retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK );
// Nothing entered and nothing selected.
state->retv = MENU_CUSTOM_INPUT;
}
if ( rc == -2 ) {
state->retv |= MENU_CUSTOM_ACTION;
}
state->quit = TRUE; state->quit = TRUE;
return; return;
} // If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too
// Key press is handled by entry box. case CANCEL:
else if ( rc == 1 ) { case ROW_UP:
state->refilter = TRUE; case ROW_TAB:
state->update = TRUE; 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; 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 ) { case NUM_ABE:
// redraw. break;
state->update = TRUE;
return;
} }
} }