/* elmo - ELectronic Mail Operator Copyright (C) 2003, 2004 rzyjontko 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; version 2. 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, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ---------------------------------------------------------------------- selection window */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #include #include #include "ecurses.h" #include "select.h" #include "xmalloc.h" #include "error.h" #include "line.h" #include "color.h" #include "ask.h" #include "status.h" #include "read.h" #include "cmd.h" #include "gettext.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define MIN(a,b) (((a)<(b))?(a):(b)) #define MAX(a,b) (((a)>(b))?(a):(b)) #define PREAMBLE do {if (select == NULL) return; } while (0) #define NONEMPTY do {if (select->items->count == 0) return; } while (0) /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ /* Color used in bar windows. */ static chtype text_color; static chtype hilight_color; static search_t *search = NULL; static select_t *search_select = NULL; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static void show_percentage (select_t *select, int count, int height) { int p; if (count <= height) p = -1; else if (select->top_pos == 0) p = 0; else if (count - select->top_pos <= height) p = 100; else p = 100 * select->top_pos / count; if (p == 0 && select->top_pos != 0) p = 1; status_show_percentage (p); } static void validate (select_t *select) { int count = select->count (select); int nothing; int height; int position; int y, x; getmaxyx (select->win, height, nothing); if (select->bar == NULL){ show_percentage (select, count, height); return; } if (select->top_pos >= count) select->top_pos = count - 1; if (select->top_pos < 0) select->top_pos = 0; if (select->bar_pos >= count) select->bar_pos = count - 1; if (select->bar_pos < 0) select->bar_pos = 0; if (select->top_pos > select->bar_pos) select->top_pos = select->bar_pos; if (select->top_pos + height < select->bar_pos + 1) select->top_pos = select->bar_pos - height + 1; position = select->bar_pos - select->top_pos; getbegyx (select->win, y, x); mvwin (select->bar, y + position, x); show_percentage (select, count, height); } static void draw_content (select_t *select) { int width; int height; int offset; int i; validate (select); getmaxyx (select->win, height, width); offset = select->top_pos; for (i = 0; i < height; i++){ wmove (select->win, i, 0); select->draw_line (select->win, width, i + offset, search); } } static void draw_bar (select_t *select) { int maxlen; int nothing; if (select->bar == NULL) return; getmaxyx (select->bar, nothing, maxlen); wmove (select->bar, 0, 0); select->draw_line (select->bar, maxlen, select->bar_pos, search); } static WINDOW * get_bar (WINDOW *win) { int top, left; int y, x; WINDOW *result; getmaxyx (win, y, x); getbegyx (win, top, left); result = newwin (1, x, top, left); wattrset (result, text_color); return result; } static void refresh_me (select_t *select) { touchwin (select->win); wnoutrefresh (select->win); if (select->bar){ touchwin (select->bar); wnoutrefresh (select->bar); } } static int is_on_last (select_t *select) { int count = select->count (select); int height, nothing; getmaxyx (select->win, height, nothing); if (select->bar == NULL){ count -= height - 1; } return select->bar_pos >= count - 1; } static int is_on_first (select_t *select) { return select->bar_pos == 0; } static int bar_on_bottom (select_t *select) { int win_top, bar_top, win_height, nothing; if (select->bar == NULL) return 1; getbegyx (select->win, win_top, nothing); getbegyx (select->bar, bar_top, nothing); getmaxyx (select->win, win_height, nothing); return bar_top >= win_top + win_height - 1; } static int bar_on_top (select_t *select) { int win_top, bar_top, nothing; if (select->bar == NULL) return 1; getbegyx (select->win, win_top, nothing); getbegyx (select->bar, bar_top, nothing); return bar_top <= win_top; } static int bar_on_first_page (select_t *select) { int height, nothing; if (select->bar == NULL) select->bar_pos = select->top_pos; getmaxyx (select->win, height, nothing); return select->bar_pos <= height; } static int bar_on_last_page (select_t *select) { int count = select->count (select); int height, nothing; if (select->bar == NULL) select->bar_pos = select->top_pos; getmaxyx (select->win, height, nothing); return select->bar_pos + height >= count; } static void scroll_down (select_t *select) { select->top_pos++; draw_content (select); } static void scroll_up (select_t *select) { select->top_pos--; draw_content (select); } static chtype get_color (ask_t *ask, const char *fg_field, const char *bg_field, const char *fg_def, const char *bg_def) { char *fg = NULL; char *bg = NULL; if (ask){ fg = ask_get_field (ask, fg_field); bg = ask_get_field (ask, bg_field); } if (fg && bg){ return color_from_str (fg, bg); } else if (fg){ return color_from_str (fg, bg_def); } else if (bg){ return color_from_str (fg_def, bg); } return color_from_str (fg_def, bg_def); } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void select_init (void) { ask_t *ask = ask_select_default ("win_bar"); text_color = get_color (ask, "text_fg", "text_bg", "black", "cyan"); hilight_color = get_color (ask, "hilight_fg", "hilight_bg", "cyan", "blue"); if (ask) ask_destroy (ask); } chtype select_bar_color (void) { return text_color; } chtype select_hilight_color (void) { return hilight_color; } select_t * select_open (WINDOW *win, int no_bar, void (*draw_line) (WINDOW *, int, int, search_t *), int (*count) (select_t *)) { select_t *select = xmalloc (sizeof (select_t)); select->win = win; select->top_pos = 0; select->bar_pos = 0; select->draw_line = draw_line; select->count = count; if (! no_bar){ select->bar = get_bar (win); } else { select->bar = NULL; } return select; } void select_close (select_t *select) { if (select->win) delwin (select->win); if (select->bar) delwin (select->bar); xfree (select); } void select_show (select_t *select) { PREAMBLE; refresh_me (select); } void select_next (select_t *select) { int top, left; PREAMBLE; if (is_on_last (select)) return; if (bar_on_bottom (select)){ select->bar_pos++; scroll_down (select); } else { getbegyx (select->bar, top, left); select->bar_pos++; mvwin (select->bar, top + 1, left); } draw_bar (select); refresh_me (select); } void select_prev (select_t *select) { int top, left; PREAMBLE; if (is_on_first (select)) return; if (bar_on_top (select)){ select->bar_pos--; scroll_up (select); } else { getbegyx (select->bar, top, left); select->bar_pos--; mvwin (select->bar, top - 1, left); } draw_bar (select); refresh_me (select); } void select_scroll_up (select_t *select) { PREAMBLE; if (is_on_first (select)) return; if (bar_on_bottom (select)){ select->bar_pos--; } scroll_up (select); draw_bar (select); refresh_me (select); } void select_scroll_down (select_t *select) { PREAMBLE; if (is_on_last (select)) return; if (bar_on_top (select)){ select->bar_pos++; } scroll_down (select); draw_bar (select); refresh_me (select); } void select_next_page (select_t *select) { int height, nothing; PREAMBLE; if (bar_on_last_page (select)){ select_last (select); return; } getmaxyx (select->win, height, nothing); select->bar_pos += height; select->top_pos += height; select_redraw (select); } void select_prev_page (select_t *select) { int height, nothing; PREAMBLE; if (bar_on_first_page (select)){ select_first (select); return; } getmaxyx (select->win, height, nothing); select->bar_pos -= height; select->top_pos -= height; select_redraw (select); } void select_first (select_t *select) { PREAMBLE; select->bar_pos = 0; select->top_pos = 0; select_redraw (select); } void select_last (select_t *select) { int count; int pos; int height, nothing; PREAMBLE; getmaxyx (select->win, height, nothing); count = select->count (select); pos = count - height; if (select->top_pos < pos) select->top_pos = pos; if (select->bar == NULL) select->bar_pos = select->top_pos; else select->bar_pos = count - 1; select_redraw (select); } void select_recenter (select_t *select) { int height, nothing; int top; PREAMBLE; if (select->bar == NULL) return; getmaxyx (select->win, height, nothing); top = select->bar_pos - height / 2; if (top < 0) top = 0; select->top_pos = top; select_redraw (select); } void select_goto (select_t *select, int index) { int count; PREAMBLE; count = select->count (select); if (index >= count) return; if (select->bar) select->bar_pos = index; else { select->bar_pos = index; select->top_pos = index; } validate (select); } void select_redraw (select_t *select) { PREAMBLE; draw_content (select); draw_bar (select); wnoutrefresh (select->win); if (select->bar) wnoutrefresh (select->bar); } /**************************************************************************** * SEARCH PRIVATE FUNCTIONS ****************************************************************************/ static int first_true (select_t *select, int start) { int i; int count = select->count (select); for (i = start; i < count; i++){ if (select->match (search, i)) return i; } return -1; } static int first_true_back (select_t *select, int start) { int i; for (i = start; i >= 0; i--){ if (select->match (search, i)) return i; } return -1; } static int forward_search (int off) { int index; int count = search_select->count (search_select); if (search_select->bar_pos >= count - 1) return 0; index = first_true (search_select, search_select->bar_pos + off); if (index != -1) select_goto (search_select, index); else fprintf (stderr, "%c", '\a'); select_redraw (search_select); return index != -1; } static int backward_search (int off) { int index; if (search_select->bar_pos == 0) return 0; index = first_true_back (search_select, search_select->bar_pos - off); if (index != -1) select_goto (search_select, index); else fprintf (stderr, "%c", '\a'); select_redraw (search_select); return index != -1; } static void loop_search (int forward, int mistakes) { int ret; int meta; int c; str_t *str = str_create ();; if (mistakes) str_sprintf (str, _("[%d] search: "), mistakes); else str_put_string (str, _("search: ")); read_prepare (str->str, NULL, COMPLETE_NONE, HIDE_NO); str_destroy (str); while (1){ meta = 0; c = wgetch (cmd_win); if (c == 27){ meta = 1; c = wgetch (cmd_win); } if (keymap_action (keymaps + CMD_SEARCH, c, meta)){ if (c > 256 || ! isprint (c)){ fprintf (stderr, "%c", '\a'); break; } if (search_add_char (search, c) == 0) read_put_char (c); else fprintf (stderr, "%c", '\a'); } if (forward) ret = forward_search (0); else ret = backward_search (0); if (! ret) select_search_backspace (); read_echo_input (); } search_destroy (search); read_restore (); search = NULL; search_select->match = NULL; select_redraw (search_select); search_select = NULL; } /**************************************************************************** * SEARCH FUNCTIONS ****************************************************************************/ void select_search_setup_forward (select_t *select, int (*match)(search_t *, int)) { int mistakes; mistakes = cmd_prefix_arg (); search = search_create (SEARCH_INSENSITIVE, mistakes); search_select = select; search_select->match = match; loop_search (1, mistakes); } void select_search_setup_backward (select_t *select, int (*match)(search_t *, int)) { int mistakes; mistakes = cmd_prefix_arg (); search = search_create (SEARCH_INSENSITIVE, mistakes); search_select = select; search_select->match = match; loop_search (0, mistakes); } void select_search_forward (void) { if (search) forward_search (1); } void select_search_backward (void) { if (search) backward_search (1); } void select_search_backspace (void) { if (search){ search_chop (search); read_del_char_back (); select_redraw (search_select); } } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE select.c * ****************************************************************************/