aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2014-12-09 02:46:34 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2014-12-09 02:46:34 +0100
commit613e8c91f082ca3cb64aa10063d3254100deac84 (patch)
treeda8d470f9e77d33a3b80de620b35d568d5239f36 /src
parent52ecff415770da4aeb4041d1308d13933dfc8801 (diff)
downloadsciteco-613e8c91f082ca3cb64aa10063d3254100deac84.tar.gz
Curses: support cycling through long lists of possible auto-completions and optimized screen refreshing/redrawing
* pressing e.g. TAB when the popup is showing a list of auto-completions will show the next page, eventually beginning at the first one again. * do not redraw curses windows in the UI methods directly. this resulted in flickering during command-line editing macros and ordinary macro calls because the physical screen was updated immediately. Instead, window refreshing and updated is done centrally in event_loop_iter() only after a key has been processed. Also we use wnoutrefresh() and doupdate() to send as little to the terminal (emulator) as possible.
Diffstat (limited to 'src')
-rw-r--r--src/cmdline.cpp80
-rw-r--r--src/interface-curses.cpp109
-rw-r--r--src/interface-curses.h19
-rw-r--r--src/interface-gtk.h7
-rw-r--r--src/interface.h5
5 files changed, 153 insertions, 67 deletions
diff --git a/src/cmdline.cpp b/src/cmdline.cpp
index 0907106..b0f5155 100644
--- a/src/cmdline.cpp
+++ b/src/cmdline.cpp
@@ -74,13 +74,13 @@ cmdline_keypress(gchar key)
gchar *echo;
/*
- * Cleanup messages, popups, etc...
+ * Cleanup messages,etc...
*/
- interface.popup_clear();
interface.msg_clear();
/*
- * Process immediate editing commands
+ * Process immediate editing commands.
+ * It may clear/hide the popup.
*/
insert = process_edit_cmd(key);
@@ -149,6 +149,7 @@ process_edit_cmd(gchar key)
{
static gchar insert[255];
gint cmdline_len = cmdline ? strlen(cmdline) : 0;
+ bool clear_popup = true;
insert[0] = key;
insert[1] = '\0';
@@ -197,10 +198,19 @@ process_edit_cmd(gchar key)
case CTL_KEY('T'):
if (States::is_string()) {
+ *insert = '\0';
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ clear_popup = false;
+ break;
+ }
+
const gchar *filename = last_occurrence(strings[0]);
gchar *new_chars = filename_complete(filename);
- *insert = '\0';
+ clear_popup = !interface.popup_is_shown();
+
if (new_chars)
g_stpcpy(insert, new_chars);
g_free(new_chars);
@@ -209,32 +219,61 @@ process_edit_cmd(gchar key)
case '\t':
if (States::is_file()) {
+ *insert = '\0';
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ clear_popup = false;
+ break;
+ }
+
gchar complete = escape_char == '{' ? ' ' : escape_char;
gchar *new_chars = filename_complete(strings[0], complete);
- *insert = '\0';
+ clear_popup = !interface.popup_is_shown();
+
if (new_chars)
g_stpcpy(insert, new_chars);
g_free(new_chars);
} else if (States::current == &States::executecommand) {
/*
* In the EC command, <TAB> completes files just like ^T
+ * TODO: Implement shell-command completion by iterating
+ * executables in $PATH
*/
+ *insert = '\0';
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ clear_popup = false;
+ break;
+ }
+
const gchar *filename = last_occurrence(strings[0]);
gchar *new_chars = filename_complete(filename);
- *insert = '\0';
+ clear_popup = !interface.popup_is_shown();
+
if (new_chars)
g_stpcpy(insert, new_chars);
g_free(new_chars);
} else if (States::current == &States::scintilla_symbols) {
+ *insert = '\0';
+ if (interface.popup_is_shown()) {
+ /* cycle through popup pages */
+ interface.popup_show();
+ clear_popup = false;
+ break;
+ }
+
const gchar *symbol = last_occurrence(strings[0], ",");
SymbolList &list = symbol == strings[0]
? Symbols::scintilla
: Symbols::scilexer;
gchar *new_chars = symbol_complete(list, symbol, ',');
- *insert = '\0';
+ clear_popup = !interface.popup_is_shown();
+
if (new_chars)
g_stpcpy(insert, new_chars);
g_free(new_chars);
@@ -286,6 +325,9 @@ process_edit_cmd(gchar key)
#endif
}
+ if (clear_popup)
+ interface.popup_clear();
+
return insert;
}
@@ -364,7 +406,7 @@ filename_complete(const gchar *filename, gchar completed)
gchar *dirname;
const gchar *basename, *cur_basename;
GDir *dir;
- GList *files = NULL;
+ GSList *files = NULL;
guint files_len = 0;
gchar *insert = NULL;
gsize filename_len;
@@ -404,10 +446,10 @@ filename_complete(const gchar *filename, gchar completed)
if (g_file_test(cur_filename, G_FILE_TEST_IS_DIR))
String::append(cur_filename, G_DIR_SEPARATOR_S);
- files = g_list_prepend(files, cur_filename);
+ files = g_slist_prepend(files, cur_filename);
- if (g_list_next(files)) {
- const gchar *other_file = (gchar *)g_list_next(files)->data;
+ if (g_slist_next(files)) {
+ const gchar *other_file = (gchar *)g_slist_next(files)->data;
gsize len = String::diff(other_file + filename_len,
cur_filename + filename_len);
if (len < prefix_len)
@@ -425,24 +467,22 @@ filename_complete(const gchar *filename, gchar completed)
g_dir_close(dir);
if (!insert && files_len > 1) {
- files = g_list_sort(files, (GCompareFunc)g_strcmp0);
+ files = g_slist_sort(files, (GCompareFunc)g_strcmp0);
- for (GList *file = g_list_first(files);
- file != NULL;
- file = g_list_next(file)) {
+ for (GSList *file = files; file; file = g_slist_next(file)) {
InterfaceCurrent::PopupEntryType type;
- bool in_buffer = false;
+ bool is_buffer = false;
if (filename_is_dir((gchar *)file->data)) {
type = InterfaceCurrent::POPUP_DIRECTORY;
} else {
type = InterfaceCurrent::POPUP_FILE;
/* FIXME: inefficient */
- in_buffer = ring.find((gchar *)file->data);
+ is_buffer = ring.find((gchar *)file->data);
}
interface.popup_add(type, (gchar *)file->data,
- in_buffer);
+ is_buffer);
}
interface.popup_show();
@@ -450,9 +490,7 @@ filename_complete(const gchar *filename, gchar completed)
String::append(insert, completed);
}
- for (GList *file = g_list_first(files); file; file = g_list_next(file))
- g_free(file->data);
- g_list_free(files);
+ g_slist_free_full(files, g_free);
return insert;
}
diff --git a/src/interface-curses.cpp b/src/interface-curses.cpp
index 6732494..1cbcef3 100644
--- a/src/interface-curses.cpp
+++ b/src/interface-curses.cpp
@@ -97,11 +97,6 @@ InterfaceCurses::main_impl(int &argc, char **&argv)
cmdline_window = newwin(0, 0, LINES - 1, 0);
cmdline_current = NULL;
- draw_info();
- /* scintilla will be refreshed in event loop */
- msg_clear();
- cmdline_update("");
-
#ifdef EMSCRIPTEN
nodelay(cmdline_window, TRUE);
#else
@@ -163,8 +158,8 @@ InterfaceCurses::resize_all_windows(void)
mvwin(cmdline_window, lines - 1, 0);
draw_info();
- /* scintilla will be refreshed in event loop */
msg_clear(); /* FIXME: use saved message */
+ popup_clear();
cmdline_update();
}
@@ -193,8 +188,6 @@ InterfaceCurses::vmsg_impl(MessageType type, const gchar *fmt, va_list ap)
wbkgdset(msg_window, ' ' | type2attr[type]);
vw_printw(msg_window, fmt, ap);
wclrtoeol(msg_window);
-
- wrefresh(msg_window);
}
void
@@ -206,8 +199,6 @@ InterfaceCurses::msg_clear(void)
wmove(msg_window, 0, 0);
wbkgdset(msg_window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE));
wclrtoeol(msg_window);
-
- wrefresh(msg_window);
}
void
@@ -236,8 +227,6 @@ InterfaceCurses::draw_info(void)
wbkgdset(info_window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE));
waddstr(info_window, info_current);
wclrtoeol(info_window);
-
- wrefresh(info_window);
}
void
@@ -283,8 +272,6 @@ InterfaceCurses::cmdline_update_impl(const gchar *cmdline)
waddstr(cmdline_window, line);
waddch(cmdline_window, ' ' | A_REVERSE);
wclrtoeol(cmdline_window);
-
- wrefresh(cmdline_window);
}
void
@@ -310,21 +297,34 @@ InterfaceCurses::popup_show_impl(void)
int lines, cols; /* screen dimensions */
int popup_lines;
gint popup_cols;
- gint cur_entry;
+ gint popup_colwidth;
+ gint cur_col;
- if (isendwin()) /* batch mode */
- goto cleanup;
+ if (isendwin() || !popup.length)
+ /* batch mode or nothing to display */
+ return;
getmaxyx(stdscr, lines, cols);
- /* reserve 2 space characters between columns */
- popup.longest += 2;
- popup.list = g_slist_reverse(popup.list);
+ if (popup.window)
+ delwin(popup.window);
+ else
+ /* reverse list only once */
+ popup.list = g_slist_reverse(popup.list);
- /* popup_cols = floor(cols / popup.longest) */
- popup_cols = MAX(cols / popup.longest, 1);
- /* popup_lines = ceil(popup.length / popup_cols) */
- popup_lines = (popup.length+popup_cols-1) / popup_cols;
+ if (!popup.cur_list) {
+ /* start from beginning of list */
+ popup.cur_list = popup.list;
+ popup.cur_entry = 0;
+ }
+
+ /* reserve 2 spaces between columns */
+ popup_colwidth = popup.longest + 2;
+
+ /* popup_cols = floor(cols / popup_colwidth) */
+ popup_cols = MAX(cols / popup_colwidth, 1);
+ /* popup_lines = ceil((popup.length-popup.cur_entry) / popup_cols) */
+ popup_lines = (popup.length-popup.cur_entry+popup_cols-1) / popup_cols;
/*
* Popup window can cover all but one screen row.
* If it does not fit, the list of tokens is truncated
@@ -336,17 +336,22 @@ InterfaceCurses::popup_show_impl(void)
popup.window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
wbkgd(popup.window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_BLUE));
- cur_entry = 0;
- for (GSList *cur = popup.list; cur; cur = g_slist_next(cur)) {
- gchar *entry = (gchar *)cur->data;
- gint cur_line = cur_entry/popup_cols + 1;
+ /*
+ * cur_col is the row currently written.
+ * It does not wrap but grows indefinitely.
+ * Therefore the real current row is (cur_col % popup_cols)
+ */
+ cur_col = 0;
+ while (popup.cur_list) {
+ gchar *entry = (gchar *)popup.cur_list->data;
+ gint cur_line = cur_col/popup_cols + 1;
wmove(popup.window,
- cur_line-1, (cur_entry % popup_cols)*popup.longest);
- cur_entry++;
+ cur_line-1, (cur_col % popup_cols)*popup_colwidth);
+ cur_col++;
- if (cur_line == popup_lines && !(cur_entry % popup_cols) &&
- cur_entry < popup.length) {
+ if (cur_line == popup_lines && !(cur_col % popup_cols) &&
+ g_slist_next(popup.cur_list) != NULL) {
/* truncate entries in the popup's very last column */
(void)wattrset(popup.window, A_BOLD);
waddstr(popup.window, "...");
@@ -355,26 +360,34 @@ InterfaceCurses::popup_show_impl(void)
(void)wattrset(popup.window, *entry == '*' ? A_BOLD : A_NORMAL);
waddstr(popup.window, entry + 1);
+
+ popup.cur_list = g_slist_next(popup.cur_list);
+ popup.cur_entry++;
}
-cleanup:
- g_slist_free_full(popup.list, g_free);
- popup.list = NULL;
- popup.longest = popup.length = 0;
+ redrawwin(info_window);
+ /* scintilla window is redrawn by ViewCurses::refresh() */
+ redrawwin(msg_window);
}
void
InterfaceCurses::popup_clear_impl(void)
{
+ g_slist_free_full(popup.list, g_free);
+ popup.list = NULL;
+ popup.length = 0;
+ /* reserve at least 3 characters for "..." */
+ popup.longest = 3;
+
+ popup.cur_list = NULL;
+ popup.cur_entry = 0;
+
if (!popup.window)
return;
redrawwin(info_window);
- wrefresh(info_window);
- redrawwin(current_view->get_window());
- current_view->refresh();
+ /* scintilla window is redrawn by ViewCurses::refresh() */
redrawwin(msg_window);
- wrefresh(msg_window);
delwin(popup.window);
popup.window = NULL;
@@ -460,17 +473,29 @@ event_loop_iter()
sigint_occurred = FALSE;
+ wnoutrefresh(interface.info_window);
+ /* FIXME: this does wrefresh() internally */
interface.current_view->refresh();
+ wnoutrefresh(interface.msg_window);
+ wnoutrefresh(interface.cmdline_window);
if (interface.popup.window)
- wrefresh(interface.popup.window);
+ wnoutrefresh(interface.popup.window);
+ doupdate();
}
void
InterfaceCurses::event_loop_impl(void)
{
- /* initial refresh: window might have been changed in batch mode */
+ /* initial refresh */
+ /* FIXME: this does wrefresh() internally */
current_view->refresh();
draw_info();
+ wnoutrefresh(info_window);
+ msg_clear();
+ wnoutrefresh(msg_window);
+ cmdline_update("");
+ wnoutrefresh(cmdline_window);
+ doupdate();
#ifdef EMSCRIPTEN
PDC_emscripten_set_handler(event_loop_iter, TRUE);
diff --git a/src/interface-curses.h b/src/interface-curses.h
index 35f7edb..70e5300 100644
--- a/src/interface-curses.h
+++ b/src/interface-curses.h
@@ -80,11 +80,16 @@ typedef class InterfaceCurses : public Interface<InterfaceCurses, ViewCurses> {
struct Popup {
WINDOW *window;
- GSList *list;
- gint longest;
- gint length;
+ GSList *list; /**! list of popup entries */
+ gint longest; /**! size of longest entry */
+ gint length; /**! total number of popup entries */
- Popup() : window(NULL), list(NULL), longest(0), length(0) {}
+ GSList *cur_list; /**! next entry to display */
+ gint cur_entry; /**! next entry to display (position) */
+
+ Popup() : window(NULL), list(NULL),
+ longest(3), length(0),
+ cur_list(NULL), cur_entry(0) {}
~Popup();
} popup;
@@ -122,6 +127,12 @@ public:
const gchar *name, bool highlight = false);
/* implementation of Interface::popup_show() */
void popup_show_impl(void);
+ /* implementation of Interface::popup_is_shown() */
+ inline bool
+ popup_is_shown_impl(void)
+ {
+ return popup.window != NULL;
+ }
/* implementation of Interface::popup_clear() */
void popup_clear_impl(void);
diff --git a/src/interface-gtk.h b/src/interface-gtk.h
index 6c8e055..ac242f2 100644
--- a/src/interface-gtk.h
+++ b/src/interface-gtk.h
@@ -116,8 +116,15 @@ public:
inline void
popup_show_impl(void)
{
+ /* FIXME: scroll through popup contents */
gtk_widget_show(popup_widget);
}
+ /* implementation of Interface::popup_is_shown() */
+ inline bool
+ popup_is_shown_impl(void)
+ {
+ return gtk_widget_get_visible(popup_widget);
+ }
/* implementation of Interface::popup_clear() */
void popup_clear_impl(void);
diff --git a/src/interface.h b/src/interface.h
index 4b5de7f..dcf72ae 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -301,6 +301,11 @@ public:
{
impl().popup_show_impl();
}
+ inline bool
+ popup_is_shown(void)
+ {
+ return impl().popup_is_shown_impl();
+ }
inline void
popup_clear(void)
{