/*
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 <string.h>
#include <ctype.h>
#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
*
****************************************************************************/
syntax highlighted by Code2HTML, v. 0.9.1