diff --git a/gtk_appdata.c b/gtk_appdata.c index 2f143a1..c90c523 100644 --- a/gtk_appdata.c +++ b/gtk_appdata.c @@ -122,7 +122,7 @@ g_print ("app->exename : %s\n" app->appshort = APPSHORT; /* short name, e.g. "GTKwrite" */ app->comment = g_strdup ("// "); - app->ctrl_shift_right_fix = TRUE; /* Use custom key-handler */ + app->ctrl_shift_right_fix = FALSE; /* Use custom key-handler */ app->new_pos = NULL; /* Goto mark, no sep init */ @@ -692,3 +692,70 @@ gboolean create_new_editor_inst (kwinst *app, gchar *fn) return result; } + +/** simplbe boolean stack function using the bits of STKMAX + * unsigned integers to provide stack storage + */ +/** clear stack - zero integers. */ +void bstack_clear (kwinst *app) +{ + app->bindex = 0; + for (gint i = 0; i < STKMAX; i++) + app->bstack[i] = 0; +} + +/** stack_push set bit at bindex to 0 or 1 based on v. + * increment bindex after setting bit. + */ +int bstack_push (kwinst *app, gint v) +{ + guint arrbits = (sizeof app->bstack) * CHAR_BIT, + elebits = (sizeof app->bindex) * CHAR_BIT, + arridx = app->bindex/elebits; + + if (app->bindex == arrbits) { /* check bstack full */ + g_print ("bstack full\n"); + return 0; + } + + if (v) /* set bit at bindex */ + app->bstack[arridx] |= (1u << (app->bindex % elebits)); + else /* clear bit at index */ + app->bstack[arridx] &= ~(1u << (app->bindex % elebits)); + + return ++app->bindex; +} + +/** stack_pop - decrement bindex, get bit at bindex, clear bit. */ +int bstack_pop (kwinst *app) +{ + if (!app->bindex) { /* check bstack empty */ + g_print ("bstack empty\n"); + return -1; + } + + guint elebits = (sizeof app->bindex) * CHAR_BIT, + arridx = --app->bindex/elebits, + /* get bit at bindex */ + v = (app->bstack[arridx] >> (app->bindex % elebits)) & 1; + + /* clear bit at bindex */ + app->bstack[arridx] &= ~(1u << (app->bindex % elebits)); + + return v; +} + +/** stack_last - get bit for last pushed value. */ +int bstack_last (kwinst *app) +{ + if (!app->bindex) { /* check bstack empty */ + g_print ("bstack empty\n"); + return -1; + } + + guint elebits = (sizeof app->bindex) * CHAR_BIT, + arridx = (app->bindex - 1)/elebits; + + /* get bit at bindex */ + return (app->bstack[arridx] >> ((app->bindex - 1) % elebits)) & 1; +} diff --git a/gtk_appdata.h b/gtk_appdata.h index 3da9ede..9866623 100644 --- a/gtk_appdata.h +++ b/gtk_appdata.h @@ -22,6 +22,10 @@ #endif #endif +#ifndef CHAR_BIT +# define CHAR_BIT 8 +#endif + #ifdef WGTKSOURCEVIEW2 #include #include @@ -76,6 +80,7 @@ enum eolorder { LF, CRLF, CR, FILE_EOL, OS_EOL }; enum { IBAR_LABEL_SELECT = 1, IBAR_VIEW_SENSITIVE }; /* infobar flags */ +enum { LEFT, RIGHT, STKMAX = 4 }; /* boolean stack constants */ /* TODO: * look at adding app->status to remove include gtk_statusbar.h @@ -257,6 +262,12 @@ typedef struct { gchar *cfgfile; /* user config file */ GKeyFile *keyfile; /* key_file for config */ + /* boolean stack implementation + * to provide keypress history. + */ + guint bstack[STKMAX]; /* sizeof guint * CHAR_BIT * STKMAX bits */ + guint bindex; /* bit index */ + } kwinst; void context_init (kwinst *app, char **argv); @@ -271,4 +282,10 @@ void delete_mark_last_pos (kwinst *app); gchar *get_posix_filename (const gchar *fn); gboolean create_new_editor_inst (kwinst *app, gchar *fn); +/* boolean stack functions */ +void bstack_clear (kwinst *app); +int bstack_push (kwinst *app, int v); +int bstack_pop (kwinst *app); +int bstack_last (kwinst *app); + #endif diff --git a/gtk_filebuf.c b/gtk_filebuf.c index c537a8a..df820c6 100644 --- a/gtk_filebuf.c +++ b/gtk_filebuf.c @@ -403,16 +403,18 @@ gboolean buffer_deselect_all (kwinst *app) /** buffer_select_to_next_char selects from cursor to next non-ws char. * this corrects the gtk_text_view default selection of whitespace - * plus the next word on the ctrl+shift+right-arrow combination + * plus the next word on the ctrl+shift+right-arrow combination. * - * TODO: implement ctrl+shift+left-arrow event handler to undo this - * handler. (currently starts at beginning and backs up rather than - * starting at end an back-tracking). This fixes the most annoying - * aspect of the default gtk key combination handling. Make optional - * for now in settings->editing. + * the keypress handler must keep track of the number of left or right + * ctrl+shift select keypress sequences so using the arrow-key in the + * opposite direction will undo in the correct direction. the current + * bstack will only keep track of 128 simultaneous ctrl+shift keypress + * events in one direction at a time (or 256 if undoing 128 in the + * opposite direction. increase STKMAX if you need more. */ -gboolean buffer_select_to_next_char (GtkTextBuffer *buf) +gboolean buffer_select_to_next_char (kwinst *app) { + GtkTextBuffer *buf = GTK_TEXT_BUFFER(app->buffer); GtkTextIter start, end; gunichar c; @@ -425,28 +427,56 @@ gboolean buffer_select_to_next_char (GtkTextBuffer *buf) end = start; } - /* get char and check if whitespace or non-whitespace */ - c = gtk_text_iter_get_char (&end); - if (c == ' ' || c == '\t') { - /* read contiguous whitespace to next word */ - while (c == ' ' || c == '\t') { - if (!gtk_text_iter_forward_char (&end)) - break; - c = gtk_text_iter_get_char (&end); + /* check if prev ctrl+shift keypress & if it was RIGHT */ + if (!app->bindex || bstack_last (app) == RIGHT) { + /* get char and check if whitespace or non-whitespace */ + c = gtk_text_iter_get_char (&end); + if (c == ' ' || c == '\t') { + /* read contiguous whitespace to next word */ + while (c == ' ' || c == '\t') { + if (!gtk_text_iter_forward_char (&end)) + break; + c = gtk_text_iter_get_char (&end); + } + } + else { + /* read contiguous non-whitespace */ + while (c != ' ' && c != '\t') { + if (!gtk_text_iter_forward_char (&end)) + break; + c = gtk_text_iter_get_char (&end); + } + /* read contiguous whitespace to next word */ + while (c == ' ' || c == '\t') { + if (!gtk_text_iter_forward_char (&end)) + break; + c = gtk_text_iter_get_char (&end); + } } } else { - /* read contiguous non-whitespace */ - while (c != ' ' && c != '\t') { - if (!gtk_text_iter_forward_char (&end)) - break; - c = gtk_text_iter_get_char (&end); + c = gtk_text_iter_get_char (&start); + if (c == ' ' || c == '\t') { + /* read contiguous whitespace to next word */ + while (c == ' ' || c == '\t') { + if (!gtk_text_iter_forward_char (&start)) + break; + c = gtk_text_iter_get_char (&start); + } + /* read contiguous non-whitespace */ + while (c != ' ' && c != '\t') { + if (!gtk_text_iter_forward_char (&start)) + break; + c = gtk_text_iter_get_char (&start); + } } - /* read contiguous whitespace to next word */ - while (c == ' ' || c == '\t') { - if (!gtk_text_iter_forward_char (&end)) - break; - c = gtk_text_iter_get_char (&end); + else { + /* read contiguous non-whitespace */ + while (c != ' ' && c != '\t') { + if (!gtk_text_iter_forward_char (&start)) + break; + c = gtk_text_iter_get_char (&start); + } } } @@ -456,8 +486,16 @@ gboolean buffer_select_to_next_char (GtkTextBuffer *buf) return TRUE; } -gboolean buffer_select_to_prev_char (GtkTextBuffer *buf) +/** buffer_select_to_next_char selects from cursor to next non-ws char. + * this corrects the gtk_text_view default selection of whitespace + * plus the next word on the ctrl+shift+right-arrow combination. + * + * (see extended note on function above for bstack tracking of + * ctrl+shift keypress events. + */ +gboolean buffer_select_to_prev_char (kwinst *app) { + GtkTextBuffer *buf = GTK_TEXT_BUFFER(app->buffer); GtkTextIter start, end; gunichar c; @@ -470,78 +508,76 @@ gboolean buffer_select_to_prev_char (GtkTextBuffer *buf) end = start; } - /* reduce end until equal to start, then move start */ - if (gtk_text_iter_compare (&start, &end) < 0) { - g_print ("start < end\n"); - gtk_text_iter_backward_char (&end); + /* check if prev ctrl+shift keypress & if it was LEFT */ + if (!app->bindex || bstack_last (app) == LEFT) { + + gtk_text_iter_backward_char (&start); /* get char and check if whitespace or non-whitespace */ - c = gtk_text_iter_get_char (&end); + c = gtk_text_iter_get_char (&start); if (c == ' ' || c == '\t') { /* read contiguous whitespace to next word */ while (c == ' ' || c == '\t') { - if (!gtk_text_iter_backward_char (&end) || + if (!gtk_text_iter_backward_char (&start) || gtk_text_iter_equal (&start, &end)) break; - c = gtk_text_iter_get_char (&end); + c = gtk_text_iter_get_char (&start); } } else { /* read contiguous non-whitespace */ while (c != ' ' && c != '\t') { - if (!gtk_text_iter_backward_char (&end) || + if (!gtk_text_iter_backward_char (&start) || gtk_text_iter_equal (&start, &end)) - goto swapiters; - c = gtk_text_iter_get_char (&end); + goto prevrdlt; + c = gtk_text_iter_get_char (&start); } /* read contiguous whitespace to next word */ while (c == ' ' || c == '\t') { - if (!gtk_text_iter_backward_char (&end) || + if (!gtk_text_iter_backward_char (&start) || gtk_text_iter_equal (&start, &end)) - goto swapiters; - c = gtk_text_iter_get_char (&end); + break; + c = gtk_text_iter_get_char (&start); } - swapiters:; + prevrdlt:; } - if (!gtk_text_iter_equal (&start, &end); - gtk_text_iter_forward_char (&end); - + if (!gtk_text_iter_equal (&start, &end)) + gtk_text_iter_forward_char (&start); } - else { /* no selection or selection toward beginning of file */ - /* TODO change start to end below and include - * gtk_text_iter_forward_char after each loop - * to make sure next loop executes correctly. - * method has promise, work ok until you erase existing selection. - */ - g_print ("start >= end\n"); - gtk_text_iter_backward_char (&start); + else { + + gtk_text_iter_backward_char (&end); /* get char and check if whitespace or non-whitespace */ - c = gtk_text_iter_get_char (&start); + c = gtk_text_iter_get_char (&end); if (c == ' ' || c == '\t') { /* read contiguous whitespace to next word */ while (c == ' ' || c == '\t') { - if (!gtk_text_iter_backward_char (&start)) - break; - c = gtk_text_iter_get_char (&start); + if (!gtk_text_iter_backward_char (&end) || + gtk_text_iter_equal (&start, &end)) + goto prevrdrt; + c = gtk_text_iter_get_char (&end); } - } - else { /* read contiguous non-whitespace */ while (c != ' ' && c != '\t') { - if (!gtk_text_iter_backward_char (&start)) + if (!gtk_text_iter_backward_char (&end) || + gtk_text_iter_equal (&start, &end)) break; - c = gtk_text_iter_get_char (&start); + c = gtk_text_iter_get_char (&end); } + prevrdrt:; + } + else { /* read contiguous whitespace to next word */ - while (c == ' ' || c == '\t') { - if (!gtk_text_iter_backward_char (&start)) + while (c != ' ' || c != '\t') { + if (!gtk_text_iter_backward_char (&end) || + gtk_text_iter_equal (&start, &end)) break; - c = gtk_text_iter_get_char (&start); + c = gtk_text_iter_get_char (&end); } } - gtk_text_iter_forward_char (&start); - + if (!gtk_text_iter_equal (&start, &end)) + gtk_text_iter_forward_char (&end); } /* select range */ diff --git a/gtk_filebuf.h b/gtk_filebuf.h index 0bc7a85..8fc16b0 100644 --- a/gtk_filebuf.h +++ b/gtk_filebuf.h @@ -27,8 +27,8 @@ void file_open (kwinst *app, gchar *filename); void buffer_insert_file (kwinst *app, gchar *filename); gboolean buffer_select_all (kwinst *app); gboolean buffer_deselect_all (kwinst *app); -gboolean buffer_select_to_next_char (GtkTextBuffer *buf); -gboolean buffer_select_to_prev_char (GtkTextBuffer *buf); +gboolean buffer_select_to_next_char (kwinst *app); +gboolean buffer_select_to_prev_char (kwinst *app); void buffer_comment_lines (kwinst *app, GtkTextIter *start, GtkTextIter *end); diff --git a/gtk_windef.c b/gtk_windef.c index b530117..884e63a 100644 --- a/gtk_windef.c +++ b/gtk_windef.c @@ -226,20 +226,38 @@ gboolean on_keypress (GtkWidget *widget, GdkEventKey *event, kwinst *app) switch (event->keyval) { case GDK_KEY_Left: if (app->ctrl_shift_right_fix) { - g_print ("key pressed: %s\n", "ctrl + shift + Right->Arrow"); - return buffer_select_to_prev_char (GTK_TEXT_BUFFER(app->buffer)); + // g_print ("key pressed: %s\n", "ctrl + shift + Left->Arrow"); + gboolean rtn = buffer_select_to_prev_char (app); + /* if no prev ctrl+shift keypress or last was to left + * push another left keypress on stack. + */ + if (!app->bindex || bstack_last (app) == LEFT) + bstack_push (app, LEFT); /* record LEFT selection */ + else /* otherwise, pop left from stack */ + bstack_pop (app); + return rtn; } break; case GDK_KEY_Right: if (app->ctrl_shift_right_fix) { // g_print ("key pressed: %s\n", "ctrl + shift + Right->Arrow"); - return buffer_select_to_next_char (GTK_TEXT_BUFFER(app->buffer)); + gboolean rtn = buffer_select_to_next_char (app); + /* if no prev ctrl+shift keypress or last was to right + * push another right keypress on stack. + */ + if (!app->bindex || bstack_last (app) == RIGHT) + bstack_push (app, RIGHT); /* record RIGHT selection */ + else /* otherwise, pop right from stack */ + bstack_pop (app); + return rtn; } break; } return FALSE; /* return - only process ctrl + shift events */ } + app->bindex = 0; /* zero boolean keypress stack index */ + /* handle control key combinations */ /* if (event->type == GDK_KEY_PRESS &&