diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-11-16 14:42:47 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2012-11-16 14:42:47 +0100 |
commit | f6ff327f0b7b50b74328e448ce862f7212dcae23 (patch) | |
tree | 4a773dcb9120d6a7fb96fce92422667e8702dbdf | |
parent | 0a8f940ffe1aaf77ba12ccc02d4e382be2118151 (diff) | |
download | sciteco-f6ff327f0b7b50b74328e448ce862f7212dcae23.tar.gz |
keep a buffer dirty flag and display infos about the current buffer in the interfaces (including the dirty flag)
* was a bit tricky because the Scintilla SAVEPOINTS cannot be (fully) used
* when a file is loaded or saved, a Scintilla SAVEPOINT is set
* SAVEPOINTLEFT notifications are used to set a buffer dirty
* SAVEPOINTREACHED notifications are useless since Scintilla does not consider the saves themselves to be undoable
* GTK interface displays infos in window title bar
* NCURSES interface has also been updated and cleaned up a bit. Infos are displayed in a new info line.
* NCURSES: fixed popup display after terminal resizing
-rw-r--r-- | interface-gtk.cpp | 49 | ||||
-rw-r--r-- | interface-gtk.h | 6 | ||||
-rw-r--r-- | interface-ncurses.cpp | 158 | ||||
-rw-r--r-- | interface-ncurses.h | 19 | ||||
-rw-r--r-- | interface.h | 39 | ||||
-rw-r--r-- | main.cpp | 30 | ||||
-rw-r--r-- | qbuffers.cpp | 15 | ||||
-rw-r--r-- | qbuffers.h | 10 |
8 files changed, 261 insertions, 65 deletions
diff --git a/interface-gtk.cpp b/interface-gtk.cpp index ed0b36c..d348a09 100644 --- a/interface-gtk.cpp +++ b/interface-gtk.cpp @@ -14,26 +14,31 @@ #include <ScintillaWidget.h> #include "sciteco.h" +#include "qbuffers.h" #include "interface.h" #include "interface-gtk.h" InterfaceGtk interface; extern "C" { +static void scintilla_notify(ScintillaObject *sci, uptr_t idFrom, + SCNotification *notify, gpointer user_data); static gboolean cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data); static gboolean exit_app(GtkWidget *w, GdkEventAny *e, gpointer p); } +#define UNNAMED_FILE "(Unnamed)" + InterfaceGtk::InterfaceGtk() { - GtkWidget *window, *vbox; + GtkWidget *vbox; GtkWidget *info_content; gtk_init(NULL, NULL); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(window), "SciTECO"); + gtk_window_set_title(GTK_WINDOW(window), PACKAGE_NAME); g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(exit_app), NULL); @@ -43,6 +48,8 @@ InterfaceGtk::InterfaceGtk() scintilla_set_id(SCINTILLA(editor_widget), 0); gtk_widget_set_usize(editor_widget, 500, 300); gtk_widget_set_can_focus(editor_widget, FALSE); + g_signal_connect(G_OBJECT(editor_widget), SCINTILLA_NOTIFY, + G_CALLBACK(scintilla_notify), NULL); gtk_box_pack_start(GTK_BOX(vbox), editor_widget, TRUE, TRUE, 0); info_widget = gtk_info_bar_new(); @@ -96,6 +103,27 @@ InterfaceGtk::vmsg(MessageType type, const gchar *fmt, va_list ap) } void +InterfaceGtk::info_update(QRegister *reg) +{ + gchar buf[255]; + + g_snprintf(buf, sizeof(buf), "%s - <QRegister> %s", PACKAGE_NAME, + reg->name); + gtk_window_set_title(GTK_WINDOW(window), buf); +} + +void +InterfaceGtk::info_update(Buffer *buffer) +{ + gchar buf[255]; + + g_snprintf(buf, sizeof(buf), "%s - <Buffer> %s%s", PACKAGE_NAME, + buffer->filename ? : UNNAMED_FILE, + buffer->dirty ? "*" : ""); + gtk_window_set_title(GTK_WINDOW(window), buf); +} + +void InterfaceGtk::cmdline_update(const gchar *cmdline) { gint pos = 1; @@ -142,10 +170,27 @@ InterfaceGtk::widget_set_font(GtkWidget *widget, const gchar *font_name) pango_font_description_free(font_desc); } +InterfaceGtk::~InterfaceGtk() +{ + gtk_widget_destroy(popup_widget); + gtk_widget_destroy(window); + + scintilla_release_resources(); +} + /* * GTK+ callbacks */ +static void +scintilla_notify(ScintillaObject *sci __attribute__((unused)), + uptr_t idFrom __attribute__((unused)), + SCNotification *notify, + gpointer user_data __attribute__((unused))) +{ + interface.process_notify(notify); +} + static gboolean cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer user_data __attribute__((unused))) diff --git a/interface-gtk.h b/interface-gtk.h index 661786a..1c62b4a 100644 --- a/interface-gtk.h +++ b/interface-gtk.h @@ -12,6 +12,7 @@ #include "interface.h" extern class InterfaceGtk : public Interface { + GtkWidget *window; GtkWidget *editor_widget; GtkWidget *cmdline_widget; GtkWidget *info_widget, *message_widget; @@ -20,7 +21,7 @@ extern class InterfaceGtk : public Interface { public: InterfaceGtk(); - //~InterfaceGtk(); + ~InterfaceGtk(); inline GOptionGroup * get_options(void) @@ -42,6 +43,9 @@ public: iMessage, wParam, lParam); } + void info_update(QRegister *reg); + void info_update(Buffer *buffer); + void cmdline_update(const gchar *cmdline = NULL); void popup_add_filename(PopupFileType type, diff --git a/interface-ncurses.cpp b/interface-ncurses.cpp index 7fc6ae9..9d4c4fe 100644 --- a/interface-ncurses.cpp +++ b/interface-ncurses.cpp @@ -13,24 +13,30 @@ #include <ScintillaTerm.h> #include "sciteco.h" +#include "qbuffers.h" #include "interface.h" #include "interface-ncurses.h" InterfaceNCurses interface; extern "C" { -static void scnotification(Scintilla *view, int i, void *p1, void *p2); +static void scintilla_notify(Scintilla *sci, int idFrom, + void *notify, void *user_data); } -/* FIXME: should be configurable in TECO */ +#define UNNAMED_FILE "(Unnamed)" + +/* FIXME: should be configurable in TECO (Function key substitutes) */ #define ESCAPE_SURROGATE KEY_DC /* from ScintillaTerm.cxx */ -#define SCI_COLOR_PAIR(f, b) ((b) * COLORS + (f) + 1) +#define SCI_COLOR_PAIR(f, b) \ + ((b) * COLORS + (f) + 1) + +#define SCI_COLOR_ATTR(f, b) \ + COLOR_PAIR(SCI_COLOR_PAIR(f, b)) InterfaceNCurses::InterfaceNCurses() - : popup_window(NULL), popup_list(NULL), - popup_list_longest(0), popup_list_length(0) { /* * Prevent the initial redraw and any escape sequences that may @@ -50,10 +56,14 @@ InterfaceNCurses::InterfaceNCurses() setlocale(LC_CTYPE, ""); /* for displaying UTF-8 characters properly */ - /* NOTE: initializes color pairs */ - sci = scintilla_new(scnotification); + info_window = newwin(1, 0, 0, 0); + info_current = g_strdup(PACKAGE_NAME); + + /* NOTE: Scintilla initializes color pairs */ + sci = scintilla_new(scintilla_notify); sci_window = scintilla_get_window(sci); - wresize(sci_window, LINES - 2, COLS); + wresize(sci_window, LINES - 3, COLS); + mvwin(sci_window, 1, 0); msg_window = newwin(1, 0, LINES - 2, 0); @@ -63,6 +73,7 @@ InterfaceNCurses::InterfaceNCurses() ssm(SCI_SETFOCUS, TRUE); + draw_info(); /* scintilla will be refreshed in event loop */ msg(MSG_USER, " "); cmdline_update(""); @@ -73,16 +84,18 @@ InterfaceNCurses::InterfaceNCurses() void InterfaceNCurses::resize_all_windows(void) { - int lines, cols; + int lines, cols; /* screen dimensions */ getmaxyx(stdscr, lines, cols); - wresize(sci_window, lines - 2, cols); + wresize(info_window, 1, cols); + wresize(sci_window, lines - 3, cols); wresize(msg_window, 1, cols); mvwin(msg_window, lines - 2, 0); wresize(cmdline_window, 1, cols); mvwin(cmdline_window, lines - 1, 0); + draw_info(); /* scintilla will be refreshed in event loop */ msg(MSG_USER, " "); /* FIXME: use saved message */ cmdline_update(); @@ -91,11 +104,11 @@ InterfaceNCurses::resize_all_windows(void) void InterfaceNCurses::vmsg(MessageType type, const gchar *fmt, va_list ap) { - 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 */ + static const int type2attr[] = { + SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE), /* MSG_USER */ + SCI_COLOR_ATTR(COLOR_BLACK, COLOR_GREEN), /* MSG_INFO */ + SCI_COLOR_ATTR(COLOR_BLACK, COLOR_YELLOW), /* MSG_WARNING */ + SCI_COLOR_ATTR(COLOR_BLACK, COLOR_RED) /* MSG_ERROR */ }; if (isendwin()) { /* batch mode */ @@ -104,7 +117,7 @@ InterfaceNCurses::vmsg(MessageType type, const gchar *fmt, va_list ap) } wmove(msg_window, 0, 0); - wbkgdset(msg_window, ' ' | COLOR_PAIR(type2colorid[type])); + wbkgdset(msg_window, ' ' | type2attr[type]); vw_printw(msg_window, fmt, ap); wclrtoeol(msg_window); @@ -112,6 +125,38 @@ InterfaceNCurses::vmsg(MessageType type, const gchar *fmt, va_list ap) } void +InterfaceNCurses::draw_info(void) +{ + wmove(info_window, 0, 0); + wbkgdset(info_window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_WHITE)); + waddstr(info_window, info_current); + wclrtoeol(info_window); + + wrefresh(info_window); +} + +void +InterfaceNCurses::info_update(QRegister *reg) +{ + g_free(info_current); + info_current = g_strdup_printf("%s - <QRegister> %s", PACKAGE_NAME, + reg->name); + + draw_info(); +} + +void +InterfaceNCurses::info_update(Buffer *buffer) +{ + g_free(info_current); + info_current = g_strdup_printf("%s - <Buffer> %s%s", PACKAGE_NAME, + buffer->filename ? : UNNAMED_FILE, + buffer->dirty ? "*" : ""); + + draw_info(); +} + +void InterfaceNCurses::cmdline_update(const gchar *cmdline) { size_t len; @@ -143,65 +188,68 @@ InterfaceNCurses::popup_add_filename(PopupFileType type, { gchar *entry = g_strconcat(highlight ? "*" : " ", filename, NULL); - popup_list_longest = MAX(popup_list_longest, (gint)strlen(filename)); - popup_list_length++; + popup.longest = MAX(popup.longest, (gint)strlen(filename)); + popup.length++; - popup_list = g_slist_prepend(popup_list, entry); + popup.list = g_slist_prepend(popup.list, entry); } void InterfaceNCurses::popup_show(void) { + int lines, cols; /* screen dimensions */ int popup_lines; gint popup_cols, cur_file; - popup_list_longest += 3; - popup_list = g_slist_reverse(popup_list); + getmaxyx(stdscr, lines, cols); + + popup.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; + popup_cols = MIN(popup.length, cols / popup.longest); + popup_lines = (popup.length + popup.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))); + popup.window = newwin(popup_lines, 0, lines - 1 - popup_lines, 0); + wbkgdset(popup.window, ' ' | SCI_COLOR_ATTR(COLOR_BLACK, COLOR_BLUE)); cur_file = 0; - for (GSList *cur = popup_list; cur; cur = g_slist_next(cur)) { + 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'); + 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, ' '); + (void)wattrset(popup.window, *entry == '*' ? A_BOLD : A_NORMAL); + waddstr(popup.window, entry + 1); + for (int i = popup.longest - strlen(entry) + 1; i; i--) + waddch(popup.window, ' '); g_free(cur->data); cur_file++; } - wclrtoeol(popup_window); + wclrtoeol(popup.window); - g_slist_free(popup_list); - popup_list = NULL; - popup_list_longest = 0; - popup_list_length = 0; + g_slist_free(popup.list); + popup.list = NULL; + popup.longest = popup.length = 0; } void InterfaceNCurses::popup_clear(void) { - if (!popup_window) + if (!popup.window) return; + redrawwin(info_window); + wrefresh(info_window); scintilla_refresh(sci); redrawwin(msg_window); wrefresh(msg_window); - delwin(popup_window); - popup_window = NULL; + + delwin(popup.window); + popup.window = NULL; } void @@ -215,8 +263,8 @@ InterfaceNCurses::event_loop(void) /* also handles initial refresh (styles are configured...) */ scintilla_refresh(sci); - if (popup_window) - wrefresh(popup_window); + if (popup.window) + wrefresh(popup.window); key = wgetch(cmdline_window); switch (key) { @@ -250,20 +298,24 @@ InterfaceNCurses::event_loop(void) } } +InterfaceNCurses::Popup::~Popup() +{ + if (window) + delwin(window); + if (list) + g_slist_free(list); +} + InterfaceNCurses::~InterfaceNCurses() { + delwin(info_window); + g_free(info_current); scintilla_delete(sci); delwin(sci_window); - delwin(cmdline_window); g_free(cmdline_current); delwin(msg_window); - if (popup_window) - delwin(popup_window); - if (popup_list) - g_slist_free(popup_list); - if (!isendwin()) endwin(); delscreen(screen); @@ -275,7 +327,9 @@ InterfaceNCurses::~InterfaceNCurses() */ static void -scnotification(Scintilla *view, int i, void *p1, void *p2) +scintilla_notify(Scintilla *sci, int idFrom, void *_notify, void *user_data) { - /* TODO */ + SCNotification *notify = (SCNotification *)_notify; + + interface.process_notify(notify); } diff --git a/interface-ncurses.h b/interface-ncurses.h index 9469cfc..28f2e72 100644 --- a/interface-ncurses.h +++ b/interface-ncurses.h @@ -18,17 +18,25 @@ extern class InterfaceNCurses : public Interface { Scintilla *sci; + WINDOW *info_window; + gchar *info_current; WINDOW *sci_window; WINDOW *msg_window; WINDOW *cmdline_window; gchar *cmdline_current; - WINDOW *popup_window; - GSList *popup_list; - gint popup_list_longest; - gint popup_list_length; + struct Popup { + WINDOW *window; + GSList *list; + gint longest; + gint length; + + Popup() : window(NULL), list(NULL), longest(0), length(0) {} + ~Popup(); + } popup; void resize_all_windows(void); + void draw_info(void); public: InterfaceNCurses(); @@ -42,6 +50,9 @@ public: return scintilla_send_message(sci, iMessage, wParam, lParam); } + void info_update(QRegister *reg); + void info_update(Buffer *buffer); + void cmdline_update(const gchar *cmdline = NULL); void popup_add_filename(PopupFileType type, diff --git a/interface.h b/interface.h index d40506a..f89307e 100644 --- a/interface.h +++ b/interface.h @@ -7,6 +7,12 @@ #include <Scintilla.h> +#include "undo.h" + +/* avoid include dependency conflict */ +class QRegister; +class Buffer; + /* * Base class for all user interfaces - used mereley as a class interface. * The actual instance of the interface has the platform-specific type @@ -16,6 +22,22 @@ * There's only one Interface* instance in the system. */ class Interface { + template <class Type> + class UndoTokenInfoUpdate : public UndoToken { + Interface *iface; + Type *obj; + + public: + UndoTokenInfoUpdate(Interface *_iface, Type *_obj) + : iface(_iface), obj(_obj) {} + + void + run(void) + { + iface->info_update(obj); + } + }; + public: virtual GOptionGroup * get_options(void) @@ -44,6 +66,16 @@ public: virtual sptr_t ssm(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0) = 0; + virtual void info_update(QRegister *reg) = 0; + virtual void info_update(Buffer *buffer) = 0; + + template <class Type> + inline void + undo_info_update(Type *obj) + { + undo.push(new UndoTokenInfoUpdate<Type>(this, obj)); + } + /* NULL means to redraw the current cmdline if necessary */ virtual void cmdline_update(const gchar *cmdline = NULL) = 0; @@ -60,9 +92,14 @@ public: /* main entry point */ virtual void event_loop(void) = 0; + /* + * Interfacing to the external SciTECO world + * See main.cpp + */ protected: - /* see main.cpp */ void stdio_vmsg(MessageType type, const gchar *fmt, va_list ap); +public: + void process_notify(SCNotification *notify); }; #ifdef INTERFACE_GTK @@ -43,6 +43,34 @@ Interface::stdio_vmsg(MessageType type, const gchar *fmt, va_list ap) } } +void +Interface::process_notify(SCNotification *notify) +{ + switch (notify->nmhdr.code) { +#ifdef DEBUG + case SCN_SAVEPOINTREACHED: + g_printf("SCINTILLA SAVEPOINT REACHED\n"); + break; +#endif + case SCN_SAVEPOINTLEFT: +#ifdef DEBUG + g_printf("SCINTILLA SAVEPOINT LEFT\n"); +#endif + + if (!ring.current || ring.current->dirty) + break; + + undo.push_msg(SCI_SETSAVEPOINT); + undo_info_update(ring.current); + undo.push_var(ring.current->dirty); + ring.current->dirty = true; + info_update(ring.current); + break; + default: + break; + } +} + static inline void process_options(int &argc, char **&argv) { @@ -55,7 +83,7 @@ process_options(int &argc, char **&argv) GOptionContext *options; GOptionGroup *interface_group = interface.get_options(); - options = g_option_context_new("- Advanced interactive TECO"); + options = g_option_context_new("- " PACKAGE_STRING); g_option_context_add_main_entries(options, option_entries, NULL); if (interface_group) diff --git a/qbuffers.cpp b/qbuffers.cpp index 762566e..51bf65f 100644 --- a/qbuffers.cpp +++ b/qbuffers.cpp @@ -173,6 +173,9 @@ Buffer::load(const gchar *filename) interface.ssm(SCI_GOTOPOS, 0); interface.ssm(SCI_SETSAVEPOINT); + interface.undo_info_update(this); + undo.push_var(dirty); + dirty = false; set_filename(filename); @@ -231,7 +234,9 @@ Ring::edit(const gchar *filename) current_save_dot(); + qregisters.current = NULL; if (buffer) { + current = buffer; buffer->edit(); } else { new_in_ring = true; @@ -239,6 +244,8 @@ Ring::edit(const gchar *filename) buffer = new Buffer(); LIST_INSERT_HEAD(&head, buffer, buffers); + current = buffer; + if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { buffer->load(filename); @@ -258,9 +265,6 @@ Ring::edit(const gchar *filename) } } - qregisters.current = NULL; - current = buffer; - return new_in_ring; } @@ -377,6 +381,11 @@ Ring::save(const gchar *filename) if (!g_file_set_contents(filename, buffer, size, NULL)) return false; + interface.ssm(SCI_SETSAVEPOINT); + interface.undo_info_update(current); + undo.push_var(current->dirty); + current->dirty = false; + /* * FIXME: necessary also if the filename was not specified but the file * is (was) new, in order to canonicalize the filename. @@ -71,10 +71,12 @@ public: { interface.ssm(SCI_SETDOCPOINTER, 0, (sptr_t)get_document()); interface.ssm(SCI_GOTOPOS, dot); + interface.info_update(this); } inline void undo_edit(void) { + interface.undo_info_update(this); undo.push_msg(SCI_GOTOPOS, dot); undo.push_msg(SCI_SETDOCPOINTER, 0, (sptr_t)get_document()); } @@ -157,12 +159,15 @@ public: gint savepoint_id; + /* set by Interfaces using Scintilla notifications */ + bool dirty; + private: typedef void document; document *doc; public: - Buffer() : filename(NULL), dot(0), savepoint_id(0) + Buffer() : filename(NULL), dot(0), savepoint_id(0), dirty(false) { doc = (document *)interface.ssm(SCI_CREATEDOCUMENT); } @@ -184,6 +189,7 @@ public: gchar *resolved = get_absolute_path(filename); g_free(Buffer::filename); Buffer::filename = resolved; + interface.info_update(this); } inline void @@ -191,10 +197,12 @@ public: { interface.ssm(SCI_SETDOCPOINTER, 0, (sptr_t)doc); interface.ssm(SCI_GOTOPOS, dot); + interface.info_update(this); } inline void undo_edit(void) { + interface.undo_info_update(this); undo.push_msg(SCI_GOTOPOS, dot); undo.push_msg(SCI_SETDOCPOINTER, 0, (sptr_t)doc); } |