/* * Copyright (C) 2012-2016 Robin Haberkorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "curses-utils.h" #include "curses-info-popup.h" namespace SciTECO { void CursesInfoPopup::add(PopupEntryType type, const gchar *name, bool highlight) { size_t name_len = strlen(name); Entry *entry = (Entry *)g_malloc(sizeof(Entry) + name_len + 1); entry->type = type; entry->highlight = highlight; strcpy(entry->name, name); longest = MAX(longest, (gint)name_len); 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 CursesInfoPopup::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)) { Entry *entry = (Entry *)cur->data; gint cur_line = cur_col/pad_cols + 1; wmove(pad, cur_line-1, (cur_col % pad_cols)*pad_colwidth); wattrset(pad, entry->highlight ? A_BOLD : A_NORMAL); switch (entry->type) { case POPUP_FILE: case POPUP_DIRECTORY: Curses::format_filename(pad, entry->name); break; default: Curses::format_str(pad, entry->name); break; } cur_col++; } } void CursesInfoPopup::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 CursesInfoPopup::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; } } CursesInfoPopup::~CursesInfoPopup() { if (window) delwin(window); if (pad) delwin(pad); if (list) g_slist_free_full(list, g_free); } } /* namespace SciTECO */