aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/interface-curses.cpp329
-rw-r--r--src/interface-curses.h59
2 files changed, 258 insertions, 130 deletions
diff --git a/src/interface-curses.cpp b/src/interface-curses.cpp
index c3a5b33..b4d83ec 100644
--- a/src/interface-curses.cpp
+++ b/src/interface-curses.cpp
@@ -256,6 +256,193 @@ InterfaceCurses::InterfaceCurses() : stdout_orig(-1), stderr_orig(-1),
}
void
+InterfaceCurses::Popup::add(PopupEntryType type,
+ const gchar *name, bool highlight)
+{
+ gchar *entry = g_strconcat(highlight ? "*" : " ", name, NIL);
+
+ longest = MAX(longest, (gint)strlen(name));
+ length++;
+
+ /*
+ * Entries are added in reverse (constant time for GSList),
+ * so they will later have to be reversed.
+ */
+ list = g_slist_prepend(list, entry);
+}
+
+void
+InterfaceCurses::Popup::init_pad(attr_t attr)
+{
+ int cols = getmaxx(stdscr); /* screen width */
+ int pad_lines; /* pad height */
+ gint pad_cols; /* entry columns */
+ gint pad_colwidth; /* width per entry column */
+
+ gint cur_col;
+
+ /* reserve 2 spaces between columns */
+ pad_colwidth = MIN(longest + 2, cols - 2);
+
+ /* pad_cols = floor((cols - 2) / pad_colwidth) */
+ pad_cols = (cols - 2) / pad_colwidth;
+ /* pad_lines = ceil(length / pad_cols) */
+ pad_lines = (length+pad_cols-1) / pad_cols;
+
+ /*
+ * Render the entire autocompletion list into a pad
+ * which can be higher than the physical screen.
+ * The pad uses two columns less than the screen since
+ * it will be drawn into the popup window which has left
+ * and right borders.
+ */
+ pad = newpad(pad_lines, cols - 2);
+
+ wbkgd(pad, ' ' | attr);
+
+ /*
+ * 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;
+ for (GSList *cur = list; cur != NULL; cur = g_slist_next(cur)) {
+ gchar *entry = (gchar *)cur->data;
+ gint cur_line = cur_col/pad_cols + 1;
+
+ wmove(pad, cur_line-1,
+ (cur_col % pad_cols)*pad_colwidth);
+
+ wattrset(pad, *entry == '*' ? A_BOLD : A_NORMAL);
+
+ if ((int)strlen(entry + 1) > cols - 2) {
+ /* truncate entry */
+ waddnstr(pad, entry + 1, cols - 2 - 3);
+ wattron(pad, A_BOLD);
+ /*
+ * A_UNDERLINE is not supported by PDCurses/win32
+ * and causes weird colors, so we better leave it away.
+ */
+#ifndef PDCURSES_WIN32
+ wattron(pad, A_UNDERLINE);
+#endif
+ waddstr(pad, "...");
+ } else {
+ waddstr(pad, entry + 1);
+ }
+
+ cur_col++;
+ }
+}
+
+void
+InterfaceCurses::Popup::show(attr_t attr)
+{
+ int lines, cols; /* screen dimensions */
+ gint pad_lines;
+ gint popup_lines;
+ gint bar_height, bar_y;
+
+ if (!length)
+ /* nothing to display */
+ return;
+
+ getmaxyx(stdscr, lines, cols);
+
+ if (window)
+ delwin(window);
+ else
+ /* reverse list only once */
+ list = g_slist_reverse(list);
+
+ if (!pad)
+ init_pad(attr);
+ pad_lines = getmaxy(pad);
+
+ /*
+ * Popup window can cover all but one screen row.
+ * Another row is reserved for the top border.
+ */
+ popup_lines = MIN(pad_lines + 1, lines - 1);
+
+ /* window covers message, scintilla and info windows */
+ window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
+
+ wbkgdset(window, ' ' | attr);
+
+ wborder(window,
+ ACS_VLINE,
+ ACS_VLINE, /* may be overwritten with scrollbar */
+ ACS_HLINE,
+ ' ', /* no bottom line */
+ ACS_ULCORNER, ACS_URCORNER,
+ ACS_VLINE, ACS_VLINE);
+
+ copywin(pad, window,
+ pad_first_line, 0,
+ 1, 1, popup_lines - 1, cols - 2, FALSE);
+
+ if (pad_lines <= popup_lines - 1)
+ /* no need for scrollbar */
+ return;
+
+ /* bar_height = ceil((popup_lines-1)/pad_lines * (popup_lines-2)) */
+ bar_height = ((popup_lines-1)*(popup_lines-2) + pad_lines-1) /
+ pad_lines;
+ /* bar_y = floor(pad_first_line/pad_lines * (popup_lines-2)) + 1 */
+ bar_y = pad_first_line*(popup_lines-2) / pad_lines + 1;
+
+ mvwvline(window, 1, cols-1, ACS_CKBOARD, popup_lines-2);
+ /*
+ * We do not use ACS_BLOCK here since it will not
+ * always be drawn as a solid block (e.g. xterm).
+ * Instead, simply draw reverse blanks.
+ */
+ wmove(window, bar_y, cols-1);
+ wattron(window, A_REVERSE);
+ wvline(window, ' ', bar_height);
+
+ /* progress scroll position */
+ pad_first_line += popup_lines - 1;
+ /* wrap on last shown page */
+ pad_first_line %= pad_lines;
+ if (pad_lines - pad_first_line < popup_lines - 1)
+ /* show last page */
+ pad_first_line = pad_lines - (popup_lines - 1);
+}
+
+void
+InterfaceCurses::Popup::clear(void)
+{
+ g_slist_free_full(list, g_free);
+ list = NULL;
+ length = 0;
+ longest = 0;
+
+ pad_first_line = 0;
+
+ if (window) {
+ delwin(window);
+ window = NULL;
+ }
+
+ if (pad) {
+ delwin(pad);
+ pad = NULL;
+ }
+}
+
+InterfaceCurses::Popup::~Popup()
+{
+ if (window)
+ delwin(window);
+ if (pad)
+ delwin(pad);
+ if (list)
+ g_slist_free_full(list, g_free);
+}
+
+void
InterfaceCurses::main_impl(int &argc, char **&argv)
{
/*
@@ -981,127 +1168,40 @@ InterfaceCurses::draw_cmdline(void)
}
void
-InterfaceCurses::popup_add_impl(PopupEntryType type,
- const gchar *name, bool highlight)
-{
- gchar *entry;
-
- if (!cmdline_window) /* batch mode */
- return;
-
- entry = g_strconcat(highlight ? "*" : " ", name, NIL);
-
- popup.longest = MAX(popup.longest, (gint)strlen(name));
- popup.length++;
-
- popup.list = g_slist_prepend(popup.list, entry);
-}
-
-void
InterfaceCurses::popup_show_impl(void)
{
- int lines, cols; /* screen dimensions */
- int popup_lines;
- gint popup_cols;
- gint popup_colwidth;
- gint cur_col;
-
short fg, bg;
- if (!cmdline_window || !popup.length)
- /* batch mode or nothing to display */
+ if (!cmdline_window)
+ /* batch mode */
return;
- getmaxyx(stdscr, lines, cols);
-
- if (popup.window)
- delwin(popup.window);
- else
- /* reverse list only once */
- popup.list = g_slist_reverse(popup.list);
-
- 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
- * and "..." is displayed.
- */
- popup_lines = MIN(popup_lines, lines - 1);
-
- /* window covers message, scintilla and info windows */
- popup.window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0);
-
fg = rgb2curses(ssm(SCI_STYLEGETFORE, STYLE_CALLTIP));
bg = rgb2curses(ssm(SCI_STYLEGETBACK, STYLE_CALLTIP));
- wbkgd(popup.window, ' ' | SCI_COLOR_ATTR(fg, bg));
-
- /*
- * 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_col % popup_cols)*popup_colwidth);
- cur_col++;
-
- 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, "...");
- break;
- }
-
- (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++;
- }
-
- redrawwin(info_window);
- /* scintilla window is redrawn by ViewCurses::refresh() */
- redrawwin(msg_window);
+ popup.show(SCI_COLOR_ATTR(fg, bg));
}
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);
- /* scintilla window is redrawn by ViewCurses::refresh() */
- redrawwin(msg_window);
+#ifdef __PDCURSES__
+ /*
+ * PDCurses will not redraw all windows that may be
+ * overlapped by the popup window correctly - at least
+ * not the info window.
+ * The Scintilla window is apparently always touched by
+ * scintilla_noutrefresh().
+ * Actually we would expect this to be necessary on any curses,
+ * but ncurses doesn't require this.
+ */
+ if (popup.is_shown()) {
+ touchwin(info_window);
+ touchwin(msg_window);
+ }
+#endif
- delwin(popup.window);
- popup.window = NULL;
+ popup.clear();
}
/**
@@ -1232,12 +1332,10 @@ event_loop_iter()
*/
interface.draw_info();
wnoutrefresh(interface.info_window);
- /* FIXME: this does wrefresh() internally */
- interface.current_view->refresh();
+ interface.current_view->noutrefresh();
wnoutrefresh(interface.msg_window);
wnoutrefresh(interface.cmdline_window);
- if (interface.popup.window)
- wnoutrefresh(interface.popup.window);
+ interface.popup.noutrefresh();
doupdate();
}
@@ -1252,10 +1350,9 @@ InterfaceCurses::event_loop_impl(void)
init_interactive();
/* initial refresh */
- /* FIXME: this does wrefresh() internally */
- current_view->refresh();
draw_info();
wnoutrefresh(info_window);
+ current_view->noutrefresh();
msg_clear();
wnoutrefresh(msg_window);
cmdline_update(&empty_cmdline);
@@ -1287,14 +1384,6 @@ InterfaceCurses::event_loop_impl(void)
#endif
}
-InterfaceCurses::Popup::~Popup()
-{
- if (window)
- delwin(window);
- if (list)
- g_slist_free_full(list, g_free);
-}
-
InterfaceCurses::~InterfaceCurses()
{
if (info_window)
diff --git a/src/interface-curses.h b/src/interface-curses.h
index b43069f..9bb17cd 100644
--- a/src/interface-curses.h
+++ b/src/interface-curses.h
@@ -52,6 +52,12 @@ public:
}
inline void
+ noutrefresh(void)
+ {
+ scintilla_noutrefresh(sci);
+ }
+
+ inline void
refresh(void)
{
scintilla_refresh(sci);
@@ -104,19 +110,44 @@ typedef class InterfaceCurses : public Interface<InterfaceCurses, ViewCurses> {
WINDOW *cmdline_window, *cmdline_pad;
gsize cmdline_len, cmdline_rubout_len;
- struct Popup {
- WINDOW *window;
+ class Popup {
+ WINDOW *window; /**! window showing part of pad */
+ WINDOW *pad; /**! full-height entry list */
+
GSList *list; /**! list of popup entries */
gint longest; /**! size of longest entry */
gint length; /**! total number of popup entries */
- GSList *cur_list; /**! next entry to display */
- gint cur_entry; /**! next entry to display (position) */
+ gint pad_first_line; /**! first line in pad to show */
+
+ public:
+ Popup() : window(NULL), pad(NULL),
+ list(NULL), longest(0), length(0),
+ pad_first_line(0) {}
+
+ void add(PopupEntryType type,
+ const gchar *name, bool highlight = false);
+
+ void show(attr_t attr);
+ inline bool
+ is_shown(void)
+ {
+ return window != NULL;
+ }
+
+ void clear(void);
+
+ inline void
+ noutrefresh(void)
+ {
+ if (window)
+ wnoutrefresh(window);
+ }
- Popup() : window(NULL), list(NULL),
- longest(3), length(0),
- cur_list(NULL), cur_entry(0) {}
~Popup();
+
+ private:
+ void init_pad(attr_t attr);
} popup;
public:
@@ -145,16 +176,24 @@ public:
void cmdline_update_impl(const Cmdline *cmdline);
/* implementation of Interface::popup_add() */
- void popup_add_impl(PopupEntryType type,
- const gchar *name, bool highlight = false);
+ inline void
+ popup_add_impl(PopupEntryType type,
+ const gchar *name, bool highlight = false)
+ {
+ if (cmdline_window)
+ /* interactive mode */
+ popup.add(type, name, highlight);
+ }
+
/* 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;
+ return popup.is_shown();
}
+
/* implementation of Interface::popup_clear() */
void popup_clear_impl(void);