diff --git a/include/display-internal.h b/include/display-internal.h new file mode 100644 index 00000000..b201a73f --- /dev/null +++ b/include/display-internal.h @@ -0,0 +1,50 @@ +/* + * rofi + * + * MIT/X11 License + * Copyright © 2013-2020 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef ROFI_DISPLAY_INTERNAL_H +#define ROFI_DISPLAY_INTERNAL_H + +#include +#include "helper.h" +#include "nkutils-bindings.h" + +struct _workarea; +struct _view_proxy; + +typedef struct _display_proxy { + gboolean (*setup) ( GMainLoop *main_loop, NkBindings *bindings ); + gboolean (*late_setup) ( void ); + void (*early_cleanup) ( void ); + void (*cleanup) ( void ); + void (*dump_monitor_layout) ( void ); + void (*startup_notification) ( RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data ); + int (*monitor_active) ( struct _workarea *mon ); + + const struct _view_proxy* (*view) ( void ); +} display_proxy; + +#endif diff --git a/include/display.h b/include/display.h index 21013565..6efd01d0 100644 --- a/include/display.h +++ b/include/display.h @@ -56,27 +56,15 @@ typedef struct _workarea struct _workarea *next; } workarea; -struct _view_proxy; - -typedef struct _display_proxy { - gboolean (*setup) ( GMainLoop *main_loop, NkBindings *bindings ); - gboolean (*late_setup) ( void ); - void (*early_cleanup) ( void ); - void (*cleanup) ( void ); - void (*dump_monitor_layout) ( void ); - void (*startup_notification) ( RofiHelperExecuteContext *context, GSpawnChildSetupFunc *child_setup, gpointer *user_data ); - int (*monitor_active) ( workarea *mon ); - - const struct _view_proxy* (*view) ( void ); -} display_proxy; +struct _display_proxy; /* Implementations */ -extern display_proxy * const xcb_proxy; +extern struct _display_proxy * const xcb_proxy; #ifdef ENABLE_WAYLAND -extern display_proxy * const wayland_proxy; +extern struct _display_proxy * const wayland_proxy; #endif -void display_init( const display_proxy *disp_in ); +void display_init( const struct _display_proxy *disp_in ); /** * @param mon workarea to be filled in. diff --git a/include/view-internal.h b/include/view-internal.h index eac81a2e..8eadc43c 100644 --- a/include/view-internal.h +++ b/include/view-internal.h @@ -151,11 +151,54 @@ struct RofiViewState }; /** @} */ -void rofi_view_reload_message_bar ( struct RofiViewState *state ); +typedef struct _view_proxy { + void (*update) ( RofiViewState *state, gboolean qr ); + void (*maybe_update) ( RofiViewState *state ); + void (*temp_configure_notify) ( RofiViewState *state, xcb_configure_notify_event_t *xce ); + void (*temp_click_to_exit) ( RofiViewState *state, xcb_window_t target ); + void (*frame_callback) ( void ); + + void (*queue_redraw) ( void ); + + void (*set_window_title) ( const char * title ); + void (*calculate_window_position) ( RofiViewState *state ); + void (*calculate_window_width) ( RofiViewState *state ); + int (*calculate_window_height) ( RofiViewState *state ); + void (*window_update_size) ( RofiViewState *state ); + + void (*cleanup) ( void ); + void (*hide) ( void ); + void (*reload) ( void ); + void (*__create_window) ( MenuFlags menu_flags ); + xcb_window_t (*get_window) ( void ); + + void (*get_current_monitor) ( int *width, int *height ); + void (*capture_screenshot) ( void ); + + void (*set_size) ( RofiViewState * state, gint width, gint height ); + void (*get_size) ( RofiViewState * state, gint *width, gint *height ); +} view_proxy; + +/** + * Structure holding cached state. + */ +struct _rofi_view_cache_state +{ + /** main x11 windows */ + xcb_window_t main_window; + /** Main flags */ + MenuFlags flags; + /** List of stacked views */ + GQueue views; +}; +extern struct _rofi_view_cache_state CacheState; + +void rofi_view_update ( struct RofiViewState *state, gboolean qr ); void rofi_view_calculate_window_position ( struct RofiViewState *state ); +void rofi_view_calculate_window_width ( struct RofiViewState *state ); int rofi_view_calculate_window_height ( struct RofiViewState *state ); void rofi_view_window_update_size ( struct RofiViewState * state ); -void rofi_view_call_thread ( gpointer data, gpointer user_data ); void rofi_view_refilter ( struct RofiViewState *state ); +void rofi_view_set_window_title ( const char * title ); #endif diff --git a/include/view.h b/include/view.h index 70bfa897..fed458e3 100644 --- a/include/view.h +++ b/include/view.h @@ -315,50 +315,17 @@ void rofi_view_set_size ( RofiViewState * state, gint width, gint height ); void rofi_view_get_size ( RofiViewState * state, gint *width, gint *height ); -typedef struct _view_proxy { - RofiViewState* (*create) ( Mode *sw, const char *input, MenuFlags menu_flags, void ( *finalize )( RofiViewState * ) ); - void (*handle_text) ( RofiViewState *state, char *text ); - void (*maybe_update) ( RofiViewState *state ); - void (*temp_configure_notify) ( RofiViewState *state, xcb_configure_notify_event_t *xce ); - void (*temp_click_to_exit) ( RofiViewState *state, xcb_window_t target ); - void (*frame_callback) ( void ); - const char * (*get_user_input) ( const RofiViewState *state ); - void (*set_selected_line) ( RofiViewState *state, unsigned int selected_line ); - gboolean (*trigger_action) ( RofiViewState *state, BindingsScope scope, guint action ); - void (*free) ( RofiViewState *state ); - RofiViewState * (*get_active) ( void ); - void (*set_active) ( RofiViewState *state ); - int (*error_dialog) ( const char *msg, int markup ); - void (*queue_redraw) ( void ); - void (*calculate_window_position) ( RofiViewState *state ); - int (*calculate_window_height) ( RofiViewState *state ); - void (*window_update_size) ( RofiViewState *state ); +void rofi_view_set_window_tilte ( const char * title ); - void (*cleanup) ( void ); - Mode * (*get_mode) ( RofiViewState *state ); - void (*hide) ( void ); - void (*reload) ( void ); - void (*switch_mode) ( RofiViewState *state, Mode *mode ); - void (*set_overlay) ( RofiViewState *state, const char *text ); - void (*clear_input) ( RofiViewState *state ); - void (*__create_window) ( MenuFlags menu_flags ); - xcb_window_t (*get_window) ( void ); - void (*workers_initialize) ( void ); - void (*workers_finalize) ( void ); - void (*get_current_monitor) ( int *width, int *height ); - void (*capture_screenshot) ( void ); - - void (*set_size) ( RofiViewState * state, gint width, gint height ); - void (*get_size) ( RofiViewState * state, gint *width, gint *height ); -} view_proxy; +struct _view_proxy; /* Implementations */ -extern const view_proxy *xcb_view_proxy; +extern const struct _view_proxy *xcb_view_proxy; #ifdef ENABLE_WAYLAND -extern const view_proxy *wayland_view_proxy; +extern const struct _view_proxy *wayland_view_proxy; #endif -void view_init( const view_proxy *view_in ); +void view_init( const struct _view_proxy *view_in ); #endif diff --git a/source/display.c b/source/display.c index 9fe70226..aa6865f6 100644 --- a/source/display.c +++ b/source/display.c @@ -1,7 +1,9 @@ #include #include #include "keyb.h" + #include "display.h" +#include "display-internal.h" #include "view.h" #include "view-internal.h" diff --git a/source/rofi.c b/source/rofi.c index 0f252773..5f3dda6f 100644 --- a/source/rofi.c +++ b/source/rofi.c @@ -858,7 +858,7 @@ int main ( int argc, char *argv[] ) TICK_N ( "Setup Locale" ); - const display_proxy *proxy = xcb_proxy; + const struct _display_proxy *proxy = xcb_proxy; config.backend = DISPLAY_XCB; #ifdef ENABLE_WAYLAND if ( find_arg ( "-x11" ) < 0 ) { diff --git a/source/view.c b/source/view.c index 547b38b8..aa2423cd 100644 --- a/source/view.c +++ b/source/view.c @@ -1,3 +1,33 @@ +/* + * rofi + * + * MIT/X11 License + * Copyright © 2013-2020 Qball Cow + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/** The Rofi View log domain */ +#define G_LOG_DOMAIN "View" + #include #include #include @@ -11,15 +41,30 @@ #include #include -#include "keyb.h" -#include "display.h" -#include "settings.h" +/** Indicated we understand the startup notification api is not yet stable.*/ +#define SN_API_NOT_YET_FROZEN +#include +#include "rofi.h" + #include "timings.h" +#include "settings.h" + +#include "mode.h" +#include "display.h" +#include "xcb-internal.h" +#include "helper.h" +#include "helper-theme.h" +#include "xrmoptions.h" +#include "dialogs/dialogs.h" #include "view.h" #include "view-internal.h" +#include "theme.h" + +#include "xcb.h" + static const view_proxy *proxy; void view_init( const view_proxy *view_in ) @@ -27,72 +72,70 @@ void view_init( const view_proxy *view_in ) proxy = view_in; } +/** Thread pool used for filtering */ GThreadPool *tpool = NULL; -RofiViewState *rofi_view_create ( Mode *sw, const char *input, MenuFlags menu_flags, void ( *finalize )( RofiViewState * ) ) { - return proxy->create ( sw, input, menu_flags, finalize ); +/** Global pointer to the currently active RofiViewState */ +RofiViewState *current_active_menu = NULL; + +struct _rofi_view_cache_state CacheState = { + .main_window = XCB_WINDOW_NONE, + .flags = MENU_NORMAL, + .views = G_QUEUE_INIT, +}; + +static char * get_matching_state ( void ) +{ + if ( config.case_sensitive ) { + if ( config.sort ) { + return "±"; + } + else { + return "-"; + } + } + else{ + if ( config.sort ) { + return "+"; + } + } + return " "; } -void rofi_view_finalize ( RofiViewState *state ) { - if ( state && state->finalize != NULL ) { - state->finalize ( state ); +/** + * Levenshtein Sorting. + */ +static int lev_sort ( const void *p1, const void *p2, void *arg ) +{ + const int *a = p1; + const int *b = p2; + int *distances = arg; + + return distances[*a] - distances[*b]; +} + +static void rofi_view_update_prompt ( RofiViewState *state ) +{ + if ( state->prompt ) { + const char *str = mode_get_display_name ( state->sw ); + textbox_text ( state->prompt, str ); } } -MenuReturn rofi_view_get_return_value ( const RofiViewState *state ) { - return state->retv; -} - -unsigned int rofi_view_get_next_position ( const RofiViewState *state ) { - return state->selected_line; -} - -void rofi_view_handle_text ( RofiViewState *state, char *text ) { - proxy->handle_text ( state, text ); -} - -void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y ) { - state->mouse.x = x; - state->mouse.y = y; - if ( state->mouse.motion_target != NULL ) { - widget_xy_to_relative ( state->mouse.motion_target, &x, &y ); - widget_motion_notify ( state->mouse.motion_target, x, y ); +static void rofi_view_reload_message_bar ( RofiViewState *state ) +{ + if ( state->mesg_box == NULL ) { + return; } -} - -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 ); -} - -void rofi_view_temp_click_to_exit ( RofiViewState *state, xcb_window_t target ) { - proxy->temp_click_to_exit ( state, target ); -} - -void rofi_view_frame_callback ( void ) { - proxy->frame_callback ( ); -} - -unsigned int rofi_view_get_completed ( const RofiViewState *state ) { - return state->quit; -} - -const char * rofi_view_get_user_input ( const RofiViewState *state ) { - if ( state->text ) { - return state->text->text; + char *msg = mode_get_message ( state->sw ); + if ( msg ) { + textbox_text ( state->mesg_tb, msg ); + widget_enable ( WIDGET ( state->mesg_box ) ); + g_free ( msg ); + } + else { + widget_disable ( WIDGET ( state->mesg_box ) ); } - return NULL; -} - -void rofi_view_set_selected_line ( RofiViewState *state, unsigned int selected_line ) { - proxy->set_selected_line ( state, selected_line ); -} - -unsigned int rofi_view_get_selected_line ( const RofiViewState *state ) { - return state->selected_line; } void rofi_view_restart ( RofiViewState *state ) { @@ -100,38 +143,113 @@ void rofi_view_restart ( RofiViewState *state ) { state->retv = MENU_CANCEL; } -gboolean rofi_view_trigger_action ( RofiViewState *state, BindingsScope scope, guint action ) { - return proxy->trigger_action ( state, scope, action ); -} - -void rofi_view_free ( RofiViewState *state ) { - return proxy->free ( state ); -} - -RofiViewState * rofi_view_get_active ( void ) { - return proxy->get_active ( ); -} - -void rofi_view_set_active ( RofiViewState *state ) { - proxy->set_active ( state ); -} - -int rofi_view_error_dialog ( const char *msg, int markup ) { - return proxy->error_dialog ( msg, markup ); -} - -void rofi_view_queue_redraw ( void ) { - proxy->queue_redraw ( ); -} - -void rofi_view_calculate_window_position ( RofiViewState * state ) +RofiViewState * rofi_view_get_active ( void ) { - proxy->calculate_window_position ( state ); + return current_active_menu; } -void rofi_view_window_update_size ( RofiViewState * state ) +void rofi_view_set_active ( RofiViewState *state ) { - proxy->window_update_size ( state ); + if ( current_active_menu != NULL && state != NULL ) { + g_queue_push_head ( &( CacheState.views ), current_active_menu ); + // TODO check. + current_active_menu = state; + g_debug ( "stack view." ); + rofi_view_window_update_size ( current_active_menu ); + rofi_view_queue_redraw (); + return; + } + else if ( state == NULL && !g_queue_is_empty ( &( CacheState.views ) ) ) { + g_debug ( "pop view." ); + current_active_menu = g_queue_pop_head ( &( CacheState.views ) ); + rofi_view_window_update_size ( current_active_menu ); + rofi_view_queue_redraw (); + return; + } + g_assert ( ( current_active_menu == NULL && state != NULL ) || ( current_active_menu != NULL && state == NULL ) ); + current_active_menu = state; + rofi_view_queue_redraw (); +} + +void rofi_view_set_selected_line ( RofiViewState *state, unsigned int selected_line ) +{ + state->selected_line = selected_line; + // Find the line. + unsigned int selected = 0; + for ( unsigned int i = 0; ( ( state->selected_line ) ) < UINT32_MAX && !selected && i < state->filtered_lines; i++ ) { + if ( state->line_map[i] == ( state->selected_line ) ) { + selected = i; + break; + } + } + listview_set_selected ( state->list_view, selected ); + + if ( config.backend == DISPLAY_XCB ) { + xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 ); + xcb_flush ( xcb->connection ); + } +} + +void rofi_view_free ( RofiViewState *state ) +{ + if ( state->tokens ) { + helper_tokenize_free ( state->tokens ); + state->tokens = NULL; + } + // Do this here? + // Wait for final release? + widget_free ( WIDGET ( state->main_window ) ); + + g_free ( state->line_map ); + g_free ( state->distance ); + // Free the switcher boxes. + // When state is free'ed we should no longer need these. + g_free ( state->modi ); + state->num_modi = 0; + g_free ( state ); +} + +MenuReturn rofi_view_get_return_value ( const RofiViewState *state ) +{ + return state->retv; +} + +unsigned int rofi_view_get_selected_line ( const RofiViewState *state ) +{ + return state->selected_line; +} + +unsigned int rofi_view_get_next_position ( const RofiViewState *state ) +{ + unsigned int next_pos = state->selected_line; + unsigned int selected = listview_get_selected ( state->list_view ); + if ( ( selected + 1 ) < state->num_lines ) { + ( next_pos ) = state->line_map[selected + 1]; + } + return next_pos; +} + +unsigned int rofi_view_get_completed ( const RofiViewState *state ) +{ + return state->quit; +} + +const char * rofi_view_get_user_input ( const RofiViewState *state ) +{ + if ( state->text ) { + return state->text->text; + } + return NULL; +} + +/** + * Create a new, 0 initialized RofiViewState structure. + * + * @returns a new 0 initialized RofiViewState + */ +static RofiViewState * __rofi_view_state_create ( void ) +{ + return g_malloc0 ( sizeof ( RofiViewState ) ); } /** @@ -163,6 +281,17 @@ typedef struct _thread_state_view /** Length of pattern. */ glong plen; } thread_state_view; +/** + * @param data A thread_state object. + * @param user_data User data to pass to thread_state callback + * + * Small wrapper function that is internally used to pass a job to a worker. + */ +static void rofi_view_call_thread ( gpointer data, gpointer user_data ) +{ + thread_state *t = (thread_state *) data; + t->callback ( t, user_data ); +} static void filter_elements ( thread_state *ts, G_GNUC_UNUSED gpointer user_data ) { @@ -200,35 +329,125 @@ static void filter_elements ( thread_state *ts, G_GNUC_UNUSED gpointer user_data } /** - * Levenshtein Sorting. + * Nav helper functions, to avoid duplicate code. */ -static int lev_sort ( const void *p1, const void *p2, void *arg ) -{ - const int *a = p1; - const int *b = p2; - int *distances = arg; - return distances[*a] - distances[*b]; -} - -int rofi_view_calculate_window_height ( RofiViewState *state ) +/** + * @param state The current RofiViewState + * + * Tab handling. + */ +static void rofi_view_nav_row_tab ( RofiViewState *state ) { - return proxy->calculate_window_height ( state ); -} - -void rofi_view_reload_message_bar ( RofiViewState *state ) -{ - if ( state->mesg_box == NULL ) { + if ( state->filtered_lines == 1 ) { + state->retv = MENU_OK; + ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; + state->quit = 1; return; } - char *msg = mode_get_message ( state->sw ); - if ( msg ) { - textbox_text ( state->mesg_tb, msg ); - widget_enable ( WIDGET ( state->mesg_box ) ); - g_free ( msg ); + + // Double tab! + if ( state->filtered_lines == 0 && ROW_TAB == state->prev_action ) { + state->retv = MENU_NEXT; + ( state->selected_line ) = 0; + state->quit = TRUE; } else { - widget_disable ( WIDGET ( state->mesg_box ) ); + listview_nav_down ( state->list_view ); + } + state->prev_action = ROW_TAB; +} +/** + * @param state The current RofiViewState + * + * complete current row. + */ +inline static void rofi_view_nav_row_select ( RofiViewState *state ) +{ + if ( state->list_view == NULL ) { + return; + } + unsigned int selected = listview_get_selected ( state->list_view ); + // If a valid item is selected, return that.. + if ( selected < state->filtered_lines ) { + char *str = mode_get_completion ( state->sw, state->line_map[selected] ); + textbox_text ( state->text, str ); + g_free ( str ); + textbox_keybinding ( state->text, MOVE_END ); + state->refilter = TRUE; + } +} + +/** + * @param state The current RofiViewState + * + * Move the selection to first row. + */ +inline static void rofi_view_nav_first ( RofiViewState * state ) +{ +// state->selected = 0; + listview_set_selected ( state->list_view, 0 ); +} + +/** + * @param state The current RofiViewState + * + * Move the selection to last row. + */ +inline static void rofi_view_nav_last ( RofiViewState * state ) +{ + // If no lines, do nothing. + if ( state->filtered_lines == 0 ) { + return; + } + //state->selected = state->filtered_lines - 1; + listview_set_selected ( state->list_view, -1 ); +} + +static void update_callback ( textbox *t, icon *ico, unsigned int index, void *udata, TextBoxFontType *type, gboolean full ) +{ + RofiViewState *state = (RofiViewState *) udata; + if ( full ) { + GList *add_list = NULL; + int fstate = 0; + char *text = mode_get_display_value ( state->sw, state->line_map[index], &fstate, &add_list, TRUE ); + ( *type ) |= fstate; + // TODO needed for markup. + textbox_font ( t, *type ); + // Move into list view. + textbox_text ( t, text ); + PangoAttrList *list = textbox_get_pango_attributes ( t ); + if ( list != NULL ) { + pango_attr_list_ref ( list ); + } + else{ + list = pango_attr_list_new (); + } + if ( ico ) { + int icon_height = widget_get_desired_height ( WIDGET ( ico ) ); + cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[index], icon_height ); + icon_set_surface ( ico, icon ); + } + + if ( state->tokens && config.show_match ) { + RofiHighlightColorStyle th = { ROFI_HL_BOLD | ROFI_HL_UNDERLINE, { 0.0, 0.0, 0.0, 0.0 } }; + th = rofi_theme_get_highlight ( WIDGET ( t ), "highlight", th ); + helper_token_match_get_pango_attr ( th, state->tokens, textbox_get_visible_text ( t ), list ); + } + for ( GList *iter = g_list_first ( add_list ); iter != NULL; iter = g_list_next ( iter ) ) { + pango_attr_list_insert ( list, (PangoAttribute *) ( iter->data ) ); + } + textbox_set_pango_attributes ( t, list ); + pango_attr_list_unref ( list ); + g_list_free ( add_list ); + g_free ( text ); + } + else { + int fstate = 0; + mode_get_display_value ( state->sw, state->line_map[index], &fstate, NULL, FALSE ); + ( *type ) |= fstate; + // TODO needed for markup. + textbox_font ( t, *type ); } } @@ -243,7 +462,7 @@ static void _rofi_view_reload_row ( RofiViewState *state ) rofi_view_reload_message_bar ( state ); } -void rofi_view_refilter ( struct RofiViewState *state ) +void rofi_view_refilter ( RofiViewState *state ) { TICK_N ( "Filter start" ); if ( state->reload ) { @@ -355,56 +574,671 @@ void rofi_view_refilter ( struct RofiViewState *state ) state->refilter = FALSE; TICK_N ( "Filter done" ); } - -void rofi_view_cleanup ( void ) { - proxy->cleanup ( ); -} - -Mode * rofi_view_get_mode ( RofiViewState *state ) { - return proxy->get_mode ( state ); -} - -void rofi_view_hide ( void ) { - proxy->hide ( ); -} - -void rofi_view_reload ( void ) { - proxy->reload ( ); -} - -void rofi_view_switch_mode ( RofiViewState *state, Mode *mode ) { - proxy->switch_mode ( state, mode ); -} - -void rofi_view_set_overlay ( RofiViewState *state, const char *text ) { - proxy->set_overlay ( state, text ); -} - -void rofi_view_clear_input ( RofiViewState *state ) { - proxy->clear_input ( state ); -} - -void __create_window ( MenuFlags menu_flags ) { - proxy->__create_window ( menu_flags ); -} - -xcb_window_t rofi_view_get_window ( void ) { - return proxy->get_window ( ); -} - /** - * @param data A thread_state object. - * @param user_data User data to pass to thread_state callback + * @param state The Menu Handle * - * Small wrapper function that is internally used to pass a job to a worker. + * Check if a finalize function is set, and if sets executes it. */ -void rofi_view_call_thread ( gpointer data, gpointer user_data ) +void process_result ( RofiViewState *state ); +void rofi_view_finalize ( RofiViewState *state ) { - thread_state *t = (thread_state *) data; - t->callback ( t, user_data ); + if ( state && state->finalize != NULL ) { + state->finalize ( state ); + } } -void rofi_view_workers_initialize ( void ) { +static void rofi_view_trigger_global_action ( KeyBindingAction action ) +{ + RofiViewState *state = rofi_view_get_active (); + switch ( action ) + { + // Handling of paste + case PASTE_PRIMARY: + if ( config.backend == DISPLAY_XCB ) { + 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 ); + } + break; + case PASTE_SECONDARY: + if ( config.backend == DISPLAY_XCB ) { + xcb_convert_selection ( xcb->connection, CacheState.main_window, netatoms[CLIPBOARD], + xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME ); + xcb_flush ( xcb->connection ); + } + break; + case SCREENSHOT: + rofi_capture_screenshot ( ); + break; + case CHANGE_ELLIPSIZE: + if ( state->list_view ) { + listview_toggle_ellipsizing ( state->list_view ); + } + break; + case TOGGLE_SORT: + if ( state->case_indicator != NULL ) { + config.sort = !config.sort; + state->refilter = TRUE; + textbox_text ( state->case_indicator, get_matching_state () ); + } + break; + case MODE_PREVIOUS: + state->retv = MENU_PREVIOUS; + ( state->selected_line ) = 0; + state->quit = TRUE; + break; + // Menu navigation. + case MODE_NEXT: + state->retv = MENU_NEXT; + ( state->selected_line ) = 0; + state->quit = TRUE; + break; + // Toggle case sensitivity. + case TOGGLE_CASE_SENSITIVITY: + if ( state->case_indicator != NULL ) { + config.case_sensitive = !config.case_sensitive; + ( state->selected_line ) = 0; + state->refilter = TRUE; + textbox_text ( state->case_indicator, get_matching_state () ); + } + break; + // Special delete entry command. + case DELETE_ENTRY: + { + unsigned int selected = listview_get_selected ( state->list_view ); + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; + state->retv = MENU_ENTRY_DELETE; + state->quit = TRUE; + } + break; + } + case SELECT_ELEMENT_1: + case SELECT_ELEMENT_2: + case SELECT_ELEMENT_3: + case SELECT_ELEMENT_4: + case SELECT_ELEMENT_5: + case SELECT_ELEMENT_6: + case SELECT_ELEMENT_7: + case SELECT_ELEMENT_8: + case SELECT_ELEMENT_9: + case SELECT_ELEMENT_10: + { + unsigned int index = action - SELECT_ELEMENT_1; + if ( index < state->filtered_lines ) { + state->selected_line = state->line_map[index]; + state->retv = MENU_OK; + state->quit = TRUE; + } + 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; + unsigned int selected = listview_get_selected ( state->list_view ); + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; + } + state->retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK ); + state->quit = TRUE; + break; + } + // If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too + case CANCEL: + state->retv = MENU_CANCEL; + state->quit = TRUE; + break; + case ROW_UP: + listview_nav_up ( state->list_view ); + break; + case ROW_TAB: + rofi_view_nav_row_tab ( state ); + break; + case ROW_DOWN: + listview_nav_down ( state->list_view ); + break; + case ROW_LEFT: + listview_nav_left ( state->list_view ); + break; + case ROW_RIGHT: + listview_nav_right ( state->list_view ); + break; + case PAGE_PREV: + listview_nav_page_prev ( state->list_view ); + break; + case PAGE_NEXT: + listview_nav_page_next ( state->list_view ); + break; + case ROW_FIRST: + rofi_view_nav_first ( state ); + break; + case ROW_LAST: + rofi_view_nav_last ( state ); + break; + case ROW_SELECT: + rofi_view_nav_row_select ( state ); + break; + // If you add a binding here, make sure to add it to textbox_keybinding too + case MOVE_CHAR_BACK: + { + if ( textbox_keybinding ( state->text, action ) == 0 ) { + listview_nav_left ( state->list_view ); + } + break; + } + case MOVE_CHAR_FORWARD: + { + if ( textbox_keybinding ( state->text, action ) == 0 ) { + listview_nav_right ( state->list_view ); + } + break; + } + case CLEAR_LINE: + case MOVE_FRONT: + case MOVE_END: + case REMOVE_TO_EOL: + case REMOVE_TO_SOL: + case REMOVE_WORD_BACK: + case REMOVE_WORD_FORWARD: + case REMOVE_CHAR_FORWARD: + case MOVE_WORD_BACK: + case MOVE_WORD_FORWARD: + case REMOVE_CHAR_BACK: + { + int rc = textbox_keybinding ( state->text, action ); + if ( rc == 1 ) { + // Entry changed. + state->refilter = TRUE; + } + else if ( rc == 2 ) { + // Movement. + } + break; + } + case ACCEPT_ALT: + { + unsigned int selected = listview_get_selected ( state->list_view ); + state->selected_line = UINT32_MAX; + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; + state->retv = MENU_OK; + } + else { + // Nothing entered and nothing selected. + state->retv = MENU_CUSTOM_INPUT; + } + state->retv |= MENU_CUSTOM_ACTION; + state->quit = TRUE; + break; + } + case ACCEPT_CUSTOM: + { + state->selected_line = UINT32_MAX; + state->retv = MENU_CUSTOM_INPUT; + state->quit = TRUE; + break; + } + case ACCEPT_ENTRY: + { + // If a valid item is selected, return that.. + unsigned int selected = listview_get_selected ( state->list_view ); + state->selected_line = UINT32_MAX; + if ( selected < state->filtered_lines ) { + ( state->selected_line ) = state->line_map[selected]; + state->retv = MENU_OK; + } + else { + // Nothing entered and nothing selected. + state->retv = MENU_CUSTOM_INPUT; + } + + state->quit = TRUE; + break; + } + } +} + +gboolean rofi_view_trigger_action ( RofiViewState *state, BindingsScope scope, guint action ) +{ + switch ( scope ) + { + case SCOPE_GLOBAL: + rofi_view_trigger_global_action ( action ); + return TRUE; + case SCOPE_MOUSE_LISTVIEW: + case SCOPE_MOUSE_LISTVIEW_ELEMENT: + case SCOPE_MOUSE_EDITBOX: + case SCOPE_MOUSE_SCROLLBAR: + case SCOPE_MOUSE_MODE_SWITCHER: + { + gint x = state->mouse.x, y = state->mouse.y; + widget *target = widget_find_mouse_target ( WIDGET ( state->main_window ), scope, x, y ); + if ( target == NULL ) { + return FALSE; + } + widget_xy_to_relative ( target, &x, &y ); + switch ( widget_trigger_action ( target, action, x, y ) ) + { + case WIDGET_TRIGGER_ACTION_RESULT_IGNORED: + return FALSE; + case WIDGET_TRIGGER_ACTION_RESULT_GRAB_MOTION_END: + target = NULL; + /* FALLTHRU */ + case WIDGET_TRIGGER_ACTION_RESULT_GRAB_MOTION_BEGIN: + state->mouse.motion_target = target; + /* FALLTHRU */ + case WIDGET_TRIGGER_ACTION_RESULT_HANDLED: + return TRUE; + } + break; + } + } + return FALSE; +} + +void rofi_view_handle_text ( RofiViewState *state, char *text ) +{ + if ( textbox_append_text ( state->text, text, strlen ( text ) ) ) { + state->refilter = TRUE; + } +} + +void rofi_view_handle_mouse_motion ( RofiViewState *state, gint x, gint y ) +{ + state->mouse.x = x; + state->mouse.y = y; + if ( state->mouse.motion_target != NULL ) { + widget_xy_to_relative ( state->mouse.motion_target, &x, &y ); + widget_motion_notify ( state->mouse.motion_target, x, y ); + } +} + +static 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 ) +{ + RofiViewState *state = ( RofiViewState *) user_data; + switch ( action ) + { + case MOUSE_CLICK_DOWN: + { + const char * type = rofi_theme_get_string ( wid, "action", "ok" ); + ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; + if ( strcmp ( type, "ok" ) == 0 ) { + state->retv = MENU_OK; + } + else if ( strcmp ( type, "ok|alternate" ) == 0 ) { + state->retv = MENU_CUSTOM_ACTION | MENU_OK; + } + else if ( strcmp ( type, "custom" ) ) { + state->retv = MENU_CUSTOM_INPUT; + } + else if ( strcmp ( type, "custom|alternate" ) == 0 ) { + state->retv = MENU_CUSTOM_ACTION | MENU_CUSTOM_INPUT; + } + else { + g_warning ( "Invalid action specified." ); + return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; + } + state->quit = TRUE; + state->skip_absorb = TRUE; + return WIDGET_TRIGGER_ACTION_RESULT_HANDLED; + } + case MOUSE_CLICK_UP: + case MOUSE_DCLICK_DOWN: + case MOUSE_DCLICK_UP: + break; + } + return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; +} +static WidgetTriggerActionResult textbox_sidebar_modi_trigger_action ( widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data ) +{ + RofiViewState *state = ( RofiViewState *) user_data; + unsigned int i; + for ( i = 0; i < state->num_modi; i++ ) { + if ( WIDGET ( state->modi[i] ) == wid ) { + break; + } + } + if ( i == state->num_modi ) { + return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; + } + + switch ( action ) + { + case MOUSE_CLICK_DOWN: + state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK ); + state->quit = TRUE; + state->skip_absorb = TRUE; + return WIDGET_TRIGGER_ACTION_RESULT_HANDLED; + case MOUSE_CLICK_UP: + case MOUSE_DCLICK_DOWN: + case MOUSE_DCLICK_UP: + break; + } + return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; +} + +// @TODO don't like this construction. +static void rofi_view_listview_mouse_activated_cb ( listview *lv, gboolean custom, void *udata ) +{ + RofiViewState *state = (RofiViewState *) udata; + state->retv = MENU_OK; + if ( custom ) { + state->retv |= MENU_CUSTOM_ACTION; + } + ( state->selected_line ) = state->line_map[listview_get_selected ( lv )]; + // Quit + state->quit = TRUE; + state->skip_absorb = TRUE; +} + +static void rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, const char *name ) +{ + char *defaults = NULL; + widget *wid = NULL; + + /** + * MAINBOX + */ + if ( strcmp ( name, "mainbox" ) == 0 ) { + wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); + box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); + if ( config.sidebar_mode ) { + defaults = "inputbar,message,listview,mode-switcher"; + } + else { + defaults = "inputbar,message,listview"; + } + } + /** + * INPUTBAR + */ + else if ( strcmp ( name, "inputbar" ) == 0 ) { + wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL ); + defaults = "prompt,entry,overlay,case-indicator"; + box_add ( (box *) parent_widget, WIDGET ( wid ), FALSE ); + } + /** + * PROMPT + */ + else if ( strcmp ( name, "prompt" ) == 0 ) { + if ( state->prompt != NULL ) { + g_error ( "Prompt widget can only be added once to the layout." ); + return; + } + // Prompt box. + state->prompt = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); + rofi_view_update_prompt ( state ); + box_add ( (box *) parent_widget, WIDGET ( state->prompt ), FALSE ); + defaults = NULL; + } + else if ( strcmp ( name, "num-rows" ) == 0 ) { + state->tb_total_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); + box_add ( (box *) parent_widget, WIDGET ( state->tb_total_rows ), FALSE ); + defaults = NULL; + } + else if ( strcmp ( name, "num-filtered-rows" ) == 0 ) { + state->tb_filtered_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); + box_add ( (box *) parent_widget, WIDGET ( state->tb_filtered_rows ), FALSE ); + defaults = NULL; + } + /** + * CASE INDICATOR + */ + else if ( strcmp ( name, "case-indicator" ) == 0 ) { + if ( state->case_indicator != NULL ) { + g_error ( "Case indicator widget can only be added once to the layout." ); + return; + } + state->case_indicator = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, 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 () ); + } + /** + * ENTRY BOX + */ + else if ( strcmp ( name, "entry" ) == 0 ) { + if ( state->text != NULL ) { + g_error ( "Entry textbox widget can only be added once to the layout." ); + return; + } + // Entry box + TextboxFlags tfl = TB_EDITABLE; + tfl |= ( ( state->menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0; + state->text = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, tfl | TB_AUTOHEIGHT, NORMAL, NULL, 0, 0 ); + box_add ( (box *) parent_widget, WIDGET ( state->text ), TRUE ); + } + /** + * MESSAGE + */ + else if ( strcmp ( name, "message" ) == 0 ) { + if ( state->mesg_box != NULL ) { + g_error ( "Message widget can only be added once to the layout." ); + return; + } + state->mesg_box = container_create ( parent_widget, name ); + state->mesg_tb = textbox_create ( WIDGET ( state->mesg_box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL, 0, 0 ); + container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) ); + rofi_view_reload_message_bar ( state ); + box_add ( (box *) parent_widget, WIDGET ( state->mesg_box ), FALSE ); + } + /** + * LISTVIEW + */ + else if ( strcmp ( name, "listview" ) == 0 ) { + if ( state->list_view != NULL ) { + g_error ( "Listview widget can only be added once to the layout." ); + return; + } + state->list_view = listview_create ( parent_widget, name, update_callback, state, config.element_height, 0 ); + box_add ( (box *) parent_widget, WIDGET ( state->list_view ), TRUE ); + // Set configuration + listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR ); + listview_set_scroll_type ( state->list_view, config.scroll_method ); + listview_set_mouse_activated_cb ( state->list_view, rofi_view_listview_mouse_activated_cb, state ); + + int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines ); + listview_set_num_lines ( state->list_view, lines ); + listview_set_max_lines ( state->list_view, state->num_lines ); + } + /** + * MODE SWITCHER + */ + else if ( strcmp ( name, "mode-switcher" ) == 0 || strcmp ( name, "sidebar" ) == 0 ) { + if ( state->sidebar_bar != NULL ) { + g_error ( "Mode-switcher can only be added once to the layout." ); + return; + } + state->sidebar_bar = box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL ); + box_add ( (box *) parent_widget, WIDGET ( state->sidebar_bar ), FALSE ); + state->num_modi = rofi_get_num_enabled_modi (); + state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) ); + for ( unsigned int j = 0; j < state->num_modi; j++ ) { + const Mode * mode = rofi_get_mode ( j ); + state->modi[j] = textbox_create ( WIDGET ( state->sidebar_bar ), WIDGET_TYPE_MODE_SWITCHER, "button", TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL, + mode_get_display_name ( mode ), 0.5, 0.5 ); + box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE ); + widget_set_trigger_action_handler ( WIDGET ( state->modi[j] ), textbox_sidebar_modi_trigger_action, state ); + } + } + else if ( g_ascii_strcasecmp ( name, "overlay" ) == 0 ) { + state->overlay = textbox_create ( WIDGET ( parent_widget ), WIDGET_TYPE_TEXTBOX_TEXT, "overlay", TB_AUTOWIDTH | TB_AUTOHEIGHT, URGENT, "blaat", 0.5, 0 ); + box_add ( (box *) parent_widget, WIDGET ( state->overlay ), FALSE ); + widget_disable ( WIDGET ( state->overlay ) ); + } + else if ( g_ascii_strncasecmp ( name, "textbox", 7 ) == 0 ) { + textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 ); + box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); + } + else if ( g_ascii_strncasecmp ( name, "button", 6 ) == 0 ) { + textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 ); + box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); + widget_set_trigger_action_handler ( WIDGET ( t ), textbox_button_trigger_action, state ); + } + else if ( g_ascii_strncasecmp ( name, "icon", 4 ) == 0 ) { + icon *t = icon_create ( parent_widget, name ); + box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); + } + else { + wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); + box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); + //g_error("The widget %s does not exists. Invalid layout.", name); + } + if ( wid ) { + GList *list = rofi_theme_get_list ( wid, "children", defaults ); + for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) { + rofi_view_add_widget ( state, wid, (const char *) iter->data ); + } + g_list_free_full ( list, g_free ); + } +} + +RofiViewState *rofi_view_create ( Mode *sw, + const char *input, + MenuFlags menu_flags, + void ( *finalize )( RofiViewState * ) ) +{ + TICK (); + RofiViewState *state = __rofi_view_state_create (); + state->menu_flags = menu_flags; + state->sw = sw; + state->selected_line = UINT32_MAX; + state->retv = MENU_CANCEL; + state->distance = NULL; + state->quit = FALSE; + state->skip_absorb = FALSE; + //We want to filter on the first run. + state->refilter = TRUE; + state->finalize = finalize; + state->mouse_seen = FALSE; + + // Request the lines to show. + state->num_lines = mode_get_num_entries ( sw ); + + if ( state->sw ) { + char * title = g_strdup_printf ( "rofi - %s", mode_get_display_name ( state->sw ) ); + rofi_view_set_window_title ( title ); + g_free ( title ); + } + else { + rofi_view_set_window_title ( "rofi" ); + } + TICK_N ( "Startup notification" ); + + // Get active monitor size. + TICK_N ( "Get active monitor" ); + + state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL ); + // Get children. + GList *list = rofi_theme_get_list ( WIDGET ( state->main_window ), "children", "mainbox" ); + for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) { + rofi_view_add_widget ( state, WIDGET ( state->main_window ), (const char *) iter->data ); + } + g_list_free_full ( list, g_free ); + + if ( state->text && input ) { + textbox_text ( state->text, input ); + textbox_cursor_end ( state->text ); + } + + // filtered list + state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) ); + state->distance = (int *) g_malloc0_n ( state->num_lines, sizeof ( int ) ); + + rofi_view_calculate_window_width ( state ); + // Need to resize otherwise calculated desired height is wrong. + widget_resize ( WIDGET ( state->main_window ), state->width, 100 ); + // Only needed when window is fixed size. + if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) { + listview_set_fixed_num_lines ( state->list_view ); + } + + state->height = rofi_view_calculate_window_height ( state ); + // Move the window to the correct x,y position. + rofi_view_calculate_window_position ( state ); + rofi_view_window_update_size ( state ); + + state->quit = FALSE; + rofi_view_refilter ( state ); + rofi_view_update ( state, TRUE ); + if ( xcb->connection ) { + xcb_map_window ( xcb->connection, CacheState.main_window ); + } + widget_queue_redraw ( WIDGET ( state->main_window ) ); + if ( xcb->connection ) { + xcb_flush ( xcb->connection ); + } + if ( xcb->sncontext != NULL ) { + sn_launchee_context_complete ( xcb->sncontext ); + } + return state; +} + +int rofi_view_error_dialog ( const char *msg, int markup ) +{ + RofiViewState *state = __rofi_view_state_create (); + state->retv = MENU_CANCEL; + state->menu_flags = MENU_ERROR_DIALOG; + state->finalize = process_result; + + state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL ); + box *box = box_create ( WIDGET ( state->main_window ), "error-message", ROFI_ORIENTATION_VERTICAL ); + box_add ( state->main_window, WIDGET ( box ), TRUE ); + state->text = textbox_create ( WIDGET ( box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ), + NORMAL, ( msg != NULL ) ? msg : "", 0, 0 ); + box_add ( box, WIDGET ( state->text ), TRUE ); + + // Make sure we enable fixed num lines when in normal window mode. + if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) { + listview_set_fixed_num_lines ( state->list_view ); + } + rofi_view_calculate_window_width ( state ); + // Need to resize otherwise calculated desired height is wrong. + widget_resize ( WIDGET ( state->main_window ), state->width, 100 ); + // resize window vertically to suit + state->height = widget_get_desired_height ( WIDGET ( state->main_window ) ); + + // Calculte window position. + rofi_view_calculate_window_position ( state ); + + // Move the window to the correct x,y position. + rofi_view_window_update_size ( state ); + + // Display it. + if ( config.backend == DISPLAY_XCB ) { + xcb_map_window ( xcb->connection, CacheState.main_window ); + } + widget_queue_redraw ( WIDGET ( state->main_window ) ); + + if ( xcb->sncontext != NULL ) { + sn_launchee_context_complete ( xcb->sncontext ); + } + + // Set it as current window. + rofi_view_set_active ( state ); + return TRUE; +} + +void rofi_view_workers_initialize ( void ) +{ TICK_N ( "Setup Threadpool, start" ); if ( config.threads == 0 ) { config.threads = 1; @@ -430,13 +1264,142 @@ void rofi_view_workers_initialize ( void ) { } TICK_N ( "Setup Threadpool, done" ); } - -void rofi_view_workers_finalize ( void ) { +void rofi_view_workers_finalize ( void ) +{ if ( tpool ) { g_thread_pool_free ( tpool, TRUE, TRUE ); tpool = NULL; } } +Mode * rofi_view_get_mode ( RofiViewState *state ) +{ + return state->sw; +} + +void rofi_view_set_overlay ( RofiViewState *state, const char *text ) +{ + if ( state->overlay == NULL || state->list_view == NULL ) { + return; + } + if ( text == NULL ) { + widget_disable ( WIDGET ( state->overlay ) ); + return; + } + widget_enable ( WIDGET ( state->overlay ) ); + textbox_text ( state->overlay, text ); + // We want to queue a repaint. + rofi_view_queue_redraw ( ); +} + +void rofi_view_clear_input ( RofiViewState *state ) +{ + if ( state->text ) { + textbox_text ( state->text, "" ); + rofi_view_set_selected_line ( state, 0 ); + } +} + +void rofi_view_ellipsize_start ( RofiViewState *state ) +{ + listview_set_ellipsize_start ( state->list_view ); +} + +void rofi_view_switch_mode ( RofiViewState *state, Mode *mode ) +{ + state->sw = mode; + // Update prompt; + if ( state->prompt ) { + rofi_view_update_prompt ( state ); + } + if ( state->sw ) { + char * title = g_strdup_printf ( "rofi - %s", mode_get_display_name ( state->sw ) ); + rofi_view_set_window_title ( title ); + g_free ( title ); + } + else { + rofi_view_set_window_title ( "rofi" ); + } + if ( state->sidebar_bar ) { + for ( unsigned int j = 0; j < state->num_modi; j++ ) { + const Mode * mode = rofi_get_mode ( j ); + textbox_font ( state->modi[j], ( mode == state->sw ) ? HIGHLIGHT : NORMAL ); + } + } + rofi_view_restart ( state ); + state->reload = TRUE; + state->refilter = TRUE; + rofi_view_refilter ( state ); + rofi_view_update ( state, TRUE ); +} + +/** ------ */ + +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 ); +} + +void rofi_view_temp_click_to_exit ( RofiViewState *state, xcb_window_t target ) { + proxy->temp_click_to_exit ( state, target ); +} + +void rofi_view_frame_callback ( void ) { + proxy->frame_callback ( ); +} + +void rofi_view_queue_redraw ( void ) { + proxy->queue_redraw ( ); +} + +void rofi_view_set_window_title ( const char * title ) { + proxy->set_window_title ( title ); +} + +void rofi_view_calculate_window_position ( RofiViewState * state ) +{ + proxy->calculate_window_position ( state ); +} + +void rofi_view_calculate_window_width ( struct RofiViewState *state ) { + proxy->calculate_window_width ( state ); +} + +int rofi_view_calculate_window_height ( RofiViewState *state ) +{ + return proxy->calculate_window_height ( state ); +} + +void rofi_view_window_update_size ( RofiViewState * state ) +{ + proxy->window_update_size ( state ); +} + +void rofi_view_cleanup ( void ) { + proxy->cleanup ( ); +} + +void rofi_view_hide ( void ) { + proxy->hide ( ); +} + +void rofi_view_reload ( void ) { + proxy->reload ( ); +} + +void __create_window ( MenuFlags menu_flags ) { + proxy->__create_window ( menu_flags ); +} + +xcb_window_t rofi_view_get_window ( void ) { + return proxy->get_window ( ); +} void rofi_view_get_current_monitor ( int *width, int *height ) { proxy->get_current_monitor ( width, height ); @@ -446,10 +1409,6 @@ void rofi_capture_screenshot ( void ) { proxy->capture_screenshot ( ); } -void rofi_view_ellipsize_start ( RofiViewState *state ) { - listview_set_ellipsize_start ( state->list_view ); -} - void rofi_view_set_size ( RofiViewState * state, gint width, gint height ) { proxy->set_size ( state, width, height ); } diff --git a/source/wayland/display.c b/source/wayland/display.c index c7374814..46da2a17 100644 --- a/source/wayland/display.c +++ b/source/wayland/display.c @@ -52,6 +52,7 @@ #include "wayland-internal.h" #include "display.h" +#include "display-internal.h" #include "wlr-layer-shell-unstable-v1-protocol.h" diff --git a/source/wayland/view.c b/source/wayland/view.c index 677d63da..e2613cd1 100644 --- a/source/wayland/view.c +++ b/source/wayland/view.c @@ -69,27 +69,15 @@ */ static void wayland_rofi_view_update ( RofiViewState *state, gboolean qr ); -static int wayland_rofi_view_calculate_window_height ( RofiViewState *state ); - static void wayland_rofi_view_maybe_update ( RofiViewState *state ); -static RofiViewState * wayland_rofi_view_get_active ( void ); - -/** Thread pool used for filtering */ -extern GThreadPool *tpool; - -/** Global pointer to the currently active RofiViewState */ -static RofiViewState *current_active_menu = NULL; - /** - * Structure holding cached state. + * Structure holding some state */ static struct { /** Main flags */ MenuFlags flags; - /** List of stacked views */ - GQueue views; /** timeout for reloading */ guint idle_timeout; /** debug counter for redraws */ @@ -98,9 +86,8 @@ static struct guint repaint_source; /** Window fullscreen */ gboolean fullscreen; -} CacheState = { +} WlState = { .flags = MENU_NORMAL, - .views = G_QUEUE_INIT, .idle_timeout = 0, .count = 0L, .repaint_source = 0, @@ -112,24 +99,6 @@ static void wayland_rofi_view_get_current_monitor ( int *width, int *height ) display_get_surface_dimensions( width, height ); } -static char * get_matching_state ( void ) -{ - if ( config.case_sensitive ) { - if ( config.sort ) { - return "±"; - } - else { - return "-"; - } - } - else{ - if ( config.sort ) { - return "+"; - } - } - return " "; -} - /** * Stores a screenshot of Rofi at that point in time. */ @@ -139,24 +108,17 @@ static void wayland_rofi_view_capture_screenshot ( void ) static gboolean wayland_rofi_view_repaint ( G_GNUC_UNUSED void * data ) { - if ( current_active_menu ) { + RofiViewState * state = rofi_view_get_active(); + if ( state ) { // 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( current_active_menu ); - CacheState.repaint_source = 0; + wayland_rofi_view_maybe_update( state ); + WlState.repaint_source = 0; } return G_SOURCE_REMOVE; } -static void wayland_rofi_view_update_prompt ( RofiViewState *state ) -{ - if ( state->prompt ) { - const char *str = mode_get_display_name ( state->sw ); - textbox_text ( state->prompt, str ); - } -} - static const int loc_transtable[9] = { WL_CENTER, WL_NORTH | WL_WEST, @@ -201,117 +163,38 @@ static void wayland_rofi_view_get_size ( RofiViewState * state, gint *width, gin static gboolean wayland_rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data ) { - RofiViewState *state = wayland_rofi_view_get_active (); + RofiViewState *state = rofi_view_get_active (); - if ( current_active_menu ) { - current_active_menu->reload = TRUE; - current_active_menu->refilter = TRUE; + if ( state ) { + state->reload = TRUE; + state->refilter = TRUE; wayland_rofi_view_maybe_update ( state ); } - CacheState.idle_timeout = 0; + WlState.idle_timeout = 0; return G_SOURCE_REMOVE; } -static void wayland_rofi_view_reload ( void ) +static void wayland_rofi_view_reload ( void ) { // @TODO add check if current view is equal to the callee - if ( CacheState.idle_timeout == 0 ) { - CacheState.idle_timeout = g_timeout_add ( 1000 / 15, wayland_rofi_view_reload_idle, NULL ); + if ( WlState.idle_timeout == 0 ) { + WlState.idle_timeout = g_timeout_add ( 1000 / 15, wayland_rofi_view_reload_idle, NULL ); } } static void wayland_rofi_view_queue_redraw ( void ) { - if ( current_active_menu && CacheState.repaint_source == 0 ) { - CacheState.count++; - g_debug ( "redraw %llu", CacheState.count ); + RofiViewState *state = rofi_view_get_active (); + if ( state && WlState.repaint_source == 0 ) { + WlState.count++; + g_debug ( "redraw %llu", WlState.count ); - widget_queue_redraw ( WIDGET ( current_active_menu->main_window ) ); + widget_queue_redraw ( WIDGET ( state->main_window ) ); - CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, wayland_rofi_view_repaint, NULL, NULL ); + WlState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, wayland_rofi_view_repaint, NULL, NULL ); } } -static RofiViewState * wayland_rofi_view_get_active ( void ) -{ - return current_active_menu; -} - -static void wayland_rofi_view_set_active ( RofiViewState *state ) -{ - if ( current_active_menu != NULL && state != NULL ) { - g_queue_push_head ( &( CacheState.views ), current_active_menu ); - // TODO check. - current_active_menu = state; - g_debug ( "stack view." ); - wayland_rofi_view_window_update_size ( current_active_menu ); - wayland_rofi_view_queue_redraw (); - return; - } - else if ( state == NULL && !g_queue_is_empty ( &( CacheState.views ) ) ) { - g_debug ( "pop view." ); - current_active_menu = g_queue_pop_head ( &( CacheState.views ) ); - wayland_rofi_view_window_update_size ( current_active_menu ); - wayland_rofi_view_queue_redraw (); - return; - } - g_assert ( ( current_active_menu == NULL && state != NULL ) || ( current_active_menu != NULL && state == NULL ) ); - current_active_menu = state; - wayland_rofi_view_queue_redraw (); -} - -static void wayland_rofi_view_set_selected_line ( RofiViewState *state, unsigned int selected_line ) -{ - state->selected_line = selected_line; - // Find the line. - unsigned int selected = 0; - for ( unsigned int i = 0; ( ( state->selected_line ) ) < UINT32_MAX && !selected && i < state->filtered_lines; i++ ) { - if ( state->line_map[i] == ( state->selected_line ) ) { - selected = i; - break; - } - } - listview_set_selected ( state->list_view, selected ); -} - -static void wayland_rofi_view_free ( RofiViewState *state ) -{ - if ( state->tokens ) { - helper_tokenize_free ( state->tokens ); - state->tokens = NULL; - } - // Do this here? - // Wait for final release? - widget_free ( WIDGET ( state->main_window ) ); - - g_free ( state->pool ); - g_free ( state->line_map ); - g_free ( state->distance ); - // Free the switcher boxes. - // When state is free'ed we should no longer need these. - g_free ( state->modi ); - state->num_modi = 0; - g_free ( state ); -} - -static const char * wayland_rofi_view_get_user_input ( const RofiViewState *state ) -{ - if ( state->text ) { - return state->text->text; - } - return NULL; -} - -/** - * Create a new, 0 initialized RofiViewState structure. - * - * @returns a new 0 initialized RofiViewState - */ -static RofiViewState * __rofi_view_state_create ( void ) -{ - return g_malloc0 ( sizeof ( RofiViewState ) ); -} - /** * Thread state for workers started for the view. */ @@ -353,7 +236,7 @@ static void wayland___create_window ( MenuFlags menu_flags ) pango_context_set_font_map(p, pango_cairo_font_map_get_default()); TICK_N ( "pango cairo font setup" ); - CacheState.flags = menu_flags; + WlState.flags = menu_flags; // Setup dpi // FIXME: use scale, cairo_surface_set_device_scale // Setup font. @@ -403,129 +286,6 @@ static void wayland_rofi_view_calculate_window_width ( RofiViewState *state ) state->width = distance_get_pixel ( width, ROFI_ORIENTATION_HORIZONTAL ); } -/** - * Nav helper functions, to avoid duplicate code. - */ - -/** - * @param state The current RofiViewState - * - * Tab handling. - */ -static void wayland_rofi_view_nav_row_tab ( RofiViewState *state ) -{ - if ( state->filtered_lines == 1 ) { - state->retv = MENU_OK; - ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; - state->quit = 1; - return; - } - - // Double tab! - if ( state->filtered_lines == 0 && ROW_TAB == state->prev_action ) { - state->retv = MENU_NEXT; - ( state->selected_line ) = 0; - state->quit = TRUE; - } - else { - listview_nav_down ( state->list_view ); - } - state->prev_action = ROW_TAB; -} -/** - * @param state The current RofiViewState - * - * complete current row. - */ -inline static void wayland_rofi_view_nav_row_select ( RofiViewState *state ) -{ - if ( state->list_view == NULL ) { - return; - } - unsigned int selected = listview_get_selected ( state->list_view ); - // If a valid item is selected, return that.. - if ( selected < state->filtered_lines ) { - char *str = mode_get_completion ( state->sw, state->line_map[selected] ); - textbox_text ( state->text, str ); - g_free ( str ); - textbox_keybinding ( state->text, MOVE_END ); - state->refilter = TRUE; - } -} - -/** - * @param state The current RofiViewState - * - * Move the selection to first row. - */ -inline static void wayland_rofi_view_nav_first ( RofiViewState * state ) -{ -// state->selected = 0; - listview_set_selected ( state->list_view, 0 ); -} - -/** - * @param state The current RofiViewState - * - * Move the selection to last row. - */ -inline static void wayland_rofi_view_nav_last ( RofiViewState * state ) -{ - // If no lines, do nothing. - if ( state->filtered_lines == 0 ) { - return; - } - //state->selected = state->filtered_lines - 1; - listview_set_selected ( state->list_view, -1 ); -} - -static void update_callback ( textbox *t, icon *ico, unsigned int index, void *udata, TextBoxFontType *type, gboolean full ) -{ - RofiViewState *state = (RofiViewState *) udata; - if ( full ) { - GList *add_list = NULL; - int fstate = 0; - char *text = mode_get_display_value ( state->sw, state->line_map[index], &fstate, &add_list, TRUE ); - ( *type ) |= fstate; - // TODO needed for markup. - textbox_font ( t, *type ); - // Move into list view. - textbox_text ( t, text ); - PangoAttrList *list = textbox_get_pango_attributes ( t ); - if ( list != NULL ) { - pango_attr_list_ref ( list ); - } - else{ - list = pango_attr_list_new (); - } - if ( ico ) { - int icon_height = widget_get_desired_height ( WIDGET ( ico ) ); - cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[index], icon_height ); - icon_set_surface ( ico, icon ); - } - - if ( state->tokens && config.show_match ) { - RofiHighlightColorStyle th = { ROFI_HL_BOLD | ROFI_HL_UNDERLINE, { 0.0, 0.0, 0.0, 0.0 } }; - th = rofi_theme_get_highlight ( WIDGET ( t ), "highlight", th ); - helper_token_match_get_pango_attr ( th, state->tokens, textbox_get_visible_text ( t ), list ); - } - for ( GList *iter = g_list_first ( add_list ); iter != NULL; iter = g_list_next ( iter ) ) { - pango_attr_list_insert ( list, (PangoAttribute *) ( iter->data ) ); - } - textbox_set_pango_attributes ( t, list ); - pango_attr_list_unref ( list ); - g_list_free ( add_list ); - g_free ( text ); - } - else { - int fstate = 0; - mode_get_display_value ( state->sw, state->line_map[index], &fstate, NULL, FALSE ); - ( *type ) |= fstate; - // TODO needed for markup. - textbox_font ( t, *type ); - } -} - static void wayland_rofi_view_update ( RofiViewState *state, gboolean qr ) { if ( !widget_need_redraw ( WIDGET ( state->main_window ) ) ) { @@ -564,278 +324,13 @@ static void wayland_rofi_view_update ( RofiViewState *state, gboolean qr ) */ void process_result ( RofiViewState *state ); -static void wayland_rofi_view_trigger_global_action ( KeyBindingAction action ) -{ - RofiViewState *state = wayland_rofi_view_get_active (); - switch ( action ) - { - case PASTE_PRIMARY: - // TODO - break; - case PASTE_SECONDARY: - // TODO - break; - case SCREENSHOT: - rofi_capture_screenshot ( ); - break; - case CHANGE_ELLIPSIZE: - if ( state->list_view ) { - listview_toggle_ellipsizing ( state->list_view ); - } - break; - case TOGGLE_SORT: - if ( state->case_indicator != NULL ) { - config.sort = !config.sort; - state->refilter = TRUE; - textbox_text ( state->case_indicator, get_matching_state () ); - } - break; - case MODE_PREVIOUS: - state->retv = MENU_PREVIOUS; - ( state->selected_line ) = 0; - state->quit = TRUE; - break; - // Menu navigation. - case MODE_NEXT: - state->retv = MENU_NEXT; - ( state->selected_line ) = 0; - state->quit = TRUE; - break; - // Toggle case sensitivity. - case TOGGLE_CASE_SENSITIVITY: - if ( state->case_indicator != NULL ) { - config.case_sensitive = !config.case_sensitive; - ( state->selected_line ) = 0; - state->refilter = TRUE; - textbox_text ( state->case_indicator, get_matching_state () ); - } - break; - // Special delete entry command. - case DELETE_ENTRY: - { - unsigned int selected = listview_get_selected ( state->list_view ); - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - state->retv = MENU_ENTRY_DELETE; - state->quit = TRUE; - } - break; - } - case SELECT_ELEMENT_1: - case SELECT_ELEMENT_2: - case SELECT_ELEMENT_3: - case SELECT_ELEMENT_4: - case SELECT_ELEMENT_5: - case SELECT_ELEMENT_6: - case SELECT_ELEMENT_7: - case SELECT_ELEMENT_8: - case SELECT_ELEMENT_9: - case SELECT_ELEMENT_10: - { - unsigned int index = action - SELECT_ELEMENT_1; - if ( index < state->filtered_lines ) { - state->selected_line = state->line_map[index]; - state->retv = MENU_OK; - state->quit = TRUE; - } - 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; - unsigned int selected = listview_get_selected ( state->list_view ); - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - } - state->retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK ); - state->quit = TRUE; - break; - } - // If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too - case CANCEL: - state->retv = MENU_CANCEL; - state->quit = TRUE; - break; - case ROW_UP: - listview_nav_up ( state->list_view ); - break; - case ROW_TAB: - wayland_rofi_view_nav_row_tab ( state ); - break; - case ROW_DOWN: - listview_nav_down ( state->list_view ); - break; - case ROW_LEFT: - listview_nav_left ( state->list_view ); - break; - case ROW_RIGHT: - listview_nav_right ( state->list_view ); - break; - case PAGE_PREV: - listview_nav_page_prev ( state->list_view ); - break; - case PAGE_NEXT: - listview_nav_page_next ( state->list_view ); - break; - case ROW_FIRST: - wayland_rofi_view_nav_first ( state ); - break; - case ROW_LAST: - wayland_rofi_view_nav_last ( state ); - break; - case ROW_SELECT: - wayland_rofi_view_nav_row_select ( state ); - break; - // If you add a binding here, make sure to add it to textbox_keybinding too - case MOVE_CHAR_BACK: - { - if ( textbox_keybinding ( state->text, action ) == 0 ) { - listview_nav_left ( state->list_view ); - } - break; - } - case MOVE_CHAR_FORWARD: - { - if ( textbox_keybinding ( state->text, action ) == 0 ) { - listview_nav_right ( state->list_view ); - } - break; - } - case CLEAR_LINE: - case MOVE_FRONT: - case MOVE_END: - case REMOVE_TO_EOL: - case REMOVE_TO_SOL: - case REMOVE_WORD_BACK: - case REMOVE_WORD_FORWARD: - case REMOVE_CHAR_FORWARD: - case MOVE_WORD_BACK: - case MOVE_WORD_FORWARD: - case REMOVE_CHAR_BACK: - { - int rc = textbox_keybinding ( state->text, action ); - if ( rc == 1 ) { - // Entry changed. - state->refilter = TRUE; - } - else if ( rc == 2 ) { - // Movement. - } - break; - } - case ACCEPT_ALT: - { - unsigned int selected = listview_get_selected ( state->list_view ); - state->selected_line = UINT32_MAX; - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - state->retv = MENU_OK; - } - else { - // Nothing entered and nothing selected. - state->retv = MENU_CUSTOM_INPUT; - } - state->retv |= MENU_CUSTOM_ACTION; - state->quit = TRUE; - break; - } - case ACCEPT_CUSTOM: - { - state->selected_line = UINT32_MAX; - state->retv = MENU_CUSTOM_INPUT; - state->quit = TRUE; - break; - } - case ACCEPT_ENTRY: - { - // If a valid item is selected, return that.. - unsigned int selected = listview_get_selected ( state->list_view ); - state->selected_line = UINT32_MAX; - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - state->retv = MENU_OK; - } - else { - // Nothing entered and nothing selected. - state->retv = MENU_CUSTOM_INPUT; - } - - state->quit = TRUE; - break; - } - } -} - -static gboolean wayland_rofi_view_trigger_action ( RofiViewState *state, BindingsScope scope, guint action ) -{ - switch ( scope ) - { - case SCOPE_GLOBAL: - wayland_rofi_view_trigger_global_action ( action ); - return TRUE; - case SCOPE_MOUSE_LISTVIEW: - case SCOPE_MOUSE_LISTVIEW_ELEMENT: - case SCOPE_MOUSE_EDITBOX: - case SCOPE_MOUSE_SCROLLBAR: - case SCOPE_MOUSE_MODE_SWITCHER: - { - gint x = state->mouse.x, y = state->mouse.y; - widget *target = widget_find_mouse_target ( WIDGET ( state->main_window ), scope, x, y ); - if ( target == NULL ) { - return FALSE; - } - widget_xy_to_relative ( target, &x, &y ); - switch ( widget_trigger_action ( target, action, x, y ) ) - { - case WIDGET_TRIGGER_ACTION_RESULT_IGNORED: - return FALSE; - case WIDGET_TRIGGER_ACTION_RESULT_GRAB_MOTION_END: - target = NULL; - /* FALLTHRU */ - case WIDGET_TRIGGER_ACTION_RESULT_GRAB_MOTION_BEGIN: - state->mouse.motion_target = target; - /* FALLTHRU */ - case WIDGET_TRIGGER_ACTION_RESULT_HANDLED: - return TRUE; - } - break; - } - } - return FALSE; -} - -static void wayland_rofi_view_handle_text ( RofiViewState *state, char *text ) -{ - if ( textbox_append_text ( state->text, text, strlen ( text ) ) ) { - state->refilter = TRUE; - } -} - 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 = wayland_rofi_view_get_active (); + state = rofi_view_get_active (); // cleanup, if no more state to display. if ( state == NULL ) { @@ -854,14 +349,14 @@ static void wayland_rofi_view_maybe_update ( RofiViewState *state ) static void wayland_rofi_view_frame_callback ( void ) { - if ( CacheState.repaint_source == 0 ) { - CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, wayland_rofi_view_repaint, NULL, NULL ); + if ( WlState.repaint_source == 0 ) { + WlState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, wayland_rofi_view_repaint, NULL, NULL ); } } static int wayland_rofi_view_calculate_window_height ( RofiViewState *state ) { - if ( CacheState.fullscreen == TRUE ) { + if ( WlState.fullscreen == TRUE ) { int height = 1080; display_get_surface_dimensions( NULL, &height ); return height; @@ -880,434 +375,45 @@ static int wayland_rofi_view_calculate_window_height ( RofiViewState *state ) return height; } -static 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 ) -{ - RofiViewState *state = ( RofiViewState *) user_data; - switch ( action ) - { - case MOUSE_CLICK_DOWN: - { - const char * type = rofi_theme_get_string ( wid, "action", "ok" ); - ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; - if ( strcmp ( type, "ok" ) == 0 ) { - state->retv = MENU_OK; - } - else if ( strcmp ( type, "ok|alternate" ) == 0 ) { - state->retv = MENU_CUSTOM_ACTION | MENU_OK; - } - else if ( strcmp ( type, "custom" ) ) { - state->retv = MENU_CUSTOM_INPUT; - } - else if ( strcmp ( type, "custom|alternate" ) == 0 ) { - state->retv = MENU_CUSTOM_ACTION | MENU_CUSTOM_INPUT; - } - else { - g_warning ( "Invalid action specified." ); - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; - } - state->quit = TRUE; - state->skip_absorb = TRUE; - return WIDGET_TRIGGER_ACTION_RESULT_HANDLED; - } - case MOUSE_CLICK_UP: - case MOUSE_DCLICK_DOWN: - case MOUSE_DCLICK_UP: - break; - } - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; -} -static WidgetTriggerActionResult textbox_sidebar_modi_trigger_action ( widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data ) -{ - RofiViewState *state = ( RofiViewState *) user_data; - unsigned int i; - for ( i = 0; i < state->num_modi; i++ ) { - if ( WIDGET ( state->modi[i] ) == wid ) { - break; - } - } - if ( i == state->num_modi ) { - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; - } - - switch ( action ) - { - case MOUSE_CLICK_DOWN: - state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK ); - state->quit = TRUE; - state->skip_absorb = TRUE; - return WIDGET_TRIGGER_ACTION_RESULT_HANDLED; - case MOUSE_CLICK_UP: - case MOUSE_DCLICK_DOWN: - case MOUSE_DCLICK_UP: - break; - } - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; -} - -// @TODO don't like this construction. -static void wayland_rofi_view_listview_mouse_activated_cb ( listview *lv, gboolean custom, void *udata ) -{ - RofiViewState *state = (RofiViewState *) udata; - state->retv = MENU_OK; - if ( custom ) { - state->retv |= MENU_CUSTOM_ACTION; - } - ( state->selected_line ) = state->line_map[listview_get_selected ( lv )]; - // Quit - state->quit = TRUE; - state->skip_absorb = TRUE; -} - -static void wayland_rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, const char *name ) -{ - char *defaults = NULL; - widget *wid = NULL; - - /** - * MAINBOX - */ - if ( strcmp ( name, "mainbox" ) == 0 ) { - wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); - box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); - if ( config.sidebar_mode ) { - defaults = "inputbar,message,listview,mode-switcher"; - } - else { - defaults = "inputbar,message,listview"; - } - } - /** - * INPUTBAR - */ - else if ( strcmp ( name, "inputbar" ) == 0 ) { - wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL ); - defaults = "prompt,entry,overlay,case-indicator"; - box_add ( (box *) parent_widget, WIDGET ( wid ), FALSE ); - } - /** - * PROMPT - */ - else if ( strcmp ( name, "prompt" ) == 0 ) { - if ( state->prompt != NULL ) { - g_error ( "Prompt widget can only be added once to the layout." ); - return; - } - // Prompt box. - state->prompt = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); - wayland_rofi_view_update_prompt ( state ); - box_add ( (box *) parent_widget, WIDGET ( state->prompt ), FALSE ); - defaults = NULL; - } - else if ( strcmp ( name, "num-rows" ) == 0 ) { - state->tb_total_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->tb_total_rows ), FALSE ); - defaults = NULL; - } - else if ( strcmp ( name, "num-filtered-rows" ) == 0 ) { - state->tb_filtered_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->tb_filtered_rows ), FALSE ); - defaults = NULL; - } - /** - * CASE INDICATOR - */ - else if ( strcmp ( name, "case-indicator" ) == 0 ) { - if ( state->case_indicator != NULL ) { - g_error ( "Case indicator widget can only be added once to the layout." ); - return; - } - state->case_indicator = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, 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 () ); - } - /** - * ENTRY BOX - */ - else if ( strcmp ( name, "entry" ) == 0 ) { - if ( state->text != NULL ) { - g_error ( "Entry textbox widget can only be added once to the layout." ); - return; - } - // Entry box - TextboxFlags tfl = TB_EDITABLE; - tfl |= ( ( state->menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0; - state->text = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, tfl | TB_AUTOHEIGHT, NORMAL, NULL, 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->text ), TRUE ); - } - /** - * MESSAGE - */ - else if ( strcmp ( name, "message" ) == 0 ) { - if ( state->mesg_box != NULL ) { - g_error ( "Message widget can only be added once to the layout." ); - return; - } - state->mesg_box = container_create ( parent_widget, name ); - state->mesg_tb = textbox_create ( WIDGET ( state->mesg_box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL, 0, 0 ); - container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) ); - rofi_view_reload_message_bar ( state ); - box_add ( (box *) parent_widget, WIDGET ( state->mesg_box ), FALSE ); - } - /** - * LISTVIEW - */ - else if ( strcmp ( name, "listview" ) == 0 ) { - if ( state->list_view != NULL ) { - g_error ( "Listview widget can only be added once to the layout." ); - return; - } - state->list_view = listview_create ( parent_widget, name, update_callback, state, config.element_height, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->list_view ), TRUE ); - // Set configuration - listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR ); - listview_set_scroll_type ( state->list_view, config.scroll_method ); - listview_set_mouse_activated_cb ( state->list_view, wayland_rofi_view_listview_mouse_activated_cb, state ); - - int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines ); - listview_set_num_lines ( state->list_view, lines ); - listview_set_max_lines ( state->list_view, state->num_lines ); - } - /** - * MODE SWITCHER - */ - else if ( strcmp ( name, "mode-switcher" ) == 0 || strcmp ( name, "sidebar" ) == 0 ) { - if ( state->sidebar_bar != NULL ) { - g_error ( "Mode-switcher can only be added once to the layout." ); - return; - } - state->sidebar_bar = box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL ); - box_add ( (box *) parent_widget, WIDGET ( state->sidebar_bar ), FALSE ); - state->num_modi = rofi_get_num_enabled_modi (); - state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) ); - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - const Mode * mode = rofi_get_mode ( j ); - state->modi[j] = textbox_create ( WIDGET ( state->sidebar_bar ), WIDGET_TYPE_MODE_SWITCHER, "button", TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL, - mode_get_display_name ( mode ), 0.5, 0.5 ); - box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE ); - widget_set_trigger_action_handler ( WIDGET ( state->modi[j] ), textbox_sidebar_modi_trigger_action, state ); - } - } - else if ( g_ascii_strcasecmp ( name, "overlay" ) == 0 ) { - state->overlay = textbox_create ( WIDGET ( parent_widget ), WIDGET_TYPE_TEXTBOX_TEXT, "overlay", TB_AUTOWIDTH | TB_AUTOHEIGHT, URGENT, "blaat", 0.5, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->overlay ), FALSE ); - widget_disable ( WIDGET ( state->overlay ) ); - } - else if ( g_ascii_strncasecmp ( name, "textbox", 7 ) == 0 ) { - textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); - } - else if ( g_ascii_strncasecmp ( name, "button", 6 ) == 0 ) { - textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); - widget_set_trigger_action_handler ( WIDGET ( t ), textbox_button_trigger_action, state ); - } - else if ( g_ascii_strncasecmp ( name, "icon", 4 ) == 0 ) { - icon *t = icon_create ( parent_widget, name ); - box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); - } - else { - wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); - box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); - //g_error("The widget %s does not exists. Invalid layout.", name); - } - if ( wid ) { - GList *list = rofi_theme_get_list ( wid, "children", defaults ); - for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) { - wayland_rofi_view_add_widget ( state, wid, (const char *) iter->data ); - } - g_list_free_full ( list, g_free ); - } -} - -static RofiViewState *wayland_rofi_view_create ( Mode *sw, - const char *input, - MenuFlags menu_flags, - void ( *finalize )( RofiViewState * ) ) -{ - TICK (); - RofiViewState *state = __rofi_view_state_create (); - state->menu_flags = menu_flags; - state->sw = sw; - state->selected_line = UINT32_MAX; - state->retv = MENU_CANCEL; - state->distance = NULL; - state->quit = FALSE; - state->skip_absorb = FALSE; - //We want to filter on the first run. - state->refilter = TRUE; - state->finalize = finalize; - state->mouse_seen = FALSE; - state->frame_callback = TRUE; - - // Request the lines to show. - state->num_lines = mode_get_num_entries ( sw ); - - state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL ); - // Get children. - GList *list = rofi_theme_get_list ( WIDGET ( state->main_window ), "children", "mainbox" ); - for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) { - wayland_rofi_view_add_widget ( state, WIDGET ( state->main_window ), (const char *) iter->data ); - } - g_list_free_full ( list, g_free ); - - if ( state->text && input ) { - textbox_text ( state->text, input ); - textbox_cursor_end ( state->text ); - } - - // filtered list - state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) ); - state->distance = (int *) g_malloc0_n ( state->num_lines, sizeof ( int ) ); - - wayland_rofi_view_calculate_window_width ( state ); - // Need to resize otherwise calculated desired height is wrong. - widget_resize ( WIDGET ( state->main_window ), state->width, 100 ); - // Only needed when window is fixed size. - if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) { - listview_set_fixed_num_lines ( state->list_view ); - } - - state->height = wayland_rofi_view_calculate_window_height ( state ); - // Move the window to the correct x,y position. - wayland_rofi_view_calculate_window_position ( state ); - wayland_rofi_view_window_update_size ( state ); - - state->quit = FALSE; - rofi_view_refilter ( state ); - wayland_rofi_view_update ( state, TRUE ); - widget_queue_redraw ( WIDGET ( state->main_window ) ); - return state; -} - -static int wayland_rofi_view_error_dialog ( const char *msg, int markup ) -{ - RofiViewState *state = __rofi_view_state_create (); - state->retv = MENU_CANCEL; - state->menu_flags = MENU_ERROR_DIALOG; - state->finalize = process_result; - - state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL ); - box *box = box_create ( WIDGET ( state->main_window ), "error-message", ROFI_ORIENTATION_VERTICAL ); - box_add ( state->main_window, WIDGET ( box ), TRUE ); - state->text = textbox_create ( WIDGET ( box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ), - NORMAL, ( msg != NULL ) ? msg : "", 0, 0 ); - box_add ( box, WIDGET ( state->text ), TRUE ); - - // Make sure we enable fixed num lines when in normal window mode. - if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) { - listview_set_fixed_num_lines ( state->list_view ); - } - wayland_rofi_view_calculate_window_width ( state ); - // Need to resize otherwise calculated desired height is wrong. - widget_resize ( WIDGET ( state->main_window ), state->width, 100 ); - // resize window vertically to suit - state->height = widget_get_desired_height ( WIDGET ( state->main_window ) ); - - // Calculte window position. - wayland_rofi_view_calculate_window_position ( state ); - - // Move the window to the correct x,y position. - wayland_rofi_view_window_update_size ( state ); - - // Set it as current window. - wayland_rofi_view_set_active ( state ); - return TRUE; -} - static void wayland_rofi_view_hide ( void ) { } static void wayland_rofi_view_cleanup () { - //g_log ( LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Cleanup." ); - if ( CacheState.idle_timeout > 0 ) { - g_source_remove ( CacheState.idle_timeout ); - CacheState.idle_timeout = 0; + g_debug ( "Cleanup." ); + if ( WlState.idle_timeout > 0 ) { + g_source_remove ( WlState.idle_timeout ); + WlState.idle_timeout = 0; } - if ( CacheState.repaint_source > 0 ) { - g_source_remove ( CacheState.repaint_source ); - CacheState.repaint_source = 0; - } - g_assert ( g_queue_is_empty ( &( CacheState.views ) ) ); -} - -static Mode * wayland_rofi_view_get_mode ( RofiViewState *state ) -{ - return state->sw; -} - -static void wayland_rofi_view_set_overlay ( RofiViewState *state, const char *text ) -{ - if ( state->overlay == NULL || state->list_view == NULL ) { - return; - } - if ( text == NULL ) { - widget_disable ( WIDGET ( state->overlay ) ); - return; - } - widget_enable ( WIDGET ( state->overlay ) ); - textbox_text ( state->overlay, text ); - // We want to queue a repaint. - wayland_rofi_view_queue_redraw ( ); -} - -static void wayland_rofi_view_clear_input ( RofiViewState *state ) -{ - if ( state->text ) { - textbox_text ( state->text, "" ); - wayland_rofi_view_set_selected_line ( state, 0 ); + if ( WlState.repaint_source > 0 ) { + g_source_remove ( WlState.repaint_source ); + WlState.repaint_source = 0; } } -static void wayland_rofi_view_switch_mode ( RofiViewState *state, Mode *mode ) +static void wayland_rofi_view_set_window_title ( const char * title ) { - state->sw = mode; - // Update prompt; - if ( state->prompt ) { - wayland_rofi_view_update_prompt ( state ); - } - if ( state->sidebar_bar ) { - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - const Mode * mode = rofi_get_mode ( j ); - textbox_font ( state->modi[j], ( mode == state->sw ) ? HIGHLIGHT : NORMAL ); - } - } - rofi_view_restart ( state ); - state->reload = TRUE; - state->refilter = TRUE; - rofi_view_refilter ( state ); - wayland_rofi_view_update ( state, TRUE ); } static view_proxy view_ = { - .create = wayland_rofi_view_create, - .handle_text = wayland_rofi_view_handle_text, + .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, - .get_user_input = wayland_rofi_view_get_user_input, - .set_selected_line = wayland_rofi_view_set_selected_line, - .trigger_action = wayland_rofi_view_trigger_action, - .free = wayland_rofi_view_free, - .get_active = wayland_rofi_view_get_active, - .set_active = wayland_rofi_view_set_active, - .error_dialog = wayland_rofi_view_error_dialog, .queue_redraw = wayland_rofi_view_queue_redraw, + .set_window_title = wayland_rofi_view_set_window_title, .calculate_window_position = wayland_rofi_view_calculate_window_position, .calculate_window_height = wayland_rofi_view_calculate_window_height, + .calculate_window_width = wayland_rofi_view_calculate_window_width, .window_update_size = wayland_rofi_view_window_update_size, .cleanup = wayland_rofi_view_cleanup, - .get_mode = wayland_rofi_view_get_mode, .hide = wayland_rofi_view_hide, .reload = wayland_rofi_view_reload, - .switch_mode = wayland_rofi_view_switch_mode, - .set_overlay = wayland_rofi_view_set_overlay, - .clear_input = wayland_rofi_view_clear_input, + .__create_window = wayland___create_window, .get_window = NULL, .get_current_monitor = wayland_rofi_view_get_current_monitor, diff --git a/source/xcb/display.c b/source/xcb/display.c index 88f7d055..8e40b791 100644 --- a/source/xcb/display.c +++ b/source/xcb/display.c @@ -61,6 +61,7 @@ #include "settings.h" #include "helper.h" #include "timings.h" +#include "display-internal.h" #include diff --git a/source/xcb/view.c b/source/xcb/view.c index 41ea5879..bf2657b2 100644 --- a/source/xcb/view.c +++ b/source/xcb/view.c @@ -75,7 +75,6 @@ * * Update the state of the view. This involves filter state. */ -static void xcb_rofi_view_update ( RofiViewState *state, gboolean qr ); static int xcb_rofi_view_calculate_window_height ( RofiViewState *state ); @@ -86,16 +85,11 @@ static void xcb_rofi_view_queue_redraw ( void ); /** Thread pool used for filtering */ extern GThreadPool *tpool; -/** Global pointer to the currently active RofiViewState */ -RofiViewState *current_active_menu = NULL; - /** * Structure holding cached state. */ static struct { - /** main x11 windows */ - xcb_window_t main_window; /** surface containing the fake background. */ cairo_surface_t *fake_bg; /** Draw context for main window */ @@ -108,10 +102,6 @@ static struct cairo_t *edit_draw; /** Indicate that fake background should be drawn relative to the window */ int fake_bgrel; - /** Main flags */ - MenuFlags flags; - /** List of stacked views */ - GQueue views; /** Current work area */ workarea mon; /** timeout for reloading */ @@ -122,14 +112,11 @@ static struct guint repaint_source; /** Window fullscreen */ gboolean fullscreen; -} CacheState = { - .main_window = XCB_WINDOW_NONE, +} XcbState = { .fake_bg = NULL, .edit_surf = NULL, .edit_draw = NULL, .fake_bgrel = FALSE, - .flags = MENU_NORMAL, - .views = G_QUEUE_INIT, .idle_timeout = 0, .count = 0L, .repaint_source = 0, @@ -139,29 +126,12 @@ static struct static void xcb_rofi_view_get_current_monitor ( int *width, int *height ) { if ( width ) { - *width = CacheState.mon.w; + *width = XcbState.mon.w; } if ( height ) { - *height = CacheState.mon.h; + *height = XcbState.mon.h; } } -static char * get_matching_state ( void ) -{ - if ( config.case_sensitive ) { - if ( config.sort ) { - return "±"; - } - else { - return "-"; - } - } - else{ - if ( config.sort ) { - return "+"; - } - } - return " "; -} /** * Stores a screenshot of Rofi at that point in time. @@ -169,7 +139,7 @@ static char * get_matching_state ( void ) static void xcb_rofi_view_capture_screenshot ( void ) { const char *outp = g_getenv ( "ROFI_PNG_OUTPUT" ); - if ( CacheState.edit_surf == NULL ) { + if ( XcbState.edit_surf == NULL ) { // Nothing to store. g_warning ( "There is no rofi surface to store" ); return; @@ -204,7 +174,7 @@ static void xcb_rofi_view_capture_screenshot ( void ) fpath = g_strdup ( outp ); } fprintf ( stderr, color_green "Storing screenshot %s\n"color_reset, fpath ); - cairo_status_t status = cairo_surface_write_to_png ( CacheState.edit_surf, fpath ); + cairo_status_t status = cairo_surface_write_to_png ( XcbState.edit_surf, fpath ); if ( status != CAIRO_STATUS_SUCCESS ) { g_warning ( "Failed to produce screenshot '%s', got error: '%s'", fpath, cairo_status_to_string ( status ) ); @@ -258,28 +228,83 @@ static gboolean bench_update ( void ) static gboolean xcb_rofi_view_repaint ( G_GNUC_UNUSED void * data ) { - if ( current_active_menu ) { + RofiViewState * state = rofi_view_get_active(); + if ( state ) { // 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. - xcb_rofi_view_update ( current_active_menu, FALSE ); + rofi_view_update ( state, FALSE ); g_debug ( "expose event" ); TICK_N ( "Expose" ); - xcb_copy_area ( xcb->connection, CacheState.edit_pixmap, CacheState.main_window, CacheState.gc, - 0, 0, 0, 0, current_active_menu->width, current_active_menu->height ); + xcb_copy_area ( xcb->connection, XcbState.edit_pixmap, CacheState.main_window, XcbState.gc, + 0, 0, 0, 0, state->width, state->height ); xcb_flush ( xcb->connection ); TICK_N ( "flush" ); - CacheState.repaint_source = 0; + XcbState.repaint_source = 0; } return (bench_update () == TRUE )? G_SOURCE_CONTINUE:G_SOURCE_REMOVE; } -static void xcb_rofi_view_update_prompt ( RofiViewState *state ) +static void xcb_rofi_view_update ( RofiViewState *state, gboolean qr ) { - if ( state->prompt ) { - const char *str = mode_get_display_name ( state->sw ); - textbox_text ( state->prompt, str ); + if ( !widget_need_redraw ( WIDGET ( state->main_window ) ) ) { + return; } + g_debug ( "Redraw view" ); + TICK (); + cairo_t *d = XcbState.edit_draw; + cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE ); + if ( XcbState.fake_bg != NULL ) { + if ( XcbState.fake_bgrel ) { + cairo_set_source_surface ( d, XcbState.fake_bg, 0.0, 0.0 ); + } + else { + cairo_set_source_surface ( d, XcbState.fake_bg, + -(double) ( state->x - XcbState.mon.x ), + -(double) ( state->y - XcbState.mon.y ) ); + } + cairo_paint ( d ); + cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); + } + else { + // Paint the background transparent. + cairo_set_source_rgba ( d, 0, 0, 0, 0.0 ); + cairo_paint ( d ); + } + TICK_N ( "Background" ); + + // Always paint as overlay over the background. + cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); + widget_draw ( WIDGET ( state->main_window ), d ); + + TICK_N ( "widgets" ); + cairo_surface_flush ( XcbState.edit_surf ); + if ( qr ) { + rofi_view_queue_redraw (); + } +} + +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 ); } /** @@ -323,39 +348,39 @@ static void xcb_rofi_view_calculate_window_position ( RofiViewState *state ) } anchor = rofi_theme_get_position ( WIDGET ( state->main_window ), "anchor", anchor ); - if ( CacheState.fullscreen ) { - state->x = CacheState.mon.x; - state->y = CacheState.mon.y; + if ( XcbState.fullscreen ) { + state->x = XcbState.mon.x; + state->y = XcbState.mon.y; return; } - state->y = CacheState.mon.y + ( CacheState.mon.h ) / 2; - state->x = CacheState.mon.x + ( CacheState.mon.w ) / 2; + state->y = XcbState.mon.y + ( XcbState.mon.h ) / 2; + state->x = XcbState.mon.x + ( XcbState.mon.w ) / 2; // Determine window location switch ( location ) { case WL_NORTH_WEST: - state->x = CacheState.mon.x; + state->x = XcbState.mon.x; /* FALLTHRU */ case WL_NORTH: - state->y = CacheState.mon.y; + state->y = XcbState.mon.y; break; case WL_NORTH_EAST: - state->y = CacheState.mon.y; + state->y = XcbState.mon.y; /* FALLTHRU */ case WL_EAST: - state->x = CacheState.mon.x + CacheState.mon.w; + state->x = XcbState.mon.x + XcbState.mon.w; break; case WL_SOUTH_EAST: - state->x = CacheState.mon.x + CacheState.mon.w; + state->x = XcbState.mon.x + XcbState.mon.w; /* FALLTHRU */ case WL_SOUTH: - state->y = CacheState.mon.y + CacheState.mon.h; + state->y = XcbState.mon.y + XcbState.mon.h; break; case WL_SOUTH_WEST: - state->y = CacheState.mon.y + CacheState.mon.h; + state->y = XcbState.mon.y + XcbState.mon.h; /* FALLTHRU */ case WL_WEST: - state->x = CacheState.mon.x; + state->x = XcbState.mon.x; break; case WL_CENTER: ; @@ -412,16 +437,16 @@ static void xcb_rofi_view_window_update_size ( RofiViewState * state ) // Display it. xcb_configure_window ( xcb->connection, CacheState.main_window, mask, vals ); - cairo_destroy ( CacheState.edit_draw ); - cairo_surface_destroy ( CacheState.edit_surf ); + cairo_destroy ( XcbState.edit_draw ); + cairo_surface_destroy ( XcbState.edit_surf ); - xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap ); - CacheState.edit_pixmap = xcb_generate_id ( xcb->connection ); + xcb_free_pixmap ( xcb->connection, XcbState.edit_pixmap ); + XcbState.edit_pixmap = xcb_generate_id ( xcb->connection ); xcb_create_pixmap ( xcb->connection, depth->depth, - CacheState.edit_pixmap, CacheState.main_window, state->width, state->height ); + XcbState.edit_pixmap, CacheState.main_window, state->width, state->height ); - CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height ); - CacheState.edit_draw = cairo_create ( CacheState.edit_surf ); + XcbState.edit_surf = cairo_xcb_surface_create ( xcb->connection, XcbState.edit_pixmap, visual, state->width, state->height ); + XcbState.edit_draw = cairo_create ( XcbState.edit_surf ); g_debug ( "Re-size window based internal request: %dx%d.", state->width, state->height ); // Should wrap main window in a widget. @@ -430,107 +455,38 @@ static void xcb_rofi_view_window_update_size ( RofiViewState * state ) static gboolean xcb_rofi_view_reload_idle ( G_GNUC_UNUSED gpointer data ) { - if ( current_active_menu ) { - current_active_menu->reload = TRUE; - current_active_menu->refilter = TRUE; + RofiViewState * state = rofi_view_get_active (); + + if ( state ) { + state->reload = TRUE; + state->refilter = TRUE; xcb_rofi_view_queue_redraw (); } - CacheState.idle_timeout = 0; + XcbState.idle_timeout = 0; return G_SOURCE_REMOVE; } static void xcb_rofi_view_reload ( void ) { // @TODO add check if current view is equal to the callee - if ( CacheState.idle_timeout == 0 ) { - CacheState.idle_timeout = g_timeout_add ( 1000 / 10, xcb_rofi_view_reload_idle, NULL ); + if ( XcbState.idle_timeout == 0 ) { + XcbState.idle_timeout = g_timeout_add ( 1000 / 10, xcb_rofi_view_reload_idle, NULL ); } } static void xcb_rofi_view_queue_redraw ( void ) { - if ( current_active_menu && CacheState.repaint_source == 0 ) { - CacheState.count++; - g_debug ( "redraw %llu", CacheState.count ); - CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL ); + RofiViewState * state = rofi_view_get_active (); + + if ( state && XcbState.repaint_source == 0 ) { + XcbState.count++; + g_debug ( "redraw %llu", XcbState.count ); + XcbState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL ); } } -static RofiViewState * xcb_rofi_view_get_active ( void ) -{ - return current_active_menu; -} - -static void xcb_rofi_view_set_active ( RofiViewState *state ) -{ - if ( current_active_menu != NULL && state != NULL ) { - g_queue_push_head ( &( CacheState.views ), current_active_menu ); - // TODO check. - current_active_menu = state; - g_debug ( "stack view." ); - xcb_rofi_view_window_update_size ( current_active_menu ); - xcb_rofi_view_queue_redraw (); - return; - } - else if ( state == NULL && !g_queue_is_empty ( &( CacheState.views ) ) ) { - g_debug ( "pop view." ); - current_active_menu = g_queue_pop_head ( &( CacheState.views ) ); - xcb_rofi_view_window_update_size ( current_active_menu ); - xcb_rofi_view_queue_redraw (); - return; - } - g_assert ( ( current_active_menu == NULL && state != NULL ) || ( current_active_menu != NULL && state == NULL ) ); - current_active_menu = state; - xcb_rofi_view_queue_redraw (); -} - -static void xcb_rofi_view_set_selected_line ( RofiViewState *state, unsigned int selected_line ) -{ - state->selected_line = selected_line; - // Find the line. - unsigned int selected = 0; - for ( unsigned int i = 0; ( ( state->selected_line ) ) < UINT32_MAX && !selected && i < state->filtered_lines; i++ ) { - if ( state->line_map[i] == ( state->selected_line ) ) { - selected = i; - break; - } - } - listview_set_selected ( state->list_view, selected ); - xcb_clear_area ( xcb->connection, CacheState.main_window, 1, 0, 0, 1, 1 ); - xcb_flush ( xcb->connection ); -} - -static void xcb_rofi_view_free ( RofiViewState *state ) -{ - if ( state->tokens ) { - helper_tokenize_free ( state->tokens ); - state->tokens = NULL; - } - // Do this here? - // Wait for final release? - widget_free ( WIDGET ( state->main_window ) ); - - g_free ( state->line_map ); - g_free ( state->distance ); - // Free the switcher boxes. - // When state is free'ed we should no longer need these. - g_free ( state->modi ); - state->num_modi = 0; - g_free ( state ); -} - -/** - * Create a new, 0 initialized RofiViewState structure. - * - * @returns a new 0 initialized RofiViewState - static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_static xcb_*/ -static RofiViewState * __rofi_view_state_create ( void ) -{ - return g_malloc0 ( sizeof ( RofiViewState ) ); -} - static void xcb_rofi_view_setup_fake_transparency ( const char* const fake_background ) { - if ( CacheState.fake_bg == NULL ) { + if ( XcbState.fake_bg == NULL ) { cairo_surface_t *s = NULL; /** * Select Background to use for fake transparency. @@ -550,7 +506,7 @@ static void xcb_rofi_view_setup_fake_transparency ( const char* const fake_backg char *fpath = rofi_expand_path ( fake_background ); g_debug ( "Opening %s to use as background.", fpath ); s = cairo_image_surface_create_from_png ( fpath ); - CacheState.fake_bgrel = TRUE; + XcbState.fake_bgrel = TRUE; g_free ( fpath ); } TICK_N ( "Get surface." ); @@ -562,13 +518,13 @@ static void xcb_rofi_view_setup_fake_transparency ( const char* const fake_backg s = NULL; } else { - CacheState.fake_bg = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, CacheState.mon.w, CacheState.mon.h ); - cairo_t *dr = cairo_create ( CacheState.fake_bg ); - if ( CacheState.fake_bgrel ) { + XcbState.fake_bg = cairo_image_surface_create ( CAIRO_FORMAT_ARGB32, XcbState.mon.w, XcbState.mon.h ); + cairo_t *dr = cairo_create ( XcbState.fake_bg ); + if ( XcbState.fake_bgrel ) { cairo_set_source_surface ( dr, s, 0, 0 ); } else { - cairo_set_source_surface ( dr, s, -CacheState.mon.x, -CacheState.mon.y ); + cairo_set_source_surface ( dr, s, -XcbState.mon.x, -XcbState.mon.y ); } cairo_paint ( dr ); cairo_destroy ( dr ); @@ -602,32 +558,32 @@ static void xcb___create_window ( MenuFlags menu_flags ) exit ( EXIT_FAILURE ); } TICK_N ( "xcb create window" ); - CacheState.gc = xcb_generate_id ( xcb->connection ); - xcb_create_gc ( xcb->connection, CacheState.gc, box_window, 0, 0 ); + XcbState.gc = xcb_generate_id ( xcb->connection ); + xcb_create_gc ( xcb->connection, XcbState.gc, box_window, 0, 0 ); TICK_N ( "xcb create gc" ); // Create a drawable. - CacheState.edit_pixmap = xcb_generate_id ( xcb->connection ); + XcbState.edit_pixmap = xcb_generate_id ( xcb->connection ); xcb_create_pixmap ( xcb->connection, depth->depth, - CacheState.edit_pixmap, CacheState.main_window, 200, 100 ); + XcbState.edit_pixmap, CacheState.main_window, 200, 100 ); - CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, 200, 100 ); - CacheState.edit_draw = cairo_create ( CacheState.edit_surf ); + XcbState.edit_surf = cairo_xcb_surface_create ( xcb->connection, XcbState.edit_pixmap, visual, 200, 100 ); + XcbState.edit_draw = cairo_create ( XcbState.edit_surf ); TICK_N ( "create cairo surface" ); // Set up pango context. cairo_font_options_t *fo = cairo_font_options_create (); // Take font description from xlib surface - cairo_surface_get_font_options ( CacheState.edit_surf, fo ); + cairo_surface_get_font_options ( XcbState.edit_surf, fo ); // TODO should we update the drawable each time? - PangoContext *p = pango_cairo_create_context ( CacheState.edit_draw ); + PangoContext *p = pango_cairo_create_context ( XcbState.edit_draw ); // Set the font options from the xlib surface pango_cairo_context_set_font_options ( p, fo ); TICK_N ( "pango cairo font setup" ); CacheState.main_window = box_window; CacheState.flags = menu_flags; - monitor_active ( &( CacheState.mon ) ); + monitor_active ( &( XcbState.mon ) ); // Setup dpi if ( config.dpi > 1 ) { PangoFontMap *font_map = pango_cairo_font_map_get_default (); @@ -636,8 +592,8 @@ static void xcb___create_window ( MenuFlags menu_flags ) else if ( config.dpi == 0 || config.dpi == 1 ) { // Auto-detect mode. double dpi = 96; - if ( CacheState.mon.mh > 0 && config.dpi == 1 ) { - dpi = ( CacheState.mon.h * 25.4 ) / (double) ( CacheState.mon.mh ); + if ( XcbState.mon.mh > 0 && config.dpi == 1 ) { + dpi = ( XcbState.mon.h * 25.4 ) / (double) ( XcbState.mon.mh ); } else { dpi = ( xcb->screen->height_in_pixels * 25.4 ) / (double) ( xcb->screen->height_in_millimeters ); @@ -686,8 +642,8 @@ static void xcb___create_window ( MenuFlags menu_flags ) } TICK_N ( "setup window attributes" ); - CacheState.fullscreen = rofi_theme_get_boolean ( WIDGET ( win ), "fullscreen", config.fullscreen ); - if ( CacheState.fullscreen ) { + XcbState.fullscreen = rofi_theme_get_boolean ( WIDGET ( win ), "fullscreen", config.fullscreen ); + if ( XcbState.fullscreen ) { xcb_atom_t atoms[] = { xcb->ewmh._NET_WM_STATE_FULLSCREEN, xcb->ewmh._NET_WM_STATE_ABOVE @@ -739,8 +695,8 @@ static void xcb___create_window ( MenuFlags menu_flags ) */ static void xcb_rofi_view_calculate_window_width ( RofiViewState *state ) { - if ( CacheState.fullscreen ) { - state->width = CacheState.mon.w; + if ( XcbState.fullscreen ) { + state->width = XcbState.mon.w; return; } if ( config.menu_width < 0 ) { @@ -750,468 +706,13 @@ static void xcb_rofi_view_calculate_window_width ( RofiViewState *state ) } else{ // Calculate as float to stop silly, big rounding down errors. - state->width = config.menu_width < 101 ? ( CacheState.mon.w / 100.0f ) * ( float ) config.menu_width : config.menu_width; + state->width = config.menu_width < 101 ? ( XcbState.mon.w / 100.0f ) * ( float ) config.menu_width : config.menu_width; } // Use theme configured width, if set. RofiDistance width = rofi_theme_get_distance ( WIDGET ( state->main_window ), "width", state->width ); state->width = distance_get_pixel ( width, ROFI_ORIENTATION_HORIZONTAL ); } -/** - * Nav helper functions, to avoid duplicate code. - */ - -/** - * @param state The current RofiViewState - * - * Tab handling. - */ -static void xcb_rofi_view_nav_row_tab ( RofiViewState *state ) -{ - if ( state->filtered_lines == 1 ) { - state->retv = MENU_OK; - ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; - state->quit = 1; - return; - } - - // Double tab! - if ( state->filtered_lines == 0 && ROW_TAB == state->prev_action ) { - state->retv = MENU_NEXT; - ( state->selected_line ) = 0; - state->quit = TRUE; - } - else { - listview_nav_down ( state->list_view ); - } - state->prev_action = ROW_TAB; -} -/** - * @param state The current RofiViewState - * - * complete current row. - */ -inline static void xcb_rofi_view_nav_row_select ( RofiViewState *state ) -{ - if ( state->list_view == NULL ) { - return; - } - unsigned int selected = listview_get_selected ( state->list_view ); - // If a valid item is selected, return that.. - if ( selected < state->filtered_lines ) { - char *str = mode_get_completion ( state->sw, state->line_map[selected] ); - textbox_text ( state->text, str ); - g_free ( str ); - textbox_keybinding ( state->text, MOVE_END ); - state->refilter = TRUE; - } -} - -/** - * @param state The current RofiViewState - * - * Move the selection to first row. - */ -inline static void xcb_rofi_view_nav_first ( RofiViewState * state ) -{ -// state->selected = 0; - listview_set_selected ( state->list_view, 0 ); -} - -/** - * @param state The current RofiViewState - * - * Move the selection to last row. - */ -inline static void xcb_rofi_view_nav_last ( RofiViewState * state ) -{ - // If no lines, do nothing. - if ( state->filtered_lines == 0 ) { - return; - } - //state->selected = state->filtered_lines - 1; - listview_set_selected ( state->list_view, -1 ); -} - -static void update_callback ( textbox *t, icon *ico, unsigned int index, void *udata, TextBoxFontType *type, gboolean full ) -{ - RofiViewState *state = (RofiViewState *) udata; - if ( full ) { - GList *add_list = NULL; - int fstate = 0; - char *text = mode_get_display_value ( state->sw, state->line_map[index], &fstate, &add_list, TRUE ); - ( *type ) |= fstate; - // TODO needed for markup. - textbox_font ( t, *type ); - // Move into list view. - textbox_text ( t, text ); - PangoAttrList *list = textbox_get_pango_attributes ( t ); - if ( list != NULL ) { - pango_attr_list_ref ( list ); - } - else{ - list = pango_attr_list_new (); - } - if ( ico ) { - int icon_height = widget_get_desired_height ( WIDGET ( ico ) ); - cairo_surface_t *icon = mode_get_icon ( state->sw, state->line_map[index], icon_height ); - icon_set_surface ( ico, icon ); - } - - if ( state->tokens && config.show_match ) { - RofiHighlightColorStyle th = { ROFI_HL_BOLD | ROFI_HL_UNDERLINE, { 0.0, 0.0, 0.0, 0.0 } }; - th = rofi_theme_get_highlight ( WIDGET ( t ), "highlight", th ); - helper_token_match_get_pango_attr ( th, state->tokens, textbox_get_visible_text ( t ), list ); - } - for ( GList *iter = g_list_first ( add_list ); iter != NULL; iter = g_list_next ( iter ) ) { - pango_attr_list_insert ( list, (PangoAttribute *) ( iter->data ) ); - } - textbox_set_pango_attributes ( t, list ); - pango_attr_list_unref ( list ); - g_list_free ( add_list ); - g_free ( text ); - } - else { - int fstate = 0; - mode_get_display_value ( state->sw, state->line_map[index], &fstate, NULL, FALSE ); - ( *type ) |= fstate; - // TODO needed for markup. - textbox_font ( t, *type ); - } -} - -static void xcb_rofi_view_update ( RofiViewState *state, gboolean qr ) -{ - if ( !widget_need_redraw ( WIDGET ( state->main_window ) ) ) { - return; - } - g_debug ( "Redraw view" ); - TICK (); - cairo_t *d = CacheState.edit_draw; - cairo_set_operator ( d, CAIRO_OPERATOR_SOURCE ); - if ( CacheState.fake_bg != NULL ) { - if ( CacheState.fake_bgrel ) { - cairo_set_source_surface ( d, CacheState.fake_bg, 0.0, 0.0 ); - } - else { - cairo_set_source_surface ( d, CacheState.fake_bg, - -(double) ( state->x - CacheState.mon.x ), - -(double) ( state->y - CacheState.mon.y ) ); - } - cairo_paint ( d ); - cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); - } - else { - // Paint the background transparent. - cairo_set_source_rgba ( d, 0, 0, 0, 0.0 ); - cairo_paint ( d ); - } - TICK_N ( "Background" ); - - // Always paint as overlay over the background. - cairo_set_operator ( d, CAIRO_OPERATOR_OVER ); - widget_draw ( WIDGET ( state->main_window ), d ); - - TICK_N ( "widgets" ); - cairo_surface_flush ( CacheState.edit_surf ); - if ( qr ) { - xcb_rofi_view_queue_redraw (); - } -} - -static void xcb_rofi_view_trigger_global_action ( KeyBindingAction action ) -{ - RofiViewState *state = xcb_rofi_view_get_active (); - switch ( action ) - { - // Handling of paste - 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 ); - break; - case PASTE_SECONDARY: - xcb_convert_selection ( xcb->connection, CacheState.main_window, netatoms[CLIPBOARD], - xcb->ewmh.UTF8_STRING, xcb->ewmh.UTF8_STRING, XCB_CURRENT_TIME ); - xcb_flush ( xcb->connection ); - break; - case SCREENSHOT: - rofi_capture_screenshot ( ); - break; - case CHANGE_ELLIPSIZE: - if ( state->list_view ) { - listview_toggle_ellipsizing ( state->list_view ); - } - break; - case TOGGLE_SORT: - if ( state->case_indicator != NULL ) { - config.sort = !config.sort; - state->refilter = TRUE; - textbox_text ( state->case_indicator, get_matching_state () ); - } - break; - case MODE_PREVIOUS: - state->retv = MENU_PREVIOUS; - ( state->selected_line ) = 0; - state->quit = TRUE; - break; - // Menu navigation. - case MODE_NEXT: - state->retv = MENU_NEXT; - ( state->selected_line ) = 0; - state->quit = TRUE; - break; - // Toggle case sensitivity. - case TOGGLE_CASE_SENSITIVITY: - if ( state->case_indicator != NULL ) { - config.case_sensitive = !config.case_sensitive; - ( state->selected_line ) = 0; - state->refilter = TRUE; - textbox_text ( state->case_indicator, get_matching_state () ); - } - break; - // Special delete entry command. - case DELETE_ENTRY: - { - unsigned int selected = listview_get_selected ( state->list_view ); - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - state->retv = MENU_ENTRY_DELETE; - state->quit = TRUE; - } - break; - } - case SELECT_ELEMENT_1: - case SELECT_ELEMENT_2: - case SELECT_ELEMENT_3: - case SELECT_ELEMENT_4: - case SELECT_ELEMENT_5: - case SELECT_ELEMENT_6: - case SELECT_ELEMENT_7: - case SELECT_ELEMENT_8: - case SELECT_ELEMENT_9: - case SELECT_ELEMENT_10: - { - unsigned int index = action - SELECT_ELEMENT_1; - if ( index < state->filtered_lines ) { - state->selected_line = state->line_map[index]; - state->retv = MENU_OK; - state->quit = TRUE; - } - 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; - unsigned int selected = listview_get_selected ( state->list_view ); - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - } - state->retv = MENU_QUICK_SWITCH | ( ( action - CUSTOM_1 ) & MENU_LOWER_MASK ); - state->quit = TRUE; - break; - } - // If you add a binding here, make sure to add it to rofi_view_keyboard_navigation too - case CANCEL: - state->retv = MENU_CANCEL; - state->quit = TRUE; - break; - case ROW_UP: - listview_nav_up ( state->list_view ); - break; - case ROW_TAB: - xcb_rofi_view_nav_row_tab ( state ); - break; - case ROW_DOWN: - listview_nav_down ( state->list_view ); - break; - case ROW_LEFT: - listview_nav_left ( state->list_view ); - break; - case ROW_RIGHT: - listview_nav_right ( state->list_view ); - break; - case PAGE_PREV: - listview_nav_page_prev ( state->list_view ); - break; - case PAGE_NEXT: - listview_nav_page_next ( state->list_view ); - break; - case ROW_FIRST: - xcb_rofi_view_nav_first ( state ); - break; - case ROW_LAST: - xcb_rofi_view_nav_last ( state ); - break; - case ROW_SELECT: - xcb_rofi_view_nav_row_select ( state ); - break; - // If you add a binding here, make sure to add it to textbox_keybinding too - case MOVE_CHAR_BACK: - { - if ( textbox_keybinding ( state->text, action ) == 0 ) { - listview_nav_left ( state->list_view ); - } - break; - } - case MOVE_CHAR_FORWARD: - { - if ( textbox_keybinding ( state->text, action ) == 0 ) { - listview_nav_right ( state->list_view ); - } - break; - } - case CLEAR_LINE: - case MOVE_FRONT: - case MOVE_END: - case REMOVE_TO_EOL: - case REMOVE_TO_SOL: - case REMOVE_WORD_BACK: - case REMOVE_WORD_FORWARD: - case REMOVE_CHAR_FORWARD: - case MOVE_WORD_BACK: - case MOVE_WORD_FORWARD: - case REMOVE_CHAR_BACK: - { - int rc = textbox_keybinding ( state->text, action ); - if ( rc == 1 ) { - // Entry changed. - state->refilter = TRUE; - } - else if ( rc == 2 ) { - // Movement. - } - break; - } - case ACCEPT_ALT: - { - unsigned int selected = listview_get_selected ( state->list_view ); - state->selected_line = UINT32_MAX; - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - state->retv = MENU_OK; - } - else { - // Nothing entered and nothing selected. - state->retv = MENU_CUSTOM_INPUT; - } - state->retv |= MENU_CUSTOM_ACTION; - state->quit = TRUE; - break; - } - case ACCEPT_CUSTOM: - { - state->selected_line = UINT32_MAX; - state->retv = MENU_CUSTOM_INPUT; - state->quit = TRUE; - break; - } - case ACCEPT_ENTRY: - { - // If a valid item is selected, return that.. - unsigned int selected = listview_get_selected ( state->list_view ); - state->selected_line = UINT32_MAX; - if ( selected < state->filtered_lines ) { - ( state->selected_line ) = state->line_map[selected]; - state->retv = MENU_OK; - } - else { - // Nothing entered and nothing selected. - state->retv = MENU_CUSTOM_INPUT; - } - - state->quit = TRUE; - break; - } - } -} - -static gboolean xcb_rofi_view_trigger_action ( RofiViewState *state, BindingsScope scope, guint action ) -{ - switch ( scope ) - { - case SCOPE_GLOBAL: - xcb_rofi_view_trigger_global_action ( action ); - return TRUE; - case SCOPE_MOUSE_LISTVIEW: - case SCOPE_MOUSE_LISTVIEW_ELEMENT: - case SCOPE_MOUSE_EDITBOX: - case SCOPE_MOUSE_SCROLLBAR: - case SCOPE_MOUSE_MODE_SWITCHER: - { - gint x = state->mouse.x, y = state->mouse.y; - widget *target = widget_find_mouse_target ( WIDGET ( state->main_window ), scope, x, y ); - if ( target == NULL ) { - return FALSE; - } - widget_xy_to_relative ( target, &x, &y ); - switch ( widget_trigger_action ( target, action, x, y ) ) - { - case WIDGET_TRIGGER_ACTION_RESULT_IGNORED: - return FALSE; - case WIDGET_TRIGGER_ACTION_RESULT_GRAB_MOTION_END: - target = NULL; - /* FALLTHRU */ - case WIDGET_TRIGGER_ACTION_RESULT_GRAB_MOTION_BEGIN: - state->mouse.motion_target = target; - /* FALLTHRU */ - case WIDGET_TRIGGER_ACTION_RESULT_HANDLED: - return TRUE; - } - break; - } - } - return FALSE; -} - -static void xcb_rofi_view_handle_text ( RofiViewState *state, char *text ) -{ - if ( textbox_append_text ( state->text, text, strlen ( text ) ) ) { - state->refilter = TRUE; - } -} - -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 ); - } - xcb_rofi_view_update ( state, TRUE ); -} - /** * Handle window configure event. * Handles resizes. @@ -1228,16 +729,16 @@ static void xcb_rofi_view_temp_configure_notify ( RofiViewState *state, xcb_conf state->width = xce->width; state->height = xce->height; - cairo_destroy ( CacheState.edit_draw ); - cairo_surface_destroy ( CacheState.edit_surf ); + cairo_destroy ( XcbState.edit_draw ); + cairo_surface_destroy ( XcbState.edit_surf ); - xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap ); - CacheState.edit_pixmap = xcb_generate_id ( xcb->connection ); - xcb_create_pixmap ( xcb->connection, depth->depth, CacheState.edit_pixmap, CacheState.main_window, + xcb_free_pixmap ( xcb->connection, XcbState.edit_pixmap ); + XcbState.edit_pixmap = xcb_generate_id ( xcb->connection ); + xcb_create_pixmap ( xcb->connection, depth->depth, XcbState.edit_pixmap, CacheState.main_window, state->width, state->height ); - CacheState.edit_surf = cairo_xcb_surface_create ( xcb->connection, CacheState.edit_pixmap, visual, state->width, state->height ); - CacheState.edit_draw = cairo_create ( CacheState.edit_surf ); + XcbState.edit_surf = cairo_xcb_surface_create ( xcb->connection, XcbState.edit_pixmap, visual, state->width, state->height ); + XcbState.edit_draw = cairo_create ( XcbState.edit_surf ); g_debug ( "Re-size window based external request: %d %d", state->width, state->height ); widget_resize ( WIDGET ( state->main_window ), state->width, state->height ); } @@ -1259,15 +760,15 @@ static void xcb_rofi_view_temp_click_to_exit ( RofiViewState *state, xcb_window_ static void xcb_rofi_view_frame_callback ( void ) { - if ( CacheState.repaint_source == 0 ) { - CacheState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL ); + if ( XcbState.repaint_source == 0 ) { + XcbState.repaint_source = g_idle_add_full ( G_PRIORITY_HIGH_IDLE, xcb_rofi_view_repaint, NULL, NULL ); } } static int xcb_rofi_view_calculate_window_height ( RofiViewState *state ) { - if ( CacheState.fullscreen == TRUE ) { - return CacheState.mon.h; + if ( XcbState.fullscreen == TRUE ) { + return XcbState.mon.h; } RofiDistance h = rofi_theme_get_distance ( WIDGET ( state->main_window ), "height", 0 ); @@ -1281,373 +782,6 @@ static int xcb_rofi_view_calculate_window_height ( RofiViewState *state ) return widget_get_desired_height ( main_window ); } -static 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 ) -{ - RofiViewState *state = ( RofiViewState *) user_data; - switch ( action ) - { - case MOUSE_CLICK_DOWN: - { - const char * type = rofi_theme_get_string ( wid, "action", "ok" ); - ( state->selected_line ) = state->line_map[listview_get_selected ( state->list_view )]; - if ( strcmp ( type, "ok" ) == 0 ) { - state->retv = MENU_OK; - } - else if ( strcmp ( type, "ok|alternate" ) == 0 ) { - state->retv = MENU_CUSTOM_ACTION | MENU_OK; - } - else if ( strcmp ( type, "custom" ) ) { - state->retv = MENU_CUSTOM_INPUT; - } - else if ( strcmp ( type, "custom|alternate" ) == 0 ) { - state->retv = MENU_CUSTOM_ACTION | MENU_CUSTOM_INPUT; - } - else { - g_warning ( "Invalid action specified." ); - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; - } - state->quit = TRUE; - state->skip_absorb = TRUE; - return WIDGET_TRIGGER_ACTION_RESULT_HANDLED; - } - case MOUSE_CLICK_UP: - case MOUSE_DCLICK_DOWN: - case MOUSE_DCLICK_UP: - break; - } - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; -} -static WidgetTriggerActionResult textbox_sidebar_modi_trigger_action ( widget *wid, MouseBindingMouseDefaultAction action, G_GNUC_UNUSED gint x, G_GNUC_UNUSED gint y, G_GNUC_UNUSED void *user_data ) -{ - RofiViewState *state = ( RofiViewState *) user_data; - unsigned int i; - for ( i = 0; i < state->num_modi; i++ ) { - if ( WIDGET ( state->modi[i] ) == wid ) { - break; - } - } - if ( i == state->num_modi ) { - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; - } - - switch ( action ) - { - case MOUSE_CLICK_DOWN: - state->retv = MENU_QUICK_SWITCH | ( i & MENU_LOWER_MASK ); - state->quit = TRUE; - state->skip_absorb = TRUE; - return WIDGET_TRIGGER_ACTION_RESULT_HANDLED; - case MOUSE_CLICK_UP: - case MOUSE_DCLICK_DOWN: - case MOUSE_DCLICK_UP: - break; - } - return WIDGET_TRIGGER_ACTION_RESULT_IGNORED; -} - -// @TODO don't like this construction. -static void xcb_rofi_view_listview_mouse_activated_cb ( listview *lv, gboolean custom, void *udata ) -{ - RofiViewState *state = (RofiViewState *) udata; - state->retv = MENU_OK; - if ( custom ) { - state->retv |= MENU_CUSTOM_ACTION; - } - ( state->selected_line ) = state->line_map[listview_get_selected ( lv )]; - // Quit - state->quit = TRUE; - state->skip_absorb = TRUE; -} - -static void xcb_rofi_view_add_widget ( RofiViewState *state, widget *parent_widget, const char *name ) -{ - char *defaults = NULL; - widget *wid = NULL; - - /** - * MAINBOX - */ - if ( strcmp ( name, "mainbox" ) == 0 ) { - wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); - box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); - if ( config.sidebar_mode ) { - defaults = "inputbar,message,listview,mode-switcher"; - } - else { - defaults = "inputbar,message,listview"; - } - } - /** - * INPUTBAR - */ - else if ( strcmp ( name, "inputbar" ) == 0 ) { - wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL ); - defaults = "prompt,entry,overlay,case-indicator"; - box_add ( (box *) parent_widget, WIDGET ( wid ), FALSE ); - } - /** - * PROMPT - */ - else if ( strcmp ( name, "prompt" ) == 0 ) { - if ( state->prompt != NULL ) { - g_error ( "Prompt widget can only be added once to the layout." ); - return; - } - // Prompt box. - state->prompt = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); - xcb_rofi_view_update_prompt ( state ); - box_add ( (box *) parent_widget, WIDGET ( state->prompt ), FALSE ); - defaults = NULL; - } - else if ( strcmp ( name, "num-rows" ) == 0 ) { - state->tb_total_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->tb_total_rows ), FALSE ); - defaults = NULL; - } - else if ( strcmp ( name, "num-filtered-rows" ) == 0 ) { - state->tb_filtered_rows = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOWIDTH | TB_AUTOHEIGHT, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->tb_filtered_rows ), FALSE ); - defaults = NULL; - } - /** - * CASE INDICATOR - */ - else if ( strcmp ( name, "case-indicator" ) == 0 ) { - if ( state->case_indicator != NULL ) { - g_error ( "Case indicator widget can only be added once to the layout." ); - return; - } - state->case_indicator = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, 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 () ); - } - /** - * ENTRY BOX - */ - else if ( strcmp ( name, "entry" ) == 0 ) { - if ( state->text != NULL ) { - g_error ( "Entry textbox widget can only be added once to the layout." ); - return; - } - // Entry box - TextboxFlags tfl = TB_EDITABLE; - tfl |= ( ( state->menu_flags & MENU_PASSWORD ) == MENU_PASSWORD ) ? TB_PASSWORD : 0; - state->text = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, tfl | TB_AUTOHEIGHT, NORMAL, NULL, 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->text ), TRUE ); - } - /** - * MESSAGE - */ - else if ( strcmp ( name, "message" ) == 0 ) { - if ( state->mesg_box != NULL ) { - g_error ( "Message widget can only be added once to the layout." ); - return; - } - state->mesg_box = container_create ( parent_widget, name ); - state->mesg_tb = textbox_create ( WIDGET ( state->mesg_box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", TB_AUTOHEIGHT | TB_MARKUP | TB_WRAP, NORMAL, NULL, 0, 0 ); - container_add ( state->mesg_box, WIDGET ( state->mesg_tb ) ); - rofi_view_reload_message_bar ( state ); - box_add ( (box *) parent_widget, WIDGET ( state->mesg_box ), FALSE ); - } - /** - * LISTVIEW - */ - else if ( strcmp ( name, "listview" ) == 0 ) { - if ( state->list_view != NULL ) { - g_error ( "Listview widget can only be added once to the layout." ); - return; - } - state->list_view = listview_create ( parent_widget, name, update_callback, state, config.element_height, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->list_view ), TRUE ); - // Set configuration - listview_set_multi_select ( state->list_view, ( state->menu_flags & MENU_INDICATOR ) == MENU_INDICATOR ); - listview_set_scroll_type ( state->list_view, config.scroll_method ); - listview_set_mouse_activated_cb ( state->list_view, xcb_rofi_view_listview_mouse_activated_cb, state ); - - int lines = rofi_theme_get_integer ( WIDGET ( state->list_view ), "lines", config.menu_lines ); - listview_set_num_lines ( state->list_view, lines ); - listview_set_max_lines ( state->list_view, state->num_lines ); - } - /** - * MODE SWITCHER - */ - else if ( strcmp ( name, "mode-switcher" ) == 0 || strcmp ( name, "sidebar" ) == 0 ) { - if ( state->sidebar_bar != NULL ) { - g_error ( "Mode-switcher can only be added once to the layout." ); - return; - } - state->sidebar_bar = box_create ( parent_widget, name, ROFI_ORIENTATION_HORIZONTAL ); - box_add ( (box *) parent_widget, WIDGET ( state->sidebar_bar ), FALSE ); - state->num_modi = rofi_get_num_enabled_modi (); - state->modi = g_malloc0 ( state->num_modi * sizeof ( textbox * ) ); - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - const Mode * mode = rofi_get_mode ( j ); - state->modi[j] = textbox_create ( WIDGET ( state->sidebar_bar ), WIDGET_TYPE_MODE_SWITCHER, "button", TB_AUTOHEIGHT, ( mode == state->sw ) ? HIGHLIGHT : NORMAL, - mode_get_display_name ( mode ), 0.5, 0.5 ); - box_add ( state->sidebar_bar, WIDGET ( state->modi[j] ), TRUE ); - widget_set_trigger_action_handler ( WIDGET ( state->modi[j] ), textbox_sidebar_modi_trigger_action, state ); - } - } - else if ( g_ascii_strcasecmp ( name, "overlay" ) == 0 ) { - state->overlay = textbox_create ( WIDGET ( parent_widget ), WIDGET_TYPE_TEXTBOX_TEXT, "overlay", TB_AUTOWIDTH | TB_AUTOHEIGHT, URGENT, "blaat", 0.5, 0 ); - box_add ( (box *) parent_widget, WIDGET ( state->overlay ), FALSE ); - widget_disable ( WIDGET ( state->overlay ) ); - } - else if ( g_ascii_strncasecmp ( name, "textbox", 7 ) == 0 ) { - textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_TEXTBOX_TEXT, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); - } - else if ( g_ascii_strncasecmp ( name, "button", 6 ) == 0 ) { - textbox *t = textbox_create ( parent_widget, WIDGET_TYPE_EDITBOX, name, TB_AUTOHEIGHT | TB_WRAP, NORMAL, "", 0, 0 ); - box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); - widget_set_trigger_action_handler ( WIDGET ( t ), textbox_button_trigger_action, state ); - } - else if ( g_ascii_strncasecmp ( name, "icon", 4 ) == 0 ) { - icon *t = icon_create ( parent_widget, name ); - box_add ( (box *) parent_widget, WIDGET ( t ), TRUE ); - } - else { - wid = (widget *) box_create ( parent_widget, name, ROFI_ORIENTATION_VERTICAL ); - box_add ( (box *) parent_widget, WIDGET ( wid ), TRUE ); - //g_error("The widget %s does not exists. Invalid layout.", name); - } - if ( wid ) { - GList *list = rofi_theme_get_list ( wid, "children", defaults ); - for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) { - xcb_rofi_view_add_widget ( state, wid, (const char *) iter->data ); - } - g_list_free_full ( list, g_free ); - } -} - -static RofiViewState *xcb_rofi_view_create ( Mode *sw, - const char *input, - MenuFlags menu_flags, - void ( *finalize )( RofiViewState * ) ) -{ - TICK (); - RofiViewState *state = __rofi_view_state_create (); - state->menu_flags = menu_flags; - state->sw = sw; - state->selected_line = UINT32_MAX; - state->retv = MENU_CANCEL; - state->distance = NULL; - state->quit = FALSE; - state->skip_absorb = FALSE; - //We want to filter on the first run. - state->refilter = TRUE; - state->finalize = finalize; - state->mouse_seen = FALSE; - - // Request the lines to show. - state->num_lines = mode_get_num_entries ( sw ); - - if ( state->sw ) { - char * title = g_strdup_printf ( "rofi - %s", mode_get_display_name ( state->sw ) ); - xcb_rofi_view_set_window_title ( title ); - g_free ( title ); - } - else { - xcb_rofi_view_set_window_title ( "rofi" ); - } - TICK_N ( "Startup notification" ); - - // Get active monitor size. - TICK_N ( "Get active monitor" ); - - state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL ); - // Get children. - GList *list = rofi_theme_get_list ( WIDGET ( state->main_window ), "children", "mainbox" ); - for ( const GList *iter = list; iter != NULL; iter = g_list_next ( iter ) ) { - xcb_rofi_view_add_widget ( state, WIDGET ( state->main_window ), (const char *) iter->data ); - } - g_list_free_full ( list, g_free ); - - if ( state->text && input ) { - textbox_text ( state->text, input ); - textbox_cursor_end ( state->text ); - } - - // filtered list - state->line_map = g_malloc0_n ( state->num_lines, sizeof ( unsigned int ) ); - state->distance = (int *) g_malloc0_n ( state->num_lines, sizeof ( int ) ); - - xcb_rofi_view_calculate_window_width ( state ); - // Need to resize otherwise calculated desired height is wrong. - widget_resize ( WIDGET ( state->main_window ), state->width, 100 ); - // Only needed when window is fixed size. - if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) { - listview_set_fixed_num_lines ( state->list_view ); - } - - state->height = xcb_rofi_view_calculate_window_height ( state ); - // Move the window to the correct x,y position. - xcb_rofi_view_calculate_window_position ( state ); - xcb_rofi_view_window_update_size ( state ); - - state->quit = FALSE; - rofi_view_refilter ( state ); - xcb_rofi_view_update ( state, TRUE ); - xcb_map_window ( xcb->connection, CacheState.main_window ); - widget_queue_redraw ( WIDGET ( state->main_window ) ); - xcb_flush ( xcb->connection ); - if ( xcb->sncontext != NULL ) { - sn_launchee_context_complete ( xcb->sncontext ); - } - return state; -} - -/** - * @param state The Menu Handle - * - * Check if a finalize function is set, and if sets executes it. - */ -void process_result ( RofiViewState *state ); - -static int xcb_rofi_view_error_dialog ( const char *msg, int markup ) -{ - RofiViewState *state = __rofi_view_state_create (); - state->retv = MENU_CANCEL; - state->menu_flags = MENU_ERROR_DIALOG; - state->finalize = process_result; - - state->main_window = box_create ( NULL, "window", ROFI_ORIENTATION_VERTICAL ); - box *box = box_create ( WIDGET ( state->main_window ), "error-message", ROFI_ORIENTATION_VERTICAL ); - box_add ( state->main_window, WIDGET ( box ), TRUE ); - state->text = textbox_create ( WIDGET ( box ), WIDGET_TYPE_TEXTBOX_TEXT, "textbox", ( TB_AUTOHEIGHT | TB_WRAP ) + ( ( markup ) ? TB_MARKUP : 0 ), - NORMAL, ( msg != NULL ) ? msg : "", 0, 0 ); - box_add ( box, WIDGET ( state->text ), TRUE ); - - // Make sure we enable fixed num lines when in normal window mode. - if ( ( CacheState.flags & MENU_NORMAL_WINDOW ) == MENU_NORMAL_WINDOW ) { - listview_set_fixed_num_lines ( state->list_view ); - } - xcb_rofi_view_calculate_window_width ( state ); - // Need to resize otherwise calculated desired height is wrong. - widget_resize ( WIDGET ( state->main_window ), state->width, 100 ); - // resize window vertically to suit - state->height = widget_get_desired_height ( WIDGET ( state->main_window ) ); - - // Calculte window position. - xcb_rofi_view_calculate_window_position ( state ); - - // Move the window to the correct x,y position. - xcb_rofi_view_window_update_size ( state ); - - // Display it. - xcb_map_window ( xcb->connection, CacheState.main_window ); - widget_queue_redraw ( WIDGET ( state->main_window ) ); - - if ( xcb->sncontext != NULL ) { - sn_launchee_context_complete ( xcb->sncontext ); - } - - // Set it as current window. - xcb_rofi_view_set_active ( state ); - return TRUE; -} - static void xcb_rofi_view_hide ( void ) { if ( CacheState.main_window != XCB_WINDOW_NONE ) { @@ -1659,31 +793,31 @@ static void xcb_rofi_view_hide ( void ) static void xcb_rofi_view_cleanup () { g_debug ( "Cleanup." ); - if ( CacheState.idle_timeout > 0 ) { - g_source_remove ( CacheState.idle_timeout ); - CacheState.idle_timeout = 0; + if ( XcbState.idle_timeout > 0 ) { + g_source_remove ( XcbState.idle_timeout ); + XcbState.idle_timeout = 0; } - if ( CacheState.repaint_source > 0 ) { - g_source_remove ( CacheState.repaint_source ); - CacheState.repaint_source = 0; + if ( XcbState.repaint_source > 0 ) { + g_source_remove ( XcbState.repaint_source ); + XcbState.repaint_source = 0; } - if ( CacheState.fake_bg ) { - cairo_surface_destroy ( CacheState.fake_bg ); - CacheState.fake_bg = NULL; + if ( XcbState.fake_bg ) { + cairo_surface_destroy ( XcbState.fake_bg ); + XcbState.fake_bg = NULL; } - if ( CacheState.edit_draw ) { - cairo_destroy ( CacheState.edit_draw ); - CacheState.edit_draw = NULL; + if ( XcbState.edit_draw ) { + cairo_destroy ( XcbState.edit_draw ); + XcbState.edit_draw = NULL; } - if ( CacheState.edit_surf ) { - cairo_surface_destroy ( CacheState.edit_surf ); - CacheState.edit_surf = NULL; + if ( XcbState.edit_surf ) { + cairo_surface_destroy ( XcbState.edit_surf ); + XcbState.edit_surf = NULL; } if ( CacheState.main_window != XCB_WINDOW_NONE ) { g_debug ( "Unmapping and free'ing window" ); xcb_unmap_window ( xcb->connection, CacheState.main_window ); - xcb_free_gc ( xcb->connection, CacheState.gc ); - xcb_free_pixmap ( xcb->connection, CacheState.edit_pixmap ); + xcb_free_gc ( xcb->connection, XcbState.gc ); + xcb_free_pixmap ( xcb->connection, XcbState.edit_pixmap ); xcb_destroy_window ( xcb->connection, CacheState.main_window ); CacheState.main_window = XCB_WINDOW_NONE; } @@ -1694,61 +828,6 @@ static void xcb_rofi_view_cleanup () xcb_flush ( xcb->connection ); g_assert ( g_queue_is_empty ( &( CacheState.views ) ) ); } -static Mode * xcb_rofi_view_get_mode ( RofiViewState *state ) -{ - return state->sw; -} - -static void xcb_rofi_view_set_overlay ( RofiViewState *state, const char *text ) -{ - if ( state->overlay == NULL || state->list_view == NULL ) { - return; - } - if ( text == NULL ) { - widget_disable ( WIDGET ( state->overlay ) ); - return; - } - widget_enable ( WIDGET ( state->overlay ) ); - textbox_text ( state->overlay, text ); - // We want to queue a repaint. - xcb_rofi_view_queue_redraw ( ); -} - -static void xcb_rofi_view_clear_input ( RofiViewState *state ) -{ - if ( state->text ) { - textbox_text ( state->text, "" ); - xcb_rofi_view_set_selected_line ( state, 0 ); - } -} - -static void xcb_rofi_view_switch_mode ( RofiViewState *state, Mode *mode ) -{ - state->sw = mode; - // Update prompt; - if ( state->prompt ) { - xcb_rofi_view_update_prompt ( state ); - } - if ( state->sw ) { - char * title = g_strdup_printf ( "rofi - %s", mode_get_display_name ( state->sw ) ); - xcb_rofi_view_set_window_title ( title ); - g_free ( title ); - } - else { - xcb_rofi_view_set_window_title ( "rofi" ); - } - if ( state->sidebar_bar ) { - for ( unsigned int j = 0; j < state->num_modi; j++ ) { - const Mode * mode = rofi_get_mode ( j ); - textbox_font ( state->modi[j], ( mode == state->sw ) ? HIGHLIGHT : NORMAL ); - } - } - rofi_view_restart ( state ); - state->reload = TRUE; - state->refilter = TRUE; - rofi_view_refilter ( state ); - xcb_rofi_view_update ( state, TRUE ); -} static xcb_window_t xcb_rofi_view_get_window ( void ) { @@ -1763,31 +842,23 @@ static void xcb_rofi_view_set_window_title ( const char * title ) } static view_proxy view_ = { - .create = xcb_rofi_view_create, - .handle_text = xcb_rofi_view_handle_text, + .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, - .set_selected_line = xcb_rofi_view_set_selected_line, - .trigger_action = xcb_rofi_view_trigger_action, - .free = xcb_rofi_view_free, - .get_active = xcb_rofi_view_get_active, - .set_active = xcb_rofi_view_set_active, - .error_dialog = xcb_rofi_view_error_dialog, .queue_redraw = xcb_rofi_view_queue_redraw, + .set_window_title = xcb_rofi_view_set_window_title, .calculate_window_position = xcb_rofi_view_calculate_window_position, + .calculate_window_width = xcb_rofi_view_calculate_window_width, .calculate_window_height = xcb_rofi_view_calculate_window_height, .window_update_size = xcb_rofi_view_window_update_size, .cleanup = xcb_rofi_view_cleanup, - .get_mode = xcb_rofi_view_get_mode, .hide = xcb_rofi_view_hide, .reload = xcb_rofi_view_reload, - .switch_mode = xcb_rofi_view_switch_mode, - .set_overlay = xcb_rofi_view_set_overlay, - .clear_input = xcb_rofi_view_clear_input, + .__create_window = xcb___create_window, .get_window = xcb_rofi_view_get_window, .get_current_monitor = xcb_rofi_view_get_current_monitor,