mirror of
https://github.com/davatorium/rofi.git
synced 2024-11-25 13:55:34 -05:00
Using Glib's utf8 functions to do string collating.
* casefold and use utf-8 collating. * use g_utf8_next/prev for moving cursor.
This commit is contained in:
parent
2d87f12c44
commit
280c3d7f7f
4 changed files with 71 additions and 64 deletions
|
@ -2,21 +2,12 @@
|
||||||
#define __SIMPLESWITCHER_H__
|
#define __SIMPLESWITCHER_H__
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <X11/X.h>
|
#include <X11/X.h>
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
#define MAX( a, b ) ( ( a ) > ( b ) ? ( a ) : ( b ) )
|
|
||||||
#define MIN( a, b ) ( ( a ) < ( b ) ? ( a ) : ( b ) )
|
|
||||||
#define NEAR( a, o, b ) ( ( b ) > ( a ) - ( o ) && ( b ) < ( a ) + ( o ) )
|
#define NEAR( a, o, b ) ( ( b ) > ( a ) - ( o ) && ( b ) < ( a ) + ( o ) )
|
||||||
#define OVERLAP( a, b, c, d ) ( ( ( a ) == ( c ) && ( b ) == ( d ) ) || MIN ( ( a ) + ( b ), ( c ) + ( d ) ) - MAX ( ( a ), ( c ) ) > 0 )
|
#define OVERLAP( a, b, c, d ) ( ( ( a ) == ( c ) && ( b ) == ( d ) ) || MIN ( ( a ) + ( b ), ( c ) + ( d ) ) - MAX ( ( a ), ( c ) ) > 0 )
|
||||||
#define INTERSECT( x, y, w, h, x1, y1, w1, h1 ) ( OVERLAP ( ( x ), ( w ), ( x1 ), ( w1 ) ) && OVERLAP ( ( y ), ( h ), ( y1 ), ( h1 ) ) )
|
#define INTERSECT( x, y, w, h, x1, y1, w1, h1 ) ( OVERLAP ( ( x ), ( w ), ( x1 ), ( w1 ) ) && OVERLAP ( ( y ), ( h ), ( y1 ), ( h1 ) ) )
|
||||||
|
|
||||||
#ifndef TRUE
|
|
||||||
#define TRUE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FALSE
|
|
||||||
#define FALSE 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern const char *cache_dir;
|
extern const char *cache_dir;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -139,15 +139,18 @@ int token_match ( char **tokens, const char *input,
|
||||||
__attribute__( ( unused ) ) int index,
|
__attribute__( ( unused ) ) int index,
|
||||||
__attribute__( ( unused ) ) void *data )
|
__attribute__( ( unused ) ) void *data )
|
||||||
{
|
{
|
||||||
int match = 1;
|
int match = 1;
|
||||||
|
|
||||||
|
char *lowerc = g_utf8_casefold ( input, -1 );
|
||||||
|
char *compk = g_utf8_collate_key ( lowerc, -1 );
|
||||||
// Do a tokenized match.
|
// Do a tokenized match.
|
||||||
if ( tokens ) {
|
if ( tokens ) {
|
||||||
for ( int j = 1; match && tokens[j]; j++ ) {
|
for ( int j = 0; match && tokens[j]; j++ ) {
|
||||||
match = ( strcasestr ( input, tokens[j] ) != NULL );
|
match = ( strstr ( compk, tokens[j] ) != NULL );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
g_free ( lowerc );
|
||||||
|
g_free ( compk );
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -161,27 +164,28 @@ static char **tokenize ( const char *input )
|
||||||
char *saveptr = NULL, *token;
|
char *saveptr = NULL, *token;
|
||||||
char **retv = NULL;
|
char **retv = NULL;
|
||||||
// First entry is always full (modified) stringtext.
|
// First entry is always full (modified) stringtext.
|
||||||
int num_tokens = 1;
|
int num_tokens = 0;
|
||||||
|
|
||||||
//First entry is string that is modified.
|
// Copy the string, 'strtok_r' modifies it.
|
||||||
retv = malloc ( 2 * sizeof ( char* ) );
|
char *str = strdup ( input );
|
||||||
retv[0] = strdup ( input );
|
|
||||||
retv[1] = NULL;
|
|
||||||
|
|
||||||
// Iterate over tokens.
|
// Iterate over tokens.
|
||||||
// strtok should still be valid for utf8.
|
// strtok should still be valid for utf8.
|
||||||
for ( token = strtok_r ( retv[0], " ", &saveptr );
|
for ( token = strtok_r ( str, " ", &saveptr );
|
||||||
token != NULL;
|
token != NULL;
|
||||||
token = strtok_r ( NULL, " ", &saveptr ) ) {
|
token = strtok_r ( NULL, " ", &saveptr ) ) {
|
||||||
char **tr = realloc ( retv, sizeof ( char* ) * ( num_tokens + 2 ) );
|
char **tr = realloc ( retv, sizeof ( char* ) * ( num_tokens + 2 ) );
|
||||||
if ( tr != NULL ) {
|
if ( tr != NULL ) {
|
||||||
|
char *tmp = g_utf8_casefold ( token, -1 );
|
||||||
retv = tr;
|
retv = tr;
|
||||||
retv[num_tokens + 1] = NULL;
|
retv[num_tokens + 1] = NULL;
|
||||||
retv[num_tokens] = token;
|
retv[num_tokens] = g_utf8_collate_key ( tmp, -1 );
|
||||||
num_tokens++;
|
num_tokens++;
|
||||||
|
g_free ( tmp );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Free str.
|
||||||
|
free ( str );
|
||||||
return retv;
|
return retv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +195,10 @@ static inline void tokenize_free ( char **ip )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
free ( ip[0] );
|
// Free with g_free.
|
||||||
|
for ( int i = 0; ip[i] != NULL; i++ ) {
|
||||||
|
g_free ( ip[i] );
|
||||||
|
}
|
||||||
free ( ip );
|
free ( ip );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -785,7 +792,8 @@ void menu_draw ( textbox **boxes,
|
||||||
|
|
||||||
// selected row is always visible.
|
// selected row is always visible.
|
||||||
// If selected is visible do not scroll.
|
// If selected is visible do not scroll.
|
||||||
if ( ( selected - ( *last_offset ) ) < ( max_elements ) && ( selected - ( *last_offset ) ) >= 0 ) {
|
if ( ( ( selected - ( *last_offset ) ) < ( max_elements ) )
|
||||||
|
&& ( ( selected - ( *last_offset ) ) >= 0 ) ) {
|
||||||
offset = *last_offset;
|
offset = *last_offset;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
@ -883,25 +891,38 @@ int window_match ( char **tokens, __attribute__( ( unused ) ) const char *input,
|
||||||
winlist *ids = ( winlist * ) data;
|
winlist *ids = ( winlist * ) data;
|
||||||
client *c = window_client ( ids->array[index] );
|
client *c = window_client ( ids->array[index] );
|
||||||
|
|
||||||
|
|
||||||
if ( tokens ) {
|
if ( tokens ) {
|
||||||
for ( int j = 1; match && tokens[j]; j++ ) {
|
for ( int j = 0; match && tokens[j]; j++ ) {
|
||||||
int test = 0;
|
int test = 0;
|
||||||
|
|
||||||
|
char *sml = g_utf8_casefold ( c->title, -1 );
|
||||||
|
char *key = g_utf8_collate_key ( sml, -1 );
|
||||||
if ( !test && c->title[0] != '\0' ) {
|
if ( !test && c->title[0] != '\0' ) {
|
||||||
test = ( strcasestr ( c->title, tokens[j] ) != NULL );
|
test = ( strstr ( key, tokens[j] ) != NULL );
|
||||||
}
|
}
|
||||||
|
g_free ( sml ); g_free ( key );
|
||||||
|
|
||||||
|
sml = g_utf8_casefold ( c->class, -1 );
|
||||||
|
key = g_utf8_collate_key ( sml, -1 );
|
||||||
if ( !test && c->class[0] != '\0' ) {
|
if ( !test && c->class[0] != '\0' ) {
|
||||||
test = ( strcasestr ( c->class, tokens[j] ) != NULL );
|
test = ( strstr ( key, tokens[j] ) != NULL );
|
||||||
}
|
}
|
||||||
|
g_free ( sml ); g_free ( key );
|
||||||
|
|
||||||
|
sml = g_utf8_casefold ( c->role, -1 );
|
||||||
|
key = g_utf8_collate_key ( sml, -1 );
|
||||||
if ( !test && c->role[0] != '\0' ) {
|
if ( !test && c->role[0] != '\0' ) {
|
||||||
test = ( strcasestr ( c->role, tokens[j] ) != NULL );
|
test = ( strstr ( key, tokens[j] ) != NULL );
|
||||||
}
|
}
|
||||||
|
g_free ( sml ); g_free ( key );
|
||||||
|
|
||||||
|
sml = g_utf8_casefold ( c->name, -1 );
|
||||||
|
key = g_utf8_collate_key ( sml, -1 );
|
||||||
if ( !test && c->name[0] != '\0' ) {
|
if ( !test && c->name[0] != '\0' ) {
|
||||||
test = ( strcasestr ( c->name, tokens[j] ) != NULL );
|
test = ( strstr ( key, tokens[j] ) != NULL );
|
||||||
}
|
}
|
||||||
|
g_free ( sml ); g_free ( key );
|
||||||
|
|
||||||
if ( test == 0 ) {
|
if ( test == 0 ) {
|
||||||
match = 0;
|
match = 0;
|
||||||
|
@ -1739,7 +1760,7 @@ static void run_switcher ( int do_fork, SwitcherMode mode )
|
||||||
// strangeness...
|
// strangeness...
|
||||||
if ( do_fork == TRUE ) {
|
if ( do_fork == TRUE ) {
|
||||||
if ( fork () ) {
|
if ( fork () ) {
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
display = XOpenDisplay ( 0 );
|
display = XOpenDisplay ( 0 );
|
||||||
|
@ -2231,9 +2252,9 @@ int main ( int argc, char *argv[] )
|
||||||
}
|
}
|
||||||
else if ( find_arg ( argc, argv, "-dmenu" ) >= 0 ) {
|
else if ( find_arg ( argc, argv, "-dmenu" ) >= 0 ) {
|
||||||
find_arg_str ( argc, argv, "-p", &dmenu_prompt );
|
find_arg_str ( argc, argv, "-p", &dmenu_prompt );
|
||||||
int retv = run_dmenu();
|
int retv = run_dmenu ();
|
||||||
// User cancelled the operation.
|
// User cancelled the operation.
|
||||||
if(retv == FALSE) {
|
if ( retv == FALSE ) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,7 @@ static int sort_func ( const void *a, const void *b )
|
||||||
{
|
{
|
||||||
const char *astr = *( const char * const * ) a;
|
const char *astr = *( const char * const * ) a;
|
||||||
const char *bstr = *( const char * const * ) b;
|
const char *bstr = *( const char * const * ) b;
|
||||||
return strcasecmp ( astr, bstr );
|
return g_utf8_collate ( astr, bstr );
|
||||||
}
|
}
|
||||||
static char ** get_ssh ( unsigned int *length )
|
static char ** get_ssh ( unsigned int *length )
|
||||||
{
|
{
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
|
|
||||||
#include "rofi.h"
|
#include "rofi.h"
|
||||||
#include "textbox.h"
|
#include "textbox.h"
|
||||||
|
#include <glib.h>
|
||||||
#define SIDE_MARGIN 2
|
#define SIDE_MARGIN 2
|
||||||
|
|
||||||
|
|
||||||
|
@ -132,8 +133,12 @@ void textbox_text ( textbox *tb, char *text )
|
||||||
if ( tb->text ) {
|
if ( tb->text ) {
|
||||||
free ( tb->text );
|
free ( tb->text );
|
||||||
}
|
}
|
||||||
|
if ( g_utf8_validate ( text, -1, NULL ) ) {
|
||||||
tb->text = strdup ( text );
|
tb->text = strdup ( text );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tb->text = strdup ( "Invalid UTF-8 string." );
|
||||||
|
}
|
||||||
pango_layout_set_text ( tb->layout, tb->text, strlen ( tb->text ) );
|
pango_layout_set_text ( tb->layout, tb->text, strlen ( tb->text ) );
|
||||||
|
|
||||||
tb->cursor = MAX ( 0, MIN ( ( int ) strlen ( text ), tb->cursor ) );
|
tb->cursor = MAX ( 0, MIN ( ( int ) strlen ( text ), tb->cursor ) );
|
||||||
|
@ -206,7 +211,7 @@ void textbox_free ( textbox *tb )
|
||||||
if ( tb->text ) {
|
if ( tb->text ) {
|
||||||
free ( tb->text );
|
free ( tb->text );
|
||||||
}
|
}
|
||||||
if ( tb->layout == NULL ) {
|
if ( tb->layout != NULL ) {
|
||||||
g_object_unref ( tb->layout );
|
g_object_unref ( tb->layout );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,17 +277,6 @@ void textbox_draw ( textbox *tb )
|
||||||
XFreePixmap ( display, canvas );
|
XFreePixmap ( display, canvas );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static size_t nextrune ( textbox *tb, int inc )
|
|
||||||
{
|
|
||||||
ssize_t n;
|
|
||||||
|
|
||||||
/* return location of next utf8 rune in the given direction (+1 or -1) */
|
|
||||||
for ( n = tb->cursor + inc; n + inc >= 0 && ( tb->text[n] & 0xc0 ) == 0x80; n += inc ) {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
// cursor handling for edit mode
|
// cursor handling for edit mode
|
||||||
void textbox_cursor ( textbox *tb, int pos )
|
void textbox_cursor ( textbox *tb, int pos )
|
||||||
{
|
{
|
||||||
|
@ -292,16 +286,17 @@ void textbox_cursor ( textbox *tb, int pos )
|
||||||
// move right
|
// move right
|
||||||
void textbox_cursor_inc ( textbox *tb )
|
void textbox_cursor_inc ( textbox *tb )
|
||||||
{
|
{
|
||||||
textbox_cursor ( tb, nextrune ( tb, 1 ) );
|
int index = g_utf8_next_char ( &( tb->text[tb->cursor] ) ) - tb->text;
|
||||||
|
textbox_cursor ( tb, index );
|
||||||
}
|
}
|
||||||
|
|
||||||
// move left
|
// move left
|
||||||
void textbox_cursor_dec ( textbox *tb )
|
void textbox_cursor_dec ( textbox *tb )
|
||||||
{
|
{
|
||||||
textbox_cursor ( tb, nextrune ( tb, -1 ) );
|
int index = g_utf8_prev_char ( &( tb->text[tb->cursor] ) ) - tb->text;
|
||||||
|
textbox_cursor ( tb, index );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// end of line
|
// end of line
|
||||||
void textbox_cursor_end ( textbox *tb )
|
void textbox_cursor_end ( textbox *tb )
|
||||||
{
|
{
|
||||||
|
@ -337,8 +332,8 @@ void textbox_delete ( textbox *tb, int pos, int dlen )
|
||||||
// delete on character
|
// delete on character
|
||||||
void textbox_cursor_del ( textbox *tb )
|
void textbox_cursor_del ( textbox *tb )
|
||||||
{
|
{
|
||||||
int del_r = nextrune ( tb, 1 );
|
int index = g_utf8_next_char ( &( tb->text[tb->cursor] ) ) - tb->text;
|
||||||
textbox_delete ( tb, tb->cursor, del_r - tb->cursor );
|
textbox_delete ( tb, tb->cursor, index - tb->cursor );
|
||||||
}
|
}
|
||||||
|
|
||||||
// back up and delete one character
|
// back up and delete one character
|
||||||
|
@ -370,46 +365,46 @@ int textbox_keypress ( textbox *tb, XEvent *ev )
|
||||||
|
|
||||||
// Left or Ctrl-b
|
// Left or Ctrl-b
|
||||||
if ( key == XK_Left ||
|
if ( key == XK_Left ||
|
||||||
(( ev->xkey.state&ControlMask) && key == XK_b) ) {
|
( ( ev->xkey.state & ControlMask ) && key == XK_b ) ) {
|
||||||
textbox_cursor_dec ( tb );
|
textbox_cursor_dec ( tb );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Right or Ctrl-F
|
// Right or Ctrl-F
|
||||||
else if ( key == XK_Right ||
|
else if ( key == XK_Right ||
|
||||||
(( ev->xkey.state&ControlMask) && key == XK_f) ) {
|
( ( ev->xkey.state & ControlMask ) && key == XK_f ) ) {
|
||||||
textbox_cursor_inc ( tb );
|
textbox_cursor_inc ( tb );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Delete or Ctrl-D
|
// Delete or Ctrl-D
|
||||||
else if ( key == XK_Delete ||
|
else if ( key == XK_Delete ||
|
||||||
(( ev->xkey.state&ControlMask) && key == XK_d) ) {
|
( ( ev->xkey.state & ControlMask ) && key == XK_d ) ) {
|
||||||
textbox_cursor_del ( tb );
|
textbox_cursor_del ( tb );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Ctrl-U: Kill from the beginning to the end of the line.
|
// Ctrl-U: Kill from the beginning to the end of the line.
|
||||||
else if ( ( ev->xkey.state&ControlMask) && key == XK_u) {
|
else if ( ( ev->xkey.state & ControlMask ) && key == XK_u ) {
|
||||||
textbox_text( tb, "");
|
textbox_text ( tb, "" );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Ctrl-A
|
// Ctrl-A
|
||||||
else if ( ( ev->xkey.state&ControlMask) && key == XK_a) {
|
else if ( ( ev->xkey.state & ControlMask ) && key == XK_a ) {
|
||||||
textbox_cursor ( tb, 0 );
|
textbox_cursor ( tb, 0 );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Ctrl-E
|
// Ctrl-E
|
||||||
else if ( ( ev->xkey.state&ControlMask) && key == XK_e) {
|
else if ( ( ev->xkey.state & ControlMask ) && key == XK_e ) {
|
||||||
textbox_cursor_end ( tb );
|
textbox_cursor_end ( tb );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// BackSpace, Ctrl-h
|
// BackSpace, Ctrl-h
|
||||||
else if ( key == XK_BackSpace ||
|
else if ( key == XK_BackSpace ||
|
||||||
(( ev->xkey.state&ControlMask) && key == XK_h) ) {
|
( ( ev->xkey.state & ControlMask ) && key == XK_h ) ) {
|
||||||
textbox_cursor_bkspc ( tb );
|
textbox_cursor_bkspc ( tb );
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else if ( key == XK_Return || key == XK_KP_Enter ||
|
else if ( key == XK_Return || key == XK_KP_Enter ||
|
||||||
((ev->xkey.state&ControlMask) && key == XK_j) ||
|
( ( ev->xkey.state & ControlMask ) && key == XK_j ) ||
|
||||||
((ev->xkey.state&ControlMask) && key == XK_m)) {
|
( ( ev->xkey.state & ControlMask ) && key == XK_m ) ) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else if ( !iscntrl ( *pad ) ) {
|
else if ( !iscntrl ( *pad ) ) {
|
||||||
|
|
Loading…
Reference in a new issue