diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-02-15 01:32:05 +0300 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2025-02-23 04:52:39 +0300 |
commit | 428dafa568923d5632101c716fb20a3de35d27be (patch) | |
tree | 475b270fc384d5040e4711e155e47d580c52b1a3 | |
parent | 980dcdaa138a42830af4e2533b7e970d7f5fa3cf (diff) | |
download | sciteco-428dafa568923d5632101c716fb20a3de35d27be.tar.gz |
support mouse interaction with popup windows
* Curses allows scrolling with the scroll wheel at least
if mouse support is enabled via ED flags.
Gtk always supported that.
* Allow clicking on popup entries to fully autocomplete them.
Since this behavior - just like auto completions - is parser state-dependant,
I introduced a new state method (insert_completion_cb).
All the implementations are currently in cmdline.c since there is some overlap
with the process_edit_cmd_cb implementations.
* Fixed pressing undefined function keys while showing the popup.
The popup area is no longer redrawn/replaced with the Scintilla view.
Instead, continue to show the popup.
-rw-r--r-- | README | 5 | ||||
-rw-r--r-- | doc/sciteco.7.template | 22 | ||||
-rw-r--r-- | src/cmdline.c | 149 | ||||
-rw-r--r-- | src/file-utils.c | 2 | ||||
-rw-r--r-- | src/glob.h | 3 | ||||
-rw-r--r-- | src/goto-commands.c | 8 | ||||
-rw-r--r-- | src/help.c | 6 | ||||
-rw-r--r-- | src/interface-curses/curses-info-popup.c | 68 | ||||
-rw-r--r-- | src/interface-curses/curses-info-popup.h | 11 | ||||
-rw-r--r-- | src/interface-curses/interface.c | 74 | ||||
-rw-r--r-- | src/interface-gtk/gtk-info-popup.c | 41 | ||||
-rw-r--r-- | src/interface-gtk/gtk-label.c | 6 | ||||
-rw-r--r-- | src/interface-gtk/gtk-label.h | 1 | ||||
-rw-r--r-- | src/interface-gtk/interface.c | 44 | ||||
-rw-r--r-- | src/interface.h | 4 | ||||
-rw-r--r-- | src/parser.c | 5 | ||||
-rw-r--r-- | src/parser.h | 32 | ||||
-rw-r--r-- | src/qreg-commands.h | 7 | ||||
-rw-r--r-- | src/qreg.c | 13 | ||||
-rw-r--r-- | src/rb3str.c | 2 | ||||
-rw-r--r-- | src/ring.c | 2 | ||||
-rw-r--r-- | src/symbols.c | 8 |
22 files changed, 454 insertions, 59 deletions
@@ -44,7 +44,7 @@ master branch and between releases until version 3.0 is released.__ Features ======== -* Supports most of the [Video TECO](http://www.copters.com/teco.html) commands +* Supports most of the [Video TECO](http://www.copters.com/teco.html) commands. * Improved parser compared to classic TECOs, making SciTECO more similar to other imperative languages. * Operator precedence in arithmetic expressions and an argument stack that may be modified @@ -57,7 +57,8 @@ Features by the standard library `fnkeys.tes`. In fact, all keys with printable representation and control keys can be remapped using key macros - and they can be context-sensitive as well! -* Scriptable mouse support via the key macro mechanism (see also `fnkeys.tes`) +* There is scriptable mouse support via the key macro mechanism (see also `fnkeys.tes`). + Autocompletion popups can also be scrolled and clicked. * Many TECO-11 features, like that most commands have a colon-modified form, string-building characters, exotic match characters... * Interactivity: Immediate searching (similar to search-as-you-type) and diff --git a/doc/sciteco.7.template b/doc/sciteco.7.template index 97e1fd7..95c3503 100644 --- a/doc/sciteco.7.template +++ b/doc/sciteco.7.template @@ -303,7 +303,7 @@ current parser state (see below). .SCITECO_TOPIC ^KMOUSE .B ^KMOUSE Mouse event occurred. -This may not be delivered unless bit 7 (64) is set in the +This will not be delivered on Curses unless bit 7 (64) is set in the \fBED\fP flags. You can use \fBEJ\fP with negative keys to retrieve the event type, mouse coordinates and other information @@ -755,10 +755,12 @@ Global and local Q-Registers are not affected by command line termination. .SS Auto Completion . .SCITECO_TOPIC autocomplete -The immediate editing commands that perform auto-completions, do +The immediate editing commands, that perform auto-completions, do so in a manner similar to Posix shells. Upon first invocation they try to fully or partially complete the file name (or token). +If the token can be fully completed, the current command or Q-Register +specification will also usually be terminated automatically. If no completion can be performed, the invocation will display a list of file names (or tokens) that begin with the token to complete in \*(ST's popup area. @@ -770,6 +772,15 @@ of file names or tokens is displayed in the popup area. I.e. it is possible to cycle through long lists of possible auto-completions. .LP +You can also scroll through the popup area with the mouse wheel. +Furthermore, you can click on entries in the popup area with the +mouse in order to fully complete them. +Often this will also automatically terminate the current command or +Q-Register specification, just like an unambiguous completion via +immediate editing commands. +On Curses, mouse events are not processed unless bit 7 (64) is set +in the \fBED\fP flags. +.LP When completing file names, hidden files are not considered for completion unless a prefix of the hidden file's name has already been typed. @@ -789,6 +800,13 @@ file names with \(lq./\(rq. This is useful for writing cross-platform \*(ST macros (see .BR "FILE NAMES AND DIRECTORIES" ). .LP +File name completions are case-sensitive or insensitive depending +on operating system defaults. +On some operating systems \*(ST can determine the case-sensitivity +of individual directories as well. +\# Should be supported on Mac OS and newer versions of Windows, +\# but it's still untested. +.LP Note that completions take place after string building and tilde-expansion is also performed by file name completions, so for instance both \(lq~/foo\(rq and \(lq^EQ[$HOME]/foo\(rq diff --git a/src/cmdline.c b/src/cmdline.c index d32df03..dde096d 100644 --- a/src/cmdline.c +++ b/src/cmdline.c @@ -614,7 +614,7 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t * */ if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -645,6 +645,16 @@ teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t * } gboolean +teco_state_stringbuilding_insert_completion(teco_machine_stringbuilding_t *ctx, const teco_string_t *str, GError **error) +{ + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(ctx, str->data, str->len, &str_escaped); + if (!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) + teco_string_append_c(&str_escaped, ' '); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + +gboolean teco_state_stringbuilding_escaped_process_edit_cmd(teco_machine_stringbuilding_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { @@ -684,6 +694,14 @@ teco_state_expectstring_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_ } gboolean +teco_state_expectstring_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + teco_state_t *stringbuilding_current = stringbuilding_ctx->parent.current; + return stringbuilding_current->insert_completion_cb(&stringbuilding_ctx->parent, str, error); +} + +gboolean teco_state_insert_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; @@ -778,7 +796,7 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -801,6 +819,20 @@ teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t } gboolean +teco_state_expectfile_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + if ((!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) && + ctx->expectstring.nesting == 1) + teco_string_append_wc(&str_escaped, + ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + +gboolean teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; @@ -820,7 +852,7 @@ teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -855,6 +887,21 @@ teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t } gboolean +teco_state_expectglob_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_autofree gchar *pattern_escaped = teco_globber_escape_pattern(str->data); + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, pattern_escaped, strlen(pattern_escaped), &str_escaped); + if ((!str->len || !G_IS_DIR_SEPARATOR(str->data[str->len-1])) && + ctx->expectstring.nesting == 1) + teco_string_append_wc(&str_escaped, + ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + +gboolean teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; @@ -874,7 +921,7 @@ teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t * if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -898,6 +945,19 @@ teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t * } gboolean +teco_state_expectdir_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + /* + * FIXME: We might terminate the command in case of leaf directories. + */ + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + +gboolean teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { g_assert(ctx->expectqreg != NULL); @@ -910,6 +970,18 @@ teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t } gboolean +teco_state_expectqreg_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + g_assert(ctx->expectqreg != NULL); + /* + * NOTE: teco_machine_qregspec_t is private, so we downcast to teco_machine_t. + * Otherwise, we'd have to move this callback into qreg.c. + */ + teco_state_t *expectqreg_current = ((teco_machine_t *)ctx->expectqreg)->current; + return expectqreg_current->insert_completion_cb((teco_machine_t *)ctx->expectqreg, str, error); +} + +gboolean teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { switch (key) { @@ -919,7 +991,7 @@ teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_ if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -945,6 +1017,12 @@ teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_ } gboolean +teco_state_qregspec_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, GError **error) +{ + return teco_cmdline_insert(str->data, str->len, error); +} + +gboolean teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = teco_machine_qregspec_get_stringbuilding(ctx); @@ -967,7 +1045,7 @@ teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_m if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -985,6 +1063,17 @@ teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_m } gboolean +teco_state_qregspec_string_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = teco_machine_qregspec_get_stringbuilding(ctx); + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + teco_string_append_c(&str_escaped, ']'); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + +gboolean teco_state_execute_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; @@ -1003,14 +1092,14 @@ teco_state_execute_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *pa break; /* - * In the EC command, <TAB> completes files just like ^T + * In the EC command, <TAB> completes files just like ^G<TAB>. * * TODO: Implement shell-command completion by iterating * executables in $PATH */ if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1049,7 +1138,7 @@ teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_mac if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1075,6 +1164,17 @@ teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_mac } gboolean +teco_state_scintilla_symbols_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + teco_string_append_c(&str_escaped, ','); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + +gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; @@ -1094,7 +1194,7 @@ teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1122,6 +1222,20 @@ teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren } gboolean +teco_state_goto_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + /* + * FIXME: This does not escape `,`. Cannot be escaped via ^Q currently? + */ + teco_string_append_c(&str_escaped, ','); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + +gboolean teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error) { teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; @@ -1141,7 +1255,7 @@ teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren if (teco_interface_popup_is_shown()) { /* cycle through popup pages */ - teco_interface_popup_show(); + teco_interface_popup_scroll(); return TRUE; } @@ -1163,6 +1277,19 @@ teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *paren return stringbuilding_current->process_edit_cmd_cb(&stringbuilding_ctx->parent, &ctx->parent, key, error); } +gboolean +teco_state_help_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error) +{ + teco_machine_stringbuilding_t *stringbuilding_ctx = &ctx->expectstring.machine; + + g_auto(teco_string_t) str_escaped; + teco_machine_stringbuilding_escape(stringbuilding_ctx, str->data, str->len, &str_escaped); + if (ctx->expectstring.nesting == 1) + teco_string_append_wc(&str_escaped, + ctx->expectstring.machine.escape_char == '{' ? '}' : ctx->expectstring.machine.escape_char); + return teco_cmdline_insert(str_escaped.data, str_escaped.len, error); +} + /* * Command states */ diff --git a/src/file-utils.c b/src/file-utils.c index fc5e410..02816b4 100644 --- a/src/file-utils.c +++ b/src/file-utils.c @@ -483,7 +483,7 @@ teco_file_auto_complete(const gchar *filename, GFileTest file_test, teco_string_ strlen((gchar *)file->data), is_buffer); } - teco_interface_popup_show(); + teco_interface_popup_show(filename ? strlen(filename) : 0); } /* @@ -48,6 +48,7 @@ GRegex *teco_globber_compile_pattern(const gchar *pattern); /* in cmdline.c */ gboolean teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_expectglob_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error); /** * @interface TECO_DEFINE_STATE_EXPECTGLOB @@ -58,6 +59,8 @@ gboolean teco_state_expectglob_process_edit_cmd(teco_machine_main_t *ctx, teco_m TECO_DEFINE_STATE_EXPECTFILE(NAME, \ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \ teco_state_expectglob_process_edit_cmd, \ + .insert_completion_cb = (teco_state_insert_completion_cb_t) \ + teco_state_expectglob_insert_completion, \ ##__VA_ARGS__ \ ) diff --git a/src/goto-commands.c b/src/goto-commands.c index b87fc32..99288c1 100644 --- a/src/goto-commands.c +++ b/src/goto-commands.c @@ -157,7 +157,10 @@ teco_state_goto_done(teco_machine_main_t *ctx, const teco_string_t *str, GError } /* in cmdline.c */ -gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar chr, GError **error); +gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, + gunichar chr, GError **error); +gboolean teco_state_goto_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, + GError **error); /*$ O * Olabel$ -- Go to label @@ -186,7 +189,8 @@ gboolean teco_state_goto_process_edit_cmd(teco_machine_main_t *ctx, teco_machine * terminate the command-line. */ TECO_DEFINE_STATE_EXPECTSTRING(teco_state_goto, - .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_goto_process_edit_cmd + .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_goto_process_edit_cmd, + .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_goto_insert_completion ); /* @@ -314,7 +314,10 @@ teco_state_help_done(teco_machine_main_t *ctx, const teco_string_t *str, GError } /* in cmdline.c */ -gboolean teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar chr, GError **error); +gboolean teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, + gunichar chr, GError **error); +gboolean teco_state_help_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, + GError **error); /*$ "?" help * ?[topic]$ -- Get help for topic @@ -384,5 +387,6 @@ gboolean teco_state_help_process_edit_cmd(teco_machine_main_t *ctx, teco_machine TECO_DEFINE_STATE_EXPECTSTRING(teco_state_help, .initial_cb = (teco_state_initial_cb_t)teco_state_help_initial, .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_help_process_edit_cmd, + .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_help_insert_completion, .expectstring.string_building = FALSE ); diff --git a/src/interface-curses/curses-info-popup.c b/src/interface-curses/curses-info-popup.c index 9525391..e470879 100644 --- a/src/interface-curses/curses-info-popup.c +++ b/src/interface-curses/curses-info-popup.c @@ -200,6 +200,61 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) wmove(ctx->window, bar_y, cols-1); wattron(ctx->window, A_REVERSE); wvline(ctx->window, ' ', bar_height); +} + +/** + * Find the entry at the given character coordinates. + * + * @param ctx The popup widget to look up + * @param y The pointer's Y position, relative to the popup's window + * @param x The pointer's X position, relative to the popup's window + * @return Pointer to the entry's string under the pointer or NULL. + * This string is owned by the popup and is only valid until the + * popup is cleared. + * + * @note This must match the calculations in teco_curses_info_popup_init_pad(). + */ +const teco_string_t * +teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x) +{ + int cols = getmaxx(stdscr); /**! screen width */ + gint pad_cols; /**! entry columns */ + gint pad_colwidth; /**! width per entry column */ + + /* + * With Unicode icons enabled, we reserve 2 characters at the beginning and one + * after the filename/directory. + * Otherwise 2 characters after the entry. + */ + gint reserve = teco_ed & TECO_ED_ICONS ? 2+1 : 2; + pad_colwidth = MIN(ctx->longest + reserve, cols - 2); + + /* pad_cols = floor((cols - 2) / pad_colwidth) */ + pad_cols = (cols - 2) / pad_colwidth; + + gint cur_col = 0; + for (teco_stailq_entry_t *cur = ctx->list.first; cur != NULL; cur = cur->next) { + teco_popup_entry_t *entry = (teco_popup_entry_t *)cur; + gint cur_line = cur_col/pad_cols + 1; + + if (cur_line > ctx->pad_first_line+y) + break; + if (cur_line == ctx->pad_first_line+y && + x > (cur_col % pad_cols)*pad_colwidth && x <= ((cur_col % pad_cols)+1)*pad_colwidth) + return &entry->name; + + cur_col++; + } + + return NULL; +} + +void +teco_curses_info_popup_scroll_page(teco_curses_info_popup_t *ctx) +{ + gint lines = getmaxy(stdscr); + gint pad_lines = getmaxy(ctx->pad); + gint popup_lines = MIN(pad_lines + 1, lines - 1); /* progress scroll position */ ctx->pad_first_line += popup_lines - 1; @@ -211,6 +266,19 @@ teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr) } void +teco_curses_info_popup_scroll(teco_curses_info_popup_t *ctx, gint delta) +{ + gint lines = getmaxy(stdscr); + gint pad_lines = getmaxy(ctx->pad); + gint popup_lines = MIN(pad_lines + 1, lines - 1); + + ctx->pad_first_line = MAX(ctx->pad_first_line+delta, 0); + if (pad_lines - ctx->pad_first_line < popup_lines - 1) + /* show last page */ + ctx->pad_first_line = pad_lines - (popup_lines - 1); +} + +void teco_curses_info_popup_clear(teco_curses_info_popup_t *ctx) { if (ctx->window) diff --git a/src/interface-curses/curses-info-popup.h b/src/interface-curses/curses-info-popup.h index 96dee5a..6f2ac9a 100644 --- a/src/interface-curses/curses-info-popup.h +++ b/src/interface-curses/curses-info-popup.h @@ -23,6 +23,7 @@ #include <curses.h> #include "list.h" +#include "string-utils.h" #include "interface.h" typedef struct { @@ -49,6 +50,10 @@ void teco_curses_info_popup_add(teco_curses_info_popup_t *ctx, teco_popup_entry_ const gchar *name, gsize name_len, gboolean highlight); void teco_curses_info_popup_show(teco_curses_info_popup_t *ctx, attr_t attr); +const teco_string_t *teco_curses_info_popup_getentry(teco_curses_info_popup_t *ctx, gint y, gint x); +void teco_curses_info_popup_scroll_page(teco_curses_info_popup_t *ctx); +void teco_curses_info_popup_scroll(teco_curses_info_popup_t *ctx, gint delta); + static inline bool teco_curses_info_popup_is_shown(teco_curses_info_popup_t *ctx) { @@ -58,8 +63,10 @@ teco_curses_info_popup_is_shown(teco_curses_info_popup_t *ctx) static inline void teco_curses_info_popup_noutrefresh(teco_curses_info_popup_t *ctx) { - if (ctx->window) - wnoutrefresh(ctx->window); + if (!ctx->window) + return; + redrawwin(ctx->window); + wnoutrefresh(ctx->window); } void teco_curses_info_popup_clear(teco_curses_info_popup_t *ctx); diff --git a/src/interface-curses/interface.c b/src/interface-curses/interface.c index 9480822..3be1001 100644 --- a/src/interface-curses/interface.c +++ b/src/interface-curses/interface.c @@ -356,6 +356,7 @@ static struct { GQueue *input_queue; teco_curses_info_popup_t popup; + gsize popup_prefix_len; /** * GError "thrown" by teco_interface_event_loop_iter(). @@ -1480,7 +1481,7 @@ teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize } void -teco_interface_popup_show(void) +teco_interface_popup_show(gsize prefix_len) { if (!teco_interface.cmdline_window) /* batch mode */ @@ -1489,9 +1490,21 @@ teco_interface_popup_show(void) short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0)); short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0)); + teco_interface.popup_prefix_len = prefix_len; teco_curses_info_popup_show(&teco_interface.popup, SCI_COLOR_ATTR(fg, bg)); } +void +teco_interface_popup_scroll(void) +{ + if (!teco_interface.cmdline_window) + /* batch mode */ + return; + + teco_curses_info_popup_scroll_page(&teco_interface.popup); + teco_interface_popup_show(teco_interface.popup_prefix_len); +} + gboolean teco_interface_popup_is_shown(void) { @@ -1583,6 +1596,11 @@ teco_interface_refresh(void) teco_view_noutrefresh(teco_interface_current_view); wnoutrefresh(teco_interface.msg_window); wnoutrefresh(teco_interface.cmdline_window); + /* + * FIXME: Why do we have to redrawwin() the popup window + * to keep it on the screen? + * Perhaps something is causing a redraw of the entire Scinterm view. + */ teco_curses_info_popup_noutrefresh(&teco_interface.popup); doupdate(); } @@ -1596,20 +1614,59 @@ teco_interface_refresh(void) (BUTTON1_##X | BUTTON2_##X | BUTTON3_##X | BUTTON4_##X | BUTTON5_##X) static gboolean -teco_interface_getmouse(void) +teco_interface_getmouse(GError **error) { MEVENT event; - WINDOW *current = teco_view_get_window(teco_interface_current_view); + + if (getmouse(&event) != OK) + return TRUE; + + if (teco_curses_info_popup_is_shown(&teco_interface.popup) && + wmouse_trafo(teco_interface.popup.window, &event.y, &event.x, FALSE)) { + /* + * NOTE: Not all curses variants report the RELEASED event, + * but may also return REPORT_MOUSE_POSITION. + * So we might react to all button presses as well. + */ + if (event.bstate & (BUTTON1_RELEASED | REPORT_MOUSE_POSITION)) { + teco_machine_t *machine = &teco_cmdline.machine.parent; + const teco_string_t *insert = teco_curses_info_popup_getentry(&teco_interface.popup, event.y, event.x); + + if (insert && machine->current->insert_completion_cb) { + /* successfully clicked popup item */ + const teco_string_t insert_suffix = {insert->data + teco_interface.popup_prefix_len, + insert->len - teco_interface.popup_prefix_len}; + if (!machine->current->insert_completion_cb(machine, &insert_suffix, error)) + return FALSE; + + teco_interface_popup_clear(); + teco_interface_msg_clear(); + teco_interface_cmdline_update(&teco_cmdline); + } + + return TRUE; + } + if (event.bstate & BUTTON_NUM(4)) + teco_curses_info_popup_scroll(&teco_interface.popup, -1); + else if (event.bstate & BUTTON_NUM(5)) + teco_curses_info_popup_scroll(&teco_interface.popup, +1); + + short fg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETFORE, STYLE_CALLTIP, 0)); + short bg = teco_rgb2curses(teco_interface_ssm(SCI_STYLEGETBACK, STYLE_CALLTIP, 0)); + teco_curses_info_popup_show(&teco_interface.popup, SCI_COLOR_ATTR(fg, bg)); + + return TRUE; + } /* * Return mouse coordinates relative to the view. * They will be in characters, but that's what SCI_POSITIONFROMPOINT * expects on Scinterm anyway. */ - if (getmouse(&event) != OK || - !wmouse_trafo(current, &event.y, &event.x, FALSE)) + WINDOW *current = teco_view_get_window(teco_interface_current_view); + if (!wmouse_trafo(current, &event.y, &event.x, FALSE)) /* no event inside of current view */ - return FALSE; + return TRUE; /* * NOTE: There will only be one of the button bits @@ -1654,7 +1711,7 @@ teco_interface_getmouse(void) if (event.bstate & BUTTON_ALT) teco_mouse.mods |= TECO_MOUSE_ALT; - return TRUE; + return teco_cmdline_keymacro("MOUSE", -1, error); } #endif /* NCURSES_MOUSE_VERSION >= 2 */ @@ -1789,8 +1846,7 @@ teco_interface_event_loop_iter(void) #if NCURSES_MOUSE_VERSION >= 2 case KEY_MOUSE: /* ANY of the mouse events */ - if (teco_interface_getmouse() && - !teco_cmdline_keymacro("MOUSE", -1, error)) + if (!teco_interface_getmouse(error)) return; break; #endif diff --git a/src/interface-gtk/gtk-info-popup.c b/src/interface-gtk/gtk-info-popup.c index 29a1fa9..b9ca41c 100644 --- a/src/interface-gtk/gtk-info-popup.c +++ b/src/interface-gtk/gtk-info-popup.c @@ -53,6 +53,8 @@ struct _TecoGtkInfoPopup { gboolean frozen; }; +static guint teco_gtk_info_popup_clicked_signal; + static gboolean teco_gtk_info_popup_scroll_event(GtkWidget *widget, GdkEventScroll *event); static void teco_gtk_info_popup_show(GtkWidget *widget); static void teco_gtk_info_popup_vadjustment_changed(GtkAdjustment *vadjustment, GtkWidget *scrollbar); @@ -82,6 +84,31 @@ teco_gtk_info_popup_class_init(TecoGtkInfoPopupClass *klass) GTK_WIDGET_CLASS(klass)->scroll_event = teco_gtk_info_popup_scroll_event; GTK_WIDGET_CLASS(klass)->show = teco_gtk_info_popup_show; G_OBJECT_CLASS(klass)->finalize = teco_gtk_info_popup_finalize; + + teco_gtk_info_popup_clicked_signal = + g_signal_new("clicked", G_TYPE_FROM_CLASS(klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_ULONG); +} + +static void +teco_gtk_info_popup_activated_cb(GtkFlowBox *box, GtkFlowBoxChild *child, gpointer user_data) +{ + TecoGtkInfoPopup *popup = TECO_GTK_INFO_POPUP(user_data); + + /* + * Find the TecoGtkLabel in the flow box child. + */ + GtkWidget *hbox = gtk_bin_get_child(GTK_BIN(child)); + g_autoptr(GList) child_list = gtk_container_get_children(GTK_CONTAINER(hbox)); + GList *entry; + for (entry = child_list; entry != NULL && !TECO_IS_GTK_LABEL(entry->data); entry = g_list_next(entry)); + g_assert(entry != NULL); + const teco_string_t *str = teco_gtk_label_get_text(TECO_GTK_LABEL(entry->data)); + + g_signal_emit(popup, teco_gtk_info_popup_clicked_signal, 0, + str->data, (gulong)str->len); } static void @@ -106,6 +133,8 @@ teco_gtk_info_popup_init(TecoGtkInfoPopup *self) G_CALLBACK(teco_gtk_info_popup_vadjustment_changed), scrollbar); self->flow_box = gtk_flow_box_new(); + g_signal_connect(self->flow_box, "child-activated", + G_CALLBACK(teco_gtk_info_popup_activated_cb), self); /* take as little height as necessary */ gtk_orientable_set_orientation(GTK_ORIENTABLE(self->flow_box), GTK_ORIENTATION_HORIZONTAL); @@ -311,12 +340,6 @@ teco_gtk_info_popup_idle_add(TecoGtkInfoPopup *self, teco_popup_entry_type_t typ gtk_widget_set_halign(label, GTK_ALIGN_START); gtk_widget_set_valign(label, GTK_ALIGN_CENTER); - /* - * FIXME: This makes little sense once we've got mouse support. - * But for the time being, it's a useful setting. - */ - gtk_label_set_selectable(GTK_LABEL(label), TRUE); - switch (type) { case TECO_POPUP_PLAIN: gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_START); @@ -417,12 +440,10 @@ teco_gtk_info_popup_scroll_page(TecoGtkInfoPopup *self) * Adjust this so only complete entries are shown. * Effectively, this rounds down to the line height. */ - GList *child_list = gtk_container_get_children(GTK_CONTAINER(self->flow_box)); - if (child_list) { + g_autoptr(GList) child_list = gtk_container_get_children(GTK_CONTAINER(self->flow_box)); + if (child_list) new_value -= (gint)new_value % gtk_widget_get_allocated_height(GTK_WIDGET(child_list->data)); - g_list_free(child_list); - } /* clip to the maximum possible value */ new_value = MIN(new_value, gtk_adjustment_get_upper(adj)); diff --git a/src/interface-gtk/gtk-label.c b/src/interface-gtk/gtk-label.c index ebbb40e..ef370a2 100644 --- a/src/interface-gtk/gtk-label.c +++ b/src/interface-gtk/gtk-label.c @@ -269,3 +269,9 @@ teco_gtk_label_set_text(TecoGtkLabel *self, const gchar *str, gssize len) gtk_label_set_text(GTK_LABEL(self), plaintext); } + +const teco_string_t * +teco_gtk_label_get_text(TecoGtkLabel *self) +{ + return &self->string; +} diff --git a/src/interface-gtk/gtk-label.h b/src/interface-gtk/gtk-label.h index e852104..c52d073 100644 --- a/src/interface-gtk/gtk-label.h +++ b/src/interface-gtk/gtk-label.h @@ -27,6 +27,7 @@ G_DECLARE_FINAL_TYPE(TecoGtkLabel, teco_gtk_label, TECO, GTK_LABEL, GtkLabel) GtkWidget *teco_gtk_label_new(const gchar *str, gssize len); void teco_gtk_label_set_text(TecoGtkLabel *self, const gchar *str, gssize len); +const teco_string_t *teco_gtk_label_get_text(TecoGtkLabel *self); void teco_gtk_label_parse_string(const gchar *str, gssize len, PangoColor *fg, guint16 fg_alpha, diff --git a/src/interface-gtk/interface.c b/src/interface-gtk/interface.c index ed9271b..9c740a6 100644 --- a/src/interface-gtk/interface.c +++ b/src/interface-gtk/interface.c @@ -70,6 +70,8 @@ static void teco_interface_size_allocate_cb(GtkWidget *widget, gpointer user_data); static gboolean teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data); +static void teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, + gpointer user_data); static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer user_data); static gboolean teco_interface_sigterm_handler(gpointer user_data) G_GNUC_UNUSED; @@ -194,6 +196,7 @@ static struct { GtkIMContext *input_method; GtkWidget *popup_widget; + gsize popup_prefix_len; GtkWidget *current_view_widget; @@ -381,6 +384,8 @@ teco_interface_init(void) */ teco_interface.popup_widget = teco_gtk_info_popup_new(); gtk_widget_set_name(teco_interface.popup_widget, "sciteco-info-popup"); + g_signal_connect(teco_interface.popup_widget, "clicked", + G_CALLBACK(teco_interface_popup_clicked_cb), NULL); gtk_overlay_add_overlay(GTK_OVERLAY(overlay_widget), teco_interface.popup_widget); g_signal_connect(overlay_widget, "get-child-position", G_CALLBACK(teco_gtk_info_popup_get_position_in_overlay), NULL); @@ -754,12 +759,16 @@ teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize } void -teco_interface_popup_show(void) +teco_interface_popup_show(gsize prefix_len) +{ + teco_interface.popup_prefix_len = prefix_len; + gtk_widget_show(teco_interface.popup_widget); +} + +void +teco_interface_popup_scroll(void) { - if (gtk_widget_get_visible(teco_interface.popup_widget)) - teco_gtk_info_popup_scroll_page(TECO_GTK_INFO_POPUP(teco_interface.popup_widget)); - else - gtk_widget_show(teco_interface.popup_widget); + teco_gtk_info_popup_scroll_page(TECO_GTK_INFO_POPUP(teco_interface.popup_widget)); } gboolean @@ -1349,7 +1358,6 @@ static gboolean teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) { static gboolean recursed = FALSE; - g_autoptr(GError) error = NULL; #ifdef DEBUG if (event->type == GDK_KEY_PRESS) @@ -1393,6 +1401,8 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) GdkWindow *top_window = gdk_window_get_toplevel(gtk_widget_get_window(teco_interface.window)); do { + g_autoptr(GError) error = NULL; + /* * The event queue might be filled when pressing keys when SciTECO * is busy executing code. @@ -1461,6 +1471,28 @@ teco_interface_input_cb(GtkWidget *widget, GdkEvent *event, gpointer user_data) return TRUE; } +static void +teco_interface_popup_clicked_cb(GtkWidget *popup, gchar *str, gulong len, gpointer user_data) +{ + g_assert(len >= teco_interface.popup_prefix_len); + const teco_string_t insert = {str+teco_interface.popup_prefix_len, len-teco_interface.popup_prefix_len}; + teco_machine_t *machine = &teco_cmdline.machine.parent; + + const teco_view_t *last_view = teco_interface_current_view; + + /* + * NOTE: It shouldn't really be necessary to catch TECO_ERROR_QUIT here. + * A auto completion should never result in program termination. + */ + if (machine->current->insert_completion_cb && + !machine->current->insert_completion_cb(machine, &insert, NULL)) + return; + teco_interface_popup_clear(); + teco_interface_cmdline_update(&teco_cmdline); + + teco_interface_refresh(teco_interface_current_view != last_view); +} + static gboolean teco_interface_window_delete_cb(GtkWidget *widget, GdkEventAny *event, gpointer user_data) { diff --git a/src/interface.h b/src/interface.h index 967f14d..33b094b 100644 --- a/src/interface.h +++ b/src/interface.h @@ -132,7 +132,9 @@ typedef enum { void teco_interface_popup_add(teco_popup_entry_type_t type, const gchar *name, gsize name_len, gboolean highlight); /** @pure */ -void teco_interface_popup_show(void); +void teco_interface_popup_show(gsize prefix_len); +/** @pure */ +void teco_interface_popup_scroll(void); /** @pure */ gboolean teco_interface_popup_is_shown(void); /** @pure */ diff --git a/src/parser.c b/src/parser.c index c17186e..295c635 100644 --- a/src/parser.c +++ b/src/parser.c @@ -447,11 +447,14 @@ teco_state_stringbuilding_start_input(teco_machine_stringbuilding_t *ctx, gunich /* in cmdline.c */ gboolean teco_state_stringbuilding_start_process_edit_cmd(teco_machine_stringbuilding_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_stringbuilding_insert_completion(teco_machine_stringbuilding_t *ctx, const teco_string_t *str, GError **error); TECO_DEFINE_STATE(teco_state_stringbuilding_start, .is_start = TRUE, .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) - teco_state_stringbuilding_start_process_edit_cmd + teco_state_stringbuilding_start_process_edit_cmd, + .insert_completion_cb = (teco_state_insert_completion_cb_t) + teco_state_stringbuilding_insert_completion ); static teco_state_t * diff --git a/src/parser.h b/src/parser.h index f8bae90..fe8e764 100644 --- a/src/parser.h +++ b/src/parser.h @@ -110,6 +110,7 @@ typedef gboolean (*teco_state_refresh_cb_t)(teco_machine_t *ctx, GError **error) typedef gboolean (*teco_state_end_of_macro_cb_t)(teco_machine_t *ctx, GError **error); typedef gboolean (*teco_state_process_edit_cmd_cb_t)(teco_machine_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +typedef gboolean (*teco_state_insert_completion_cb_t)(teco_machine_t *ctx, const teco_string_t *str, GError **error); typedef enum { TECO_KEYMACRO_MASK_START = (1 << 0), @@ -187,6 +188,19 @@ struct teco_state_t { teco_state_process_edit_cmd_cb_t process_edit_cmd_cb; /** + * Insert completion after clicking an entry in the popup + * window. + * + * All implementations of this method are currently + * defined in cmdline.c. + * + * It can be NULL if not required. + * + * @fixme Perhaps move all implementations to interface.c. + */ + teco_state_insert_completion_cb_t insert_completion_cb; + + /** * Whether this state is a start state (i.e. not within any * escape sequence etc.). * This is separate of TECO_KEYMACRO_MASK_START which is set @@ -241,11 +255,12 @@ gboolean teco_state_process_edit_cmd(teco_machine_t *ctx, teco_machine_t *parent #define TECO_DEFINE_STATE(NAME, ...) \ /** @ingroup states */ \ teco_state_t NAME = { \ - .initial_cb = NULL, /* do nothing */ \ + .initial_cb = NULL, /* do nothing */ \ .input_cb = (teco_state_input_cb_t)NAME##_input, /* always required */ \ - .refresh_cb = NULL, /* do nothing */ \ + .refresh_cb = NULL, /* do nothing */ \ .end_of_macro_cb = teco_state_end_of_macro, \ .process_edit_cmd_cb = teco_state_process_edit_cmd, \ + .insert_completion_cb = NULL, /* do nothing */ \ .is_start = FALSE, \ .keymacro_mask = TECO_KEYMACRO_MASK_DEFAULT, \ .style = SCE_SCITECO_DEFAULT, \ @@ -552,7 +567,10 @@ teco_state_t *teco_state_expectstring_input(teco_machine_main_t *ctx, gunichar c gboolean teco_state_expectstring_refresh(teco_machine_main_t *ctx, GError **error); /* in cmdline.c */ -gboolean teco_state_expectstring_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_expectstring_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, + gunichar key, GError **error); +gboolean teco_state_expectstring_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, + GError **error); /** * @interface TECO_DEFINE_STATE_EXPECTSTRING @@ -577,6 +595,8 @@ gboolean teco_state_expectstring_process_edit_cmd(teco_machine_main_t *ctx, teco .refresh_cb = (teco_state_refresh_cb_t)teco_state_expectstring_refresh, \ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \ teco_state_expectstring_process_edit_cmd, \ + .insert_completion_cb = (teco_state_insert_completion_cb_t) \ + teco_state_expectstring_insert_completion, \ .keymacro_mask = TECO_KEYMACRO_MASK_STRING, \ .style = SCE_SCITECO_STRING, \ .expectstring.string_building = TRUE, \ @@ -591,6 +611,7 @@ gboolean teco_state_expectfile_process(teco_machine_main_t *ctx, const teco_stri /* in cmdline.c */ gboolean teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_expectfile_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error); /** * @interface TECO_DEFINE_STATE_EXPECTFILE @@ -601,12 +622,15 @@ gboolean teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_m TECO_DEFINE_STATE_EXPECTSTRING(NAME, \ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \ teco_state_expectfile_process_edit_cmd, \ + .insert_completion_cb = (teco_state_insert_completion_cb_t) \ + teco_state_expectfile_insert_completion, \ .expectstring.process_cb = teco_state_expectfile_process, \ ##__VA_ARGS__ \ ) /* in cmdline.c */ gboolean teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_expectdir_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, GError **error); /** * @interface TECO_DEFINE_STATE_EXPECTDIR @@ -617,5 +641,7 @@ gboolean teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_ma TECO_DEFINE_STATE_EXPECTFILE(NAME, \ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \ teco_state_expectdir_process_edit_cmd, \ + .insert_completion_cb = (teco_state_insert_completion_cb_t) \ + teco_state_expectdir_insert_completion, \ ##__VA_ARGS__ \ ) diff --git a/src/qreg-commands.h b/src/qreg-commands.h index be31f23..6dbd1c4 100644 --- a/src/qreg-commands.h +++ b/src/qreg-commands.h @@ -37,7 +37,10 @@ gboolean teco_state_expectqreg_initial(teco_machine_main_t *ctx, GError **error) teco_state_t *teco_state_expectqreg_input(teco_machine_main_t *ctx, gunichar chr, GError **error); /* in cmdline.c */ -gboolean teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, + gunichar key, GError **error); +gboolean teco_state_expectqreg_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, + GError **error); /** * @interface TECO_DEFINE_STATE_EXPECTQREG @@ -56,6 +59,8 @@ gboolean teco_state_expectqreg_process_edit_cmd(teco_machine_main_t *ctx, teco_m .initial_cb = (teco_state_initial_cb_t)teco_state_expectqreg_initial, \ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \ teco_state_expectqreg_process_edit_cmd, \ + .insert_completion_cb = (teco_state_insert_completion_cb_t) \ + teco_state_expectqreg_insert_completion, \ .style = SCE_SCITECO_QREG, \ .expectqreg.type = TECO_QREG_REQUIRED, \ .expectqreg.got_register_cb = NAME##_got_register, /* always required */ \ @@ -1445,11 +1445,15 @@ teco_state_qregspec_start_input(teco_machine_qregspec_t *ctx, gunichar chr, GErr } /* in cmdline.c */ -gboolean teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_qregspec_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_t *parent_ctx, + gunichar key, GError **error); +gboolean teco_state_qregspec_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, + GError **error); TECO_DEFINE_STATE(teco_state_qregspec_start, .is_start = TRUE, - .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_qregspec_process_edit_cmd + .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_qregspec_process_edit_cmd, + .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_qregspec_insert_completion ); static teco_state_t * @@ -1585,9 +1589,12 @@ teco_state_qregspec_string_input(teco_machine_qregspec_t *ctx, gunichar chr, GEr /* in cmdline.c */ gboolean teco_state_qregspec_string_process_edit_cmd(teco_machine_qregspec_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_qregspec_string_insert_completion(teco_machine_qregspec_t *ctx, const teco_string_t *str, + GError **error); TECO_DEFINE_STATE(teco_state_qregspec_string, - .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_qregspec_string_process_edit_cmd + .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_qregspec_string_process_edit_cmd, + .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_qregspec_string_insert_completion ); /** @static @memberof teco_machine_qregspec_t */ diff --git a/src/rb3str.c b/src/rb3str.c index e43b6ba..276b624 100644 --- a/src/rb3str.c +++ b/src/rb3str.c @@ -143,7 +143,7 @@ teco_rb3str_auto_complete(teco_rb3str_tree_t *tree, gboolean case_sensitive, cur->key.data, cur->key.len, FALSE); } - teco_interface_popup_show(); + teco_interface_popup_show(str_len); } return prefixed_entries == 1; @@ -413,7 +413,7 @@ teco_state_edit_file_initial(teco_machine_main_t *ctx, GError **error) strlen(filename), cur == teco_ring_current); } - teco_interface_popup_show(); + teco_interface_popup_show(0); } else if (id > 0) { allow_filename = FALSE; if (!teco_current_doc_undo_edit(error) || diff --git a/src/symbols.c b/src/symbols.c index 17e0b29..7dcc601 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -155,7 +155,7 @@ teco_symbol_list_auto_complete(teco_symbol_list_t *ctx, const gchar *symbol, tec strlen(entry->data), FALSE); } - teco_interface_popup_show(); + teco_interface_popup_show(symbol_len); } return glist_len == 1; @@ -252,7 +252,10 @@ teco_state_scintilla_symbols_done(teco_machine_main_t *ctx, const teco_string_t } /* in cmdline.c */ -gboolean teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gunichar key, GError **error); +gboolean teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, + gunichar key, GError **error); +gboolean teco_state_scintilla_symbols_insert_completion(teco_machine_main_t *ctx, const teco_string_t *str, + GError **error); /*$ ES scintilla message * -- Send Scintilla message @@ -332,6 +335,7 @@ gboolean teco_state_scintilla_symbols_process_edit_cmd(teco_machine_main_t *ctx, */ TECO_DEFINE_STATE_EXPECTSTRING(teco_state_scintilla_symbols, .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t)teco_state_scintilla_symbols_process_edit_cmd, + .insert_completion_cb = (teco_state_insert_completion_cb_t)teco_state_scintilla_symbols_insert_completion, .expectstring.last = FALSE ); |