aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2025-02-15 01:32:05 +0300
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2025-02-23 04:52:39 +0300
commit428dafa568923d5632101c716fb20a3de35d27be (patch)
tree475b270fc384d5040e4711e155e47d580c52b1a3 /src
parent980dcdaa138a42830af4e2533b7e970d7f5fa3cf (diff)
downloadsciteco-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.
Diffstat (limited to 'src')
-rw-r--r--src/cmdline.c149
-rw-r--r--src/file-utils.c2
-rw-r--r--src/glob.h3
-rw-r--r--src/goto-commands.c8
-rw-r--r--src/help.c6
-rw-r--r--src/interface-curses/curses-info-popup.c68
-rw-r--r--src/interface-curses/curses-info-popup.h11
-rw-r--r--src/interface-curses/interface.c74
-rw-r--r--src/interface-gtk/gtk-info-popup.c41
-rw-r--r--src/interface-gtk/gtk-label.c6
-rw-r--r--src/interface-gtk/gtk-label.h1
-rw-r--r--src/interface-gtk/interface.c44
-rw-r--r--src/interface.h4
-rw-r--r--src/parser.c5
-rw-r--r--src/parser.h32
-rw-r--r--src/qreg-commands.h7
-rw-r--r--src/qreg.c13
-rw-r--r--src/rb3str.c2
-rw-r--r--src/ring.c2
-rw-r--r--src/symbols.c8
20 files changed, 431 insertions, 55 deletions
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);
}
/*
diff --git a/src/glob.h b/src/glob.h
index e3286dd..d11fbce 100644
--- a/src/glob.h
+++ b/src/glob.h
@@ -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
);
/*
diff --git a/src/help.c b/src/help.c
index 886354c..a093032 100644
--- a/src/help.c
+++ b/src/help.c
@@ -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 */ \
diff --git a/src/qreg.c b/src/qreg.c
index 68c8db8..4beea74 100644
--- a/src/qreg.c
+++ b/src/qreg.c
@@ -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;
diff --git a/src/ring.c b/src/ring.c
index 319d3c8..2c235af 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -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
);