diff options
-rw-r--r-- | Makefile | 21 | ||||
-rw-r--r-- | interface-ncurses.cpp | 226 | ||||
-rw-r--r-- | interface-ncurses.h | 48 |
3 files changed, 286 insertions, 9 deletions
@@ -6,22 +6,24 @@ GOB2:=gob2 GLIB_CFLAGS:=$(shell pkg-config --cflags glib-2.0) GLIB_LDFLAGS:=$(shell pkg-config --libs glib-2.0) +SCI_CFLAGS:=-I../scintilla/include -D$(INTERFACE) -DSCI_LEXER +SCI_LDFLAGS:=../scintilla/bin/scintilla.a + ifeq ($(INTERFACE),GTK) GTK_CFLAGS:=$(shell pkg-config --cflags gtk+-2.0) GTK_LDFLAGS:=$(shell pkg-config --libs gtk+-2.0) else ifeq ($(INTERFACE),NCURSES) -# TODO +SCI_CFLAGS+=-I../scintilla/scinterm_1.0 +NCURSES_CFLAGS:= +NCURSES_LDFLAGS:=-lncurses endif -SCI_CFLAGS:=-I../scintilla/include -D$(INTERFACE) -DSCI_LEXER -SCI_LDFLAGS:=../scintilla/bin/scintilla.a - CPPFLAGS:=-DINTERFACE_$(INTERFACE) CFLAGS:=-Wall -std=c99 -g -O0 \ - $(GLIB_CFLAGS) $(GTK_CFLAGS) $(SCI_CFLAGS) + $(GLIB_CFLAGS) $(GTK_CFLAGS) $(NCURSES_CFLAGS) $(SCI_CFLAGS) CXXFLAGS:=-Wall -g -O0 \ - $(GLIB_CFLAGS) $(GTK_CFLAGS) $(SCI_CFLAGS) -LDFLAGS:=$(GLIB_LDFLAGS) $(GTK_LDFLAGS) $(SCI_LDFLAGS) + $(GLIB_CFLAGS) $(GTK_CFLAGS) $(NCURSES_CFLAGS) $(SCI_CFLAGS) +LDFLAGS:=$(GLIB_LDFLAGS) $(GTK_LDFLAGS) $(NCURSES_LDFLAGS) $(SCI_LDFLAGS) all : sciteco @@ -34,13 +36,14 @@ ifeq ($(INTERFACE),GTK) interface.a : interface-gtk.o gtk-info-popup.o $(AR) rc $@ $^ + touch $@ interface-gtk.o : gtk-info-popup.h else ifeq ($(INTERFACE),NCURSES) -# TODO -interface.a : +interface.a : interface-ncurses.o $(AR) rc $@ $^ + touch $@ endif diff --git a/interface-ncurses.cpp b/interface-ncurses.cpp new file mode 100644 index 0000000..48dbec6 --- /dev/null +++ b/interface-ncurses.cpp @@ -0,0 +1,226 @@ +#include <string.h> +#include <stdarg.h> +#include <locale.h> + +#include <glib.h> +#include <glib/gprintf.h> + +#include <ncurses.h> + +#include <Scintilla.h> +#include <ScintillaTerm.h> + +#include "sciteco.h" +#include "interface.h" +#include "interface-ncurses.h" + +InterfaceNCurses interface; + +extern "C" { +static void scnotification(Scintilla *view, int i, void *p1, void *p2); +} + +/* FIXME: should be configurable in TECO */ +#define ESCAPE_SURROGATE KEY_DC + +/* from ScintillaTerm.cxx */ +#define SCI_COLOR_PAIR(f, b) ((b) * COLORS + (f) + 1) + +InterfaceNCurses::InterfaceNCurses() + : popup_window(NULL), popup_list(NULL), + popup_list_longest(0), popup_list_length(0) +{ + initscr(); + raw(); + cbreak(); + noecho(); + curs_set(0); // Scintilla draws its own cursor + + setlocale(LC_CTYPE, ""); // for displaying UTF-8 characters properly + + /* TODO: handle terminal resize */ + /* NOTE: initializes color pairs */ + sci = scintilla_new(scnotification); + sci_window = scintilla_get_window(sci); + wresize(sci_window, LINES - 2, COLS); + + msg_window = newwin(1, 0, LINES - 2, 0); + + cmdline_window = newwin(0, 0, LINES - 1, 0); + keypad(cmdline_window, TRUE); + + ssm(SCI_SETFOCUS, TRUE); + + msg(MSG_USER, " "); + cmdline_update(); +} + +void +InterfaceNCurses::msg(MessageType type, const gchar *fmt, ...) +{ + static const short type2colorid[] = { + SCI_COLOR_PAIR(COLOR_BLACK, COLOR_WHITE), /* MSG_USER */ + SCI_COLOR_PAIR(COLOR_BLACK, COLOR_GREEN), /* MSG_INFO */ + SCI_COLOR_PAIR(COLOR_BLACK, COLOR_YELLOW), /* MSG_WARNING */ + SCI_COLOR_PAIR(COLOR_BLACK, COLOR_RED) /* MSG_ERROR */ + }; + + va_list ap; + + wmove(msg_window, 0, 0); + wbkgdset(msg_window, ' ' | COLOR_PAIR(type2colorid[type])); + va_start(ap, fmt); + vw_printw(msg_window, fmt, ap); + va_end(ap); + wclrtoeol(msg_window); + + wrefresh(msg_window); +} + +void +InterfaceNCurses::cmdline_update(const gchar *cmdline) +{ + size_t len = strlen(cmdline); + int half_line = (COLS - 2) / 2; + const gchar *line; + + /* FIXME: optimize */ + line = cmdline + len - MIN(len, half_line + len % half_line); + + mvwaddch(cmdline_window, 0, 0, '*'); + waddstr(cmdline_window, line); + waddch(cmdline_window, ' ' | A_REVERSE); + wclrtoeol(cmdline_window); + + wrefresh(cmdline_window); +} + +void +InterfaceNCurses::popup_add_filename(PopupFileType type, + const gchar *filename, bool highlight) +{ + gchar *entry = g_strconcat(highlight ? "*" : " ", filename, NULL); + + popup_list_longest = MAX(popup_list_longest, (gint)strlen(filename)); + popup_list_length++; + + popup_list = g_slist_prepend(popup_list, entry); +} + +void +InterfaceNCurses::popup_show(void) +{ + int popup_lines; + gint popup_cols, cur_file; + + popup_list_longest += 3; + popup_list = g_slist_reverse(popup_list); + + popup_cols = MIN(popup_list_length, COLS / popup_list_longest); + popup_lines = (popup_list_length + popup_list_length % popup_cols)/ + popup_cols; + /* window covers message and scintilla windows */ + popup_window = newwin(popup_lines, 0, LINES - 1 - popup_lines, 0); + wbkgdset(popup_window, + ' ' | COLOR_PAIR(SCI_COLOR_PAIR(COLOR_BLACK, COLOR_BLUE))); + + cur_file = 0; + for (GSList *cur = popup_list; cur; cur = g_slist_next(cur)) { + gchar *entry = (gchar *)cur->data; + + if (cur_file && !(cur_file % popup_cols)) { + wclrtoeol(popup_window); + waddch(popup_window, '\n'); + } + + (void)wattrset(popup_window, *entry == '*' ? A_BOLD : A_NORMAL); + waddstr(popup_window, entry + 1); + for (int i = popup_list_longest - strlen(entry) + 1; i; i--) + waddch(popup_window, ' '); + + g_free(cur->data); + cur_file++; + } + wclrtoeol(popup_window); + + g_slist_free(popup_list); + popup_list = NULL; + popup_list_longest = 0; + popup_list_length = 0; +} + +void +InterfaceNCurses::popup_clear(void) +{ + scintilla_refresh(sci); + redrawwin(msg_window); + wrefresh(msg_window); + if (popup_window) + delwin(popup_window); + popup_window = NULL; +} + +void +InterfaceNCurses::event_loop(void) +{ + for (;;) { + int key; + + /* also handles initial refresh (styles are configured...) */ + scintilla_refresh(sci); + if (popup_window) + wrefresh(popup_window); + + key = wgetch(cmdline_window); + switch (key) { + case ESCAPE_SURROGATE: + cmdline_keypress('\x1B'); + break; + case KEY_BACKSPACE: + cmdline_keypress('\b'); + break; + case KEY_ENTER: + switch (ssm(SCI_GETEOLMODE)) { + case SC_EOL_CR: + cmdline_keypress('\r'); + break; + case SC_EOL_CRLF: + cmdline_keypress('\r'); + /* fall through */ + case SC_EOL_LF: + default: + cmdline_keypress('\n'); + } + break; + default: + if (key <= 0xFF) + cmdline_keypress((gchar)key); + } + } +} + +InterfaceNCurses::~InterfaceNCurses() +{ + scintilla_delete(sci); + delwin(sci_window); + + delwin(cmdline_window); + delwin(msg_window); + + if (popup_window) + delwin(popup_window); + if (popup_list) + g_slist_free(popup_list); + + endwin(); +} + +/* + * Callbacks + */ + +static void +scnotification(Scintilla *view, int i, void *p1, void *p2) +{ + /* TODO */ +} diff --git a/interface-ncurses.h b/interface-ncurses.h new file mode 100644 index 0000000..a9bcad5 --- /dev/null +++ b/interface-ncurses.h @@ -0,0 +1,48 @@ +#ifndef __INTERFACE_NCURSES_H +#define __INTERFACE_NCURSES_H + +#include <glib.h> + +#include <ncurses.h> + +#include <Scintilla.h> +#include <ScintillaTerm.h> + +#include "interface.h" + +extern class InterfaceNCurses : public Interface { + Scintilla *sci; + + WINDOW *sci_window; + WINDOW *msg_window; + WINDOW *cmdline_window; + + WINDOW *popup_window; + GSList *popup_list; + gint popup_list_longest; + gint popup_list_length; + +public: + InterfaceNCurses(); + ~InterfaceNCurses(); + + void msg(MessageType type, const gchar *fmt, ...) G_GNUC_PRINTF(3, 4); + + inline sptr_t + ssm(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0) + { + return scintilla_send_message(sci, iMessage, wParam, lParam); + } + + void cmdline_update(const gchar *cmdline = ""); + + void popup_add_filename(PopupFileType type, + const gchar *filename, bool highlight = false); + void popup_show(void); + void popup_clear(void); + + /* main entry point */ + void event_loop(void); +} interface; + +#endif |