Add combi viewer, where (selected) modi can be combined.

- Add active window highlighter.
This commit is contained in:
Dave Davenport 2015-03-31 22:45:02 +02:00
parent 56a888da6f
commit 0d87cf12a3
13 changed files with 220 additions and 102 deletions

View File

@ -1,6 +1,8 @@
0.15.3: (unreleased)
0.15.4: (unreleased)
New feature:
- Number mode for dmenu. allows user to get index back instead of content.
- Combi mode. Combine multiple views into one.
- Highlight current window.
Bug fixes:
- On a single item in list disable auto-select.
Improvements:
@ -13,6 +15,7 @@
- Fix modi switcher boxes size+layout.
- Reduce work on redraws (do not always calculate new size/position), set text, etc.
- OO-ify the switchers.
- Remove unneeded filtered array.
Cleanup:
- Do not lug argc,argv around everywhere.

View File

@ -104,7 +104,10 @@ Settings config = {
/** Lazy mode setting */
.lazy_filter_limit = 5000,
/** auto select */
.auto_select = FALSE,
.parse_hosts = FALSE
.auto_select = FALSE,
/** Parse /etc/hosts file in ssh view. */
.parse_hosts = FALSE,
/** Modi to combine into one view. */
.combi_modi = "window,run"
};

View File

@ -37,6 +37,8 @@ typedef enum
typedef SwitcherMode ( *switcher_callback )( char **input, void *data );
typedef void ( *switcher_free )( Switcher *data );
typedef const char * ( *get_display_value )( unsigned int selected_line, void *data, int *state );
/**
* State returned by the rofi window.
*/
@ -90,7 +92,7 @@ typedef int ( *menu_match_cb )( char **tokens, const char *input, int case_sensi
*/
MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prompt,
menu_match_cb mmc, void *mmc_data,
int *selected_line, int sorting ) __attribute__ ( ( nonnull ( 1, 3, 4, 7 ) ) );
int *selected_line, int sorting, get_display_value mgrv, void *mgrv_data ) __attribute__ ( ( nonnull ( 1, 3, 4, 7 ) ) );
/**
* @param sig The caught signal
*
@ -203,6 +205,8 @@ typedef struct _Settings
unsigned int auto_select;
/** Hosts file parsing */
unsigned int parse_hosts;
/** Combi Switchers */
char *combi_modi;
} Settings;
/** Global Settings structure. */
@ -236,21 +240,23 @@ struct _Switcher
/**
* A switcher normally consists of the following parts:
*/
void ( *init )( struct _Switcher *sw );
char ** ( *get_data )( unsigned int *length, struct _Switcher *pd );
int ( *match )( char **tokens, const char *input, int case_sensitive, int index, struct _Switcher *data );
SwitcherMode ( *result )( int menu_retv, char **input, unsigned int selected_line, struct _Switcher *pd );
void ( *destroy )( struct _Switcher *pd );
void ( *init )( struct _Switcher *sw );
char ** ( *get_data )( unsigned int *length, struct _Switcher *pd );
int ( *match )( char **tokens, const char *input, int case_sensitive, int index, struct _Switcher *data );
SwitcherMode ( *result )( int menu_retv, char **input, unsigned int selected_line, struct _Switcher *pd );
void ( *destroy )( struct _Switcher *pd );
// Token match.
menu_match_cb token_match;
menu_match_cb token_match;
get_display_value mgrv;
// Pointer to private data.
void *private_data;
void *private_data;
// Extra fields for script
void *ed;
void *ed;
// Free SWitcher
switcher_free free;
switcher_free free;
};
#endif

View File

@ -17,6 +17,7 @@ typedef struct
XIM xim;
XIC xic;
PangoLayout *layout;
int tbft;
} textbox;
@ -34,11 +35,15 @@ typedef enum
typedef enum
{
// Render font normally
NORMAL,
NORMAL = 1,
// Alternating row.
ALT,
ALT = 2,
// Render font highlighted (inverted colors.)
HIGHLIGHT,
HIGHLIGHT = 4,
STATE_MASK = ( NORMAL | ALT | HIGHLIGHT ),
BOLD = 8,
} TextBoxFontType;
textbox* textbox_create ( Window parent,

View File

@ -29,8 +29,11 @@
#include <stdio.h>
#include <rofi.h>
extern unsigned int num_switchers;
extern Switcher **switchers;
#include <dialogs/run.h>
#include <dialogs/ssh.h>
#include <dialogs/window.h>
#include <dialogs/script.h>
/**
* Combi Switcher
*/
@ -45,28 +48,64 @@ typedef struct _CombiModePrivateData
// List of switchers to combine.
unsigned int num_switchers;
Switcher **switchers;
char *cache;
} CombiModePrivateData;
static void combi_add_switcher ( Switcher *sw, Switcher *m )
static void combi_mode_parse_switchers ( Switcher *sw )
{
CombiModePrivateData *pd = sw->private_data;
pd->switchers = g_realloc ( pd->switchers, ( pd->num_switchers + 2 ) * sizeof ( Switcher * ) );
pd->switchers[pd->num_switchers] = m;
pd->switchers[pd->num_switchers + 1] = NULL;
pd->num_switchers++;
CombiModePrivateData *pd = sw->private_data;
char *savept = NULL;
// Make a copy, as strtok will modify it.
char *switcher_str = g_strdup ( config.combi_modi );
// Split token on ','. This modifies switcher_str.
for ( char *token = strtok_r ( switcher_str, ",", &savept );
token != NULL;
token = strtok_r ( NULL, ",", &savept ) ) {
// Resize and add entry.
pd->switchers = (Switcher * *) g_realloc ( pd->switchers,
sizeof ( Switcher* ) * ( pd->num_switchers + 1 ) );
// Window switcher.
if ( strcasecmp ( token, "window" ) == 0 ) {
pd->switchers[pd->num_switchers++] = &window_mode;
}
// SSh dialog
else if ( strcasecmp ( token, "ssh" ) == 0 ) {
pd->switchers[pd->num_switchers++] = &ssh_mode;
}
// Run dialog
else if ( strcasecmp ( token, "run" ) == 0 ) {
pd->switchers[pd->num_switchers++] = &run_mode;
}
else {
// If not build in, use custom switchers.
Switcher *sw = script_switcher_parse_setup ( token );
if ( sw != NULL ) {
pd->switchers[pd->num_switchers++] = sw;
}
else{
// Report error, don't continue.
fprintf ( stderr, "Invalid script switcher: %s\n", token );
token = NULL;
}
}
// Keybinding.
}
// Free string that was modified by strtok_r
g_free ( switcher_str );
}
static void combi_mode_init ( Switcher *sw )
{
if ( sw->private_data == NULL ) {
CombiModePrivateData *pd = g_malloc0 ( sizeof ( *pd ) );
sw->private_data = (void *) pd;
for ( unsigned int i = 0; i < num_switchers; i++ ) {
if ( switchers[i] != sw ) {
combi_add_switcher ( sw, switchers[i] );
}
}
combi_mode_parse_switchers ( sw );
pd->starts = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
pd->lengths = g_malloc0 ( sizeof ( int ) * pd->num_switchers );
for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
pd->switchers[i]->init ( pd->switchers[i] );
}
}
}
static char ** combi_mode_get_data ( unsigned int *length, Switcher *sw )
@ -101,8 +140,13 @@ static void combi_mode_destroy ( Switcher *sw )
g_free ( pd->cmd_list );
g_free ( pd->starts );
g_free ( pd->lengths );
// Cleanup switchers.
for ( unsigned int i = 0; i < pd->num_switchers; i++ ) {
pd->switchers[i]->destroy ( pd->switchers[i] );
}
g_free ( pd->switchers );
g_free ( pd );
g_free ( pd->cache );
sw->private_data = NULL;
}
}
@ -138,6 +182,21 @@ static int combi_mode_match ( char **tokens, const char *input,
abort ();
return 0;
}
static const char * combi_mgrv ( unsigned int selected_line, void *sw, int *state )
{
CombiModePrivateData *pd = ( (Switcher *) sw )->private_data;
for ( unsigned i = 0; i < pd->num_switchers; i++ ) {
if ( selected_line >= pd->starts[i] && selected_line < ( pd->starts[i] + pd->lengths[i] ) ) {
g_free ( pd->cache );
pd->cache = g_strdup_printf ( "(%s) %s",
pd->switchers[i]->name,
pd->switchers[i]->mgrv ( selected_line - pd->starts[i], (void *) pd->switchers[i], state ) );
return pd->cache;
}
}
return NULL;
}
Switcher combi_mode =
{
@ -151,6 +210,7 @@ Switcher combi_mode =
.result = combi_mode_result,
.destroy = combi_mode_destroy,
.token_match = combi_mode_match,
.mgrv = combi_mgrv,
.private_data = NULL,
.free = NULL
};

View File

@ -64,6 +64,12 @@ static char **get_dmenu ( int *length )
return retv;
}
static const char *get_display_data ( unsigned int index, void *data, G_GNUC_UNUSED int *state )
{
char **retv = (char * *) data;
return retv[index];
}
int dmenu_switcher_dialog ( char **input )
{
char *dmenu_prompt = "dmenu ";
@ -84,7 +90,7 @@ int dmenu_switcher_dialog ( char **input )
do {
int mretv = menu ( list, length, input, dmenu_prompt,
token_match, NULL, &selected_line, FALSE );
token_match, NULL, &selected_line, FALSE, get_display_data, list );
// We normally do not want to restart the loop.
restart = FALSE;

View File

@ -331,6 +331,12 @@ static void run_mode_destroy ( Switcher *sw )
}
}
static const char *mgrv ( unsigned int selected_line, void *sw, G_GNUC_UNUSED int *state )
{
RunModePrivateData *rmpd = ( (Switcher *) sw )->private_data;
return rmpd->cmd_list[selected_line];
}
Switcher run_mode =
{
.name = "run",
@ -343,6 +349,7 @@ Switcher run_mode =
.result = run_mode_result,
.destroy = run_mode_destroy,
.token_match = token_match,
.mgrv = mgrv,
.private_data = NULL,
.free = NULL
};

View File

@ -163,6 +163,11 @@ static void script_mode_destroy ( Switcher *sw )
sw->private_data = NULL;
}
}
static const char *mgrv ( unsigned int selected_line, void *sw, G_GNUC_UNUSED int *state )
{
ScriptModePrivateData *rmpd = ( (Switcher *) sw )->private_data;
return rmpd->cmd_list[selected_line];
}
Switcher *script_switcher_parse_setup ( const char *str )
{
@ -189,6 +194,7 @@ Switcher *script_switcher_parse_setup ( const char *str )
sw->result = script_mode_result;
sw->destroy = script_mode_destroy;
sw->token_match = token_match;
sw->mgrv = mgrv;
return sw;
}

View File

@ -321,6 +321,12 @@ static void ssh_mode_destroy ( Switcher *sw )
}
}
static const char *mgrv ( unsigned int selected_line, void *sw, G_GNUC_UNUSED int *state )
{
SSHModePrivateData *rmpd = ( (Switcher *) sw )->private_data;
return rmpd->cmd_list[selected_line];
}
Switcher ssh_mode =
{
.name = "ssh",
@ -333,6 +339,7 @@ Switcher ssh_mode =
.result = ssh_mode_result,
.destroy = ssh_mode_destroy,
.token_match = token_match,
.mgrv = mgrv,
.private_data = NULL,
.free = NULL
};

View File

@ -286,6 +286,9 @@ typedef struct _SwitcherModePrivateData
winlist *ids;
int config_i3_mode;
int init;
// Current window.
unsigned int index;
char *cache;
} SwitcherModePrivateData;
static int window_match ( char **tokens, __attribute__( ( unused ) ) const char *input,
@ -496,11 +499,21 @@ static void window_mode_destroy ( Switcher *sw )
winlist_free ( rmpd->ids );
i3_support_free_internals ();
x11_cache_free ();
g_free ( rmpd->cache );
g_free ( rmpd );
sw->private_data = NULL;
}
}
static const char *mgrv ( unsigned int selected_line, void *sw, G_GNUC_UNUSED int *state )
{
SwitcherModePrivateData *rmpd = ( (Switcher *) sw )->private_data;
if ( window_client ( display, rmpd->ids->array[selected_line] )->active ) {
*state = BOLD;
}
return rmpd->cmd_list[selected_line];
}
Switcher window_mode =
{
.name = "window",
@ -513,6 +526,7 @@ Switcher window_mode =
.result = window_mode_result,
.destroy = window_mode_destroy,
.token_match = window_match,
.mgrv = mgrv,
.private_data = NULL,
.free = NULL
};

View File

@ -272,51 +272,52 @@ static Window create_window ( Display *display )
typedef struct MenuState
{
unsigned int menu_lines;
unsigned int max_elements;
unsigned int max_rows;
unsigned int columns;
unsigned int menu_lines;
unsigned int max_elements;
unsigned int max_rows;
unsigned int columns;
// window width,height
unsigned int w, h;
int x, y;
unsigned int element_width;
unsigned int w, h;
int x, y;
unsigned int element_width;
// Update/Refilter list.
int update;
int refilter;
int rchanged;
int cur_page;
int update;
int refilter;
int rchanged;
int cur_page;
// Entries
textbox *text;
textbox *prompt_tb;
textbox *case_indicator;
textbox *arrowbox_top;
textbox *arrowbox_bottom;
textbox **boxes;
char **filtered;
int *distance;
int *line_map;
textbox *text;
textbox *prompt_tb;
textbox *case_indicator;
textbox *arrowbox_top;
textbox *arrowbox_bottom;
textbox **boxes;
int *distance;
int *line_map;
unsigned int num_lines;
unsigned int num_lines;
// Selected element.
unsigned int selected;
unsigned int filtered_lines;
unsigned int selected;
unsigned int filtered_lines;
// Last offset in paginating.
unsigned int last_offset;
unsigned int last_offset;
KeySym prev_key;
Time last_button_press;
KeySym prev_key;
Time last_button_press;
int quit;
int init;
int quit;
int init;
// Return state
int *selected_line;
MenuReturn retv;
char **lines;
int line_height;
int *selected_line;
MenuReturn retv;
char **lines;
int line_height;
get_display_value mgrv;
void *mgrv_data;
}MenuState;
/**
@ -337,7 +338,6 @@ static void menu_free_state ( MenuState *state )
}
g_free ( state->boxes );
g_free ( state->filtered );
g_free ( state->line_map );
g_free ( state->distance );
}
@ -575,15 +575,9 @@ static void menu_keyboard_navigation ( MenuState *state, KeySym key, unsigned in
}
else if ( key == XK_Tab ) {
if ( state->filtered_lines == 1 ) {
if ( state->filtered[state->selected] ) {
state->retv = MENU_OK;
*( state->selected_line ) = state->line_map[state->selected];
state->quit = 1;
}
else{
fprintf ( stderr, "We should never hit this." );
abort ();
}
state->retv = MENU_OK;
*( state->selected_line ) = state->line_map[state->selected];
state->quit = 1;
return;
}
@ -636,7 +630,7 @@ static void menu_keyboard_navigation ( MenuState *state, KeySym key, unsigned in
}
else if ( key == XK_space && ( modstate & ControlMask ) == ControlMask ) {
// If a valid item is selected, return that..
if ( state->selected < state->filtered_lines && state->filtered[state->selected] != NULL ) {
if ( state->selected < state->filtered_lines ) {
textbox_text ( state->text, state->lines[state->line_map[state->selected]] );
textbox_cursor_end ( state->text );
state->update = TRUE;
@ -753,13 +747,6 @@ static void menu_refilter ( MenuState *state, char **lines, menu_match_cb mmc, v
if ( sorting ) {
qsort_r ( state->line_map, j, sizeof ( int ), lev_sort, state->distance );
}
// Update the filtered list.
for ( unsigned int i = 0; i < j; i++ ) {
state->filtered[i] = lines[state->line_map[i]];
}
for ( unsigned int i = j; i < state->num_lines; i++ ) {
state->filtered[i] = NULL;
}
// Cleanup + bookkeeping.
state->filtered_lines = j;
@ -767,7 +754,6 @@ static void menu_refilter ( MenuState *state, char **lines, menu_match_cb mmc, v
}
else{
for ( unsigned int i = 0; i < state->num_lines; i++ ) {
state->filtered[i] = lines[i];
state->line_map[i] = i;
}
state->filtered_lines = state->num_lines;
@ -841,9 +827,10 @@ static void menu_draw ( MenuState *state )
ex + x_offset, ey + y_offset,
element_width, element_height );
{
TextBoxFontType type = ( ( ( i % state->max_rows ) & 1 ) == 0 ) ? NORMAL : ALT;
char *text = state->filtered[i + offset];
TextBoxFontType tbft = ( i + offset ) == state->selected ? HIGHLIGHT : type;
TextBoxFontType type = ( ( ( i % state->max_rows ) & 1 ) == 0 ) ? NORMAL : ALT;
int fstate = 0;
const char *text = state->mgrv ( state->line_map[i + offset], state->mgrv_data, &fstate );
TextBoxFontType tbft = fstate | ( ( i + offset ) == state->selected ? HIGHLIGHT : type );
textbox_font ( state->boxes[i], tbft );
textbox_text ( state->boxes[i], text );
}
@ -855,8 +842,10 @@ static void menu_draw ( MenuState *state )
else{
// Only do basic redrawing + highlight of row.
for ( i = 0; i < max_elements; i++ ) {
TextBoxFontType type = ( ( ( i % state->max_rows ) & 1 ) == 0 ) ? NORMAL : ALT;
TextBoxFontType tbft = ( i + offset ) == state->selected ? HIGHLIGHT : type;
TextBoxFontType type = ( ( ( i % state->max_rows ) & 1 ) == 0 ) ? NORMAL : ALT;
int fstate = 0;
state->mgrv ( state->line_map[i + offset], state->mgrv_data, &fstate );
TextBoxFontType tbft = fstate | ( ( i + offset ) == state->selected ? HIGHLIGHT : type );
textbox_font ( state->boxes[i], tbft );
textbox_draw ( state->boxes[i] );
}
@ -924,7 +913,8 @@ static void menu_paste ( MenuState *state, XSelectionEvent *xse )
}
MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prompt,
menu_match_cb mmc, void *mmc_data, int *selected_line, int sorting )
menu_match_cb mmc, void *mmc_data, int *selected_line, int sorting,
get_display_value mgrv, void *mgrv_data )
{
int shift = FALSE;
MenuState state = {
@ -940,11 +930,13 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
.filtered_lines = 0,
.max_elements = 0,
// We want to filter on the first run.
.refilter = TRUE,
.update = FALSE,
.rchanged = TRUE,
.cur_page = -1,
.lines = lines
.refilter = TRUE,
.update = FALSE,
.rchanged = TRUE,
.cur_page = -1,
.lines = lines,
.mgrv = mgrv,
.mgrv_data = mgrv_data
};
unsigned int i;
workarea mon;
@ -1036,7 +1028,6 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
config.padding + state.max_rows * element_height + LINE_MARGIN );
// filtered list
state.filtered = (char * *) g_malloc0_n ( state.num_lines, sizeof ( char* ) );
state.line_map = g_malloc0_n ( state.num_lines, sizeof ( int ) );
if ( sorting ) {
state.distance = (int *) g_malloc0_n ( state.num_lines, sizeof ( int ) );
@ -1212,7 +1203,7 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
// Special delete entry command.
else if ( ( ( ev.xkey.state & ShiftMask ) == ShiftMask ) &&
key == XK_Delete ) {
if ( state.filtered[state.selected] != NULL ) {
if ( state.selected < state.filtered_lines ) {
*( state.selected_line ) = state.line_map[state.selected];
state.retv = MENU_ENTRY_DELETE;
state.quit = TRUE;
@ -1226,7 +1217,7 @@ MenuReturn menu ( char **lines, unsigned int num_lines, char **input, char *prom
shift = ( ( ev.xkey.state & ShiftMask ) == ShiftMask );
// If a valid item is selected, return that..
if ( state.selected < state.filtered_lines && state.filtered[state.selected] != NULL ) {
if ( state.selected < state.filtered_lines ) {
*( state.selected_line ) = state.line_map[state.selected];
if ( strlen ( state.text->text ) > 0 && rc == -2 ) {
state.retv = MENU_CUSTOM_INPUT;
@ -1836,7 +1827,9 @@ SwitcherMode switcher_run ( char **input, Switcher *sw )
input, prompt,
sw->token_match, sw,
&selected_line,
config.levenshtein_sort );
config.levenshtein_sort,
sw->mgrv,
sw );
SwitcherMode retv = sw->result ( mretv, input, selected_line, sw );

View File

@ -78,10 +78,6 @@ textbox* textbox_create ( Window parent,
tb->layout = pango_layout_new ( p_context );
PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font );
pango_layout_set_font_description ( tb->layout, pfd );
pango_font_description_free ( pfd );
unsigned int cp;
switch ( tbft )
{
@ -104,6 +100,8 @@ textbox* textbox_create ( Window parent,
InputOutput, vinfo->visual, CWColormap | CWBorderPixel | CWBackPixel, &attr );
// need to preload the font to calc line height
// Force update of font descriptor.
tb->tbft = ~tbft;
textbox_font ( tb, tbft );
textbox_text ( tb, text ? text : "" );
@ -129,7 +127,15 @@ textbox* textbox_create ( Window parent,
// set an Xft font by name
void textbox_font ( textbox *tb, TextBoxFontType tbft )
{
switch ( tbft )
if ( ( tbft & BOLD ) != ( tb->tbft & BOLD ) ) {
PangoFontDescription *pfd = pango_font_description_from_string ( config.menu_font );
if ( ( tbft & BOLD ) == BOLD ) {
pango_font_description_set_weight ( pfd, PANGO_WEIGHT_BOLD );
}
pango_layout_set_font_description ( tb->layout, pfd );
pango_font_description_free ( pfd );
}
switch ( ( tbft & STATE_MASK ) )
{
case HIGHLIGHT:
tb->color_bg = color_hlbg;
@ -145,6 +151,7 @@ void textbox_font ( textbox *tb, TextBoxFontType tbft )
tb->color_fg = color_fg;
break;
}
tb->tbft = tbft;
}
// set the default text to display

View File

@ -105,7 +105,8 @@ static XrmOption xrmOptions[] = {
{ xrm_Number, "lazy-filter-limit", { .num = &config.lazy_filter_limit }, NULL },
{ xrm_SNumber, "eh", { .snum = &config.element_height }, NULL },
{ xrm_Boolean, "auto-select", { .num = &config.auto_select }, NULL },
{ xrm_Boolean, "parse-hosts", { .num = &config.parse_hosts }, NULL }
{ xrm_Boolean, "parse-hosts", { .num = &config.parse_hosts }, NULL },
{ xrm_String, "combi-modi", { .str = &config.combi_modi }, NULL }
};
// Dynamic options.