/* 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. ---------------------------------------------------------------------- Discussion: There are 2 kinds of windows: 1. Windows with non-configurable size and position. These windows must be refreshed by the interface module on demand. But you cannot switch to such a window, it cannot take focus. 2. Windows with configurable size. These are all the windows, that the user would generally consider as a window. They can be focused, consist of multiple WINDOW objects and so on. This module must keep the screen clean, and up to date. It must decide which windows are visible, and draw them in the correct order. */ /**************************************************************************** * IMPLEMENTATION HEADERS ****************************************************************************/ #include #include #include "ecurses.h" #include "interface.h" #include "xmalloc.h" #include "error.h" #include "gettext.h" #include "debug.h" #include "status.h" #include "topbar.h" #include "cmd.h" /**************************************************************************** * IMPLEMENTATION PRIVATE DEFINITIONS / ENUMERATIONS / SIMPLE TYPEDEFS ****************************************************************************/ #define HEAP_SIZE 8 /**************************************************************************** * IMPLEMENTATION PRIVATE CLASS PROTOTYPES / EXTERNAL CLASS REFERENCES ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE STRUCTURES / UTILITY CLASSES ****************************************************************************/ struct frame { struct frame *next; WINDOW *win; /* window or NULL */ char *name; /* name displayed in statusbar */ int num; /* window id or -1 if it cannot get focus */ int hidden; /* if it is hidden */ void (*show) (void); void (*draw) (void); void (*set_focus) (void); void (*unset_focus) (void); }; /**************************************************************************** * IMPLEMENTATION REQUIRED EXTERNAL REFERENCES (AVOID) ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE DATA ****************************************************************************/ /* This is used in signal handler to indicate that this error is being handled (see info libc) */ static volatile sig_atomic_t error_indicator = 0; /* I use heap to store numbers, that are assigned to windows, when they are displayed. When window is being hidden, its number is inserted in the heap. When window is being opened module assings the minimal value from the heap. */ static int num_heap[HEAP_SIZE]; static int heap_count = 0; static struct frame *visible_windows = NULL; static struct frame *hidden_windows = NULL; static struct frame *other_windows = NULL; static struct frame *focused_window = NULL; /**************************************************************************** * INTERFACE DATA ****************************************************************************/ /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTION PROTOTYPES ****************************************************************************/ /**************************************************************************** * HEAP FUNCTIONS ****************************************************************************/ static void heap_exchange (int i, int j) { int tmp = num_heap[i]; num_heap[i] = num_heap[j]; num_heap[j] = tmp; } static void heapify_up (void) { int i = heap_count; int p; while (1){ if (i == 1) break; p = i >> 1; if (num_heap[i] >= num_heap[p]) break; heap_exchange (i, p); i = p; } } static void heapify_down (void) { int i = 1; int l, r; while (1){ if ((i << 1) >= heap_count) break; l = i << 1; r = (i << 1) | 1; if (num_heap[l] > num_heap[r]) l = r; if (num_heap[i] <= num_heap[l]) break; heap_exchange (i, l); i = l; } } static void heap_insert (int i) { heap_count++; num_heap[heap_count] = i; heapify_up (); } static int heap_extract_min (void) { int last = heap_count; int ret = num_heap[1]; heap_count--; num_heap[1] = num_heap[last]; num_heap[last] = -1; heapify_down (); return ret; } static void heap_init (void) { int i; for (i = 0; i < HEAP_SIZE; i++) num_heap[i] = -1; for (i = 1; i < HEAP_SIZE; i++) heap_insert (i); } /**************************************************************************** * IMPLEMENTATION PRIVATE FUNCTIONS ****************************************************************************/ static void curses_cleanup (int signum) { if (error_indicator) raise (signum); error_indicator = 1; /* cleanup code start */ endwin (); /* cleanup code end */ signal (signum, SIG_DFL); raise (signum); } static void ncurses_setup (void) { signal (SIGTERM, curses_cleanup); signal (SIGQUIT, curses_cleanup); signal (SIGINT, curses_cleanup); signal (SIGSEGV, curses_cleanup); signal (SIGFPE, curses_cleanup); signal (SIGABRT, curses_cleanup); signal (SIGBUS, curses_cleanup); if (! initscr ()){ error_critical (1, 0, _("ncurses: initialization failure")); } cbreak (); noecho (); nonl (); curs_set (0); if (has_colors ()){ start_color (); } } static void destroy_list (struct frame *list) { if (list == NULL) return; destroy_list (list->next); xfree (list); } static struct frame * list_rem_win (struct frame **list, WINDOW *win) { struct frame *result; if (*list == NULL) return NULL; if ((*list)->win == win){ result = *list; *list = (*list)->next; return result; } return list_rem_win (& (*list)->next, win); } static struct frame * list_find_win (struct frame *list, WINDOW *win) { if (list == NULL) return NULL; if (list->win == win) return list; return list_find_win (list->next, win); } static struct frame * list_find_num (struct frame *list, int num) { if (list == NULL) return NULL; if (list->num == num) return list; return list_find_num (list->next, num); } static void unfocus (void) { if (focused_window == NULL) return; if (focused_window->unset_focus) focused_window->unset_focus (); } static int list_find_min (struct frame *list, int i) { int x; if (list == NULL) return i; x = list_find_min (list->next, i); if (i == x){ return list->num; } else if (x > i){ if (list->num < x && list->num > i) return list->num; return x; } else { if (list->num < x || list->num > i) return list->num; return x; } } static int list_find_max (struct frame *list, int i) { int x; if (list == NULL) return i; x = list_find_min (list->next, i); if (i == x){ return list->num; } else if (x < i){ if (list->num > x && list->num < i) return list->num; return x; } else { if (list->num > x || list->num < i) return list->num; return x; } } /**************************************************************************** * INTERFACE FUNCTIONS ****************************************************************************/ void interface_free_resources (void) { destroy_list (visible_windows); destroy_list (hidden_windows); destroy_list (other_windows); } void interface_setup (void) { error_indicator = 0; heap_init (); ncurses_setup (); } void interface_post_setup (void) { interface_focus (1); } void interface_redraw (void) { struct frame *list; for (list = other_windows; list; list = list->next){ if (list->draw) list->draw (); else touchwin (list->win); if (list->show) list->show (); else wnoutrefresh (list->win); } for (list = visible_windows; list; list = list->next){ if (list->draw) list->draw (); else touchwin (list->win); if (list->show) list->show (); else wnoutrefresh (list->win); } if (focused_window->draw) focused_window->draw (); if (focused_window->show) focused_window->show (); } void interface_focus (int num) { struct frame *frame = list_find_num (visible_windows, num); if (frame == NULL) return; if (focused_window && focused_window->num == num) return; unfocus (); status_switch_window (frame->name, frame->num); if (frame->set_focus) frame->set_focus (); focused_window = frame; topbar_refresh (); list_rem_win (& visible_windows, frame->win); frame->next = visible_windows; visible_windows = frame; } void interface_next_window (void) { int i; if (focused_window == NULL) return; i = list_find_min (visible_windows, focused_window->num); if (i == focused_window->num) return; interface_focus (i); } void interface_prev_window (void) { int i; if (focused_window == NULL) return; i = list_find_max (visible_windows, focused_window->num); if (i == focused_window->num) return; interface_focus (i); } void interface_num_window (void) { int i = cmd_last_char (); if (i < '0' || i > '9') return; interface_focus (i - '0'); } /**************************************************************************** * WINDOW FUNCTIONS ****************************************************************************/ WINDOW * window_create (char *name, int height, int width, int top, int left, int focus) { struct frame *frame = xmalloc (sizeof (struct frame)); frame->show = NULL; frame->draw = NULL; frame->set_focus = NULL; frame->unset_focus = NULL; frame->name = name; frame->num = -1; frame->hidden = 1; frame->win = newwin (height, width, top, left); if (frame->win == NULL) error_critical (1, 0, _("invalid window")); if (focus){ frame->next = hidden_windows; hidden_windows = frame; } else { frame->next = other_windows; other_windows = frame; } return frame->win; } void window_set_functions (WINDOW *win, void (*show)(void), void (*draw)(void), void (*set_focus)(void), void (*unset_focus)(void)) { struct frame *frame = list_find_win (hidden_windows, win); if (frame == NULL){ debug_msg (DEBUG_ERROR, "no frame found in window_set_funs"); return; } frame->show = show; frame->draw = draw; frame->set_focus = set_focus; frame->unset_focus = unset_focus; } void window_hide (WINDOW *win) { struct frame *frame = list_rem_win (& visible_windows, win); if (frame == NULL) return; heap_insert (frame->num); frame->num = -1; frame->next = hidden_windows; hidden_windows = frame; if (visible_windows) interface_focus (visible_windows->num); interface_redraw (); } void window_show (WINDOW *win) { struct frame *frame = list_rem_win (& hidden_windows, win); if (frame == NULL){ frame = list_find_win (visible_windows, win); if (frame) interface_focus (frame->num); return; } frame->num = heap_extract_min (); frame->next = visible_windows; visible_windows = frame; interface_focus (frame->num); } int window_addch (WINDOW *win, chtype c) { int ret; if (win == NULL) return ERR; if (c == '\r' || c == '\n') return ERR; if (c == '\t') c = ' '; if (c < 32){ wattron (win, A_REVERSE); ret = waddch (win, c + 64); wattroff (win, A_REVERSE); return ret; } waddch (win, c); return OK; } int window_addnstr (WINDOW *win, const char *str, int n) { int i = n; if (win == NULL || str == NULL) return 0; while (i && *str){ if (window_addch (win, (unsigned char) *str) != ERR) i--; str++; } return n - i; } int window_addnast (WINDOW *win, const char *str, int n) { int i = n; if (win == NULL || str == NULL) return 0; while (i && *str){ if (window_addch (win, '*') != ERR) i--; str++; } return n - i; } int window_addchnstr (WINDOW *win, const chtype *str, int n) { int i = n; if (win == NULL || str == NULL) return 0; while (i && *str){ if (window_addch (win, *str) != ERR) i--; str++; } return n - i; } /**************************************************************************** * INTERFACE CLASS BODIES ****************************************************************************/ /**************************************************************************** * * END MODULE interface.c * ****************************************************************************/