aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2012-11-16 14:42:47 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2012-11-16 14:42:47 +0100
commitf6ff327f0b7b50b74328e448ce862f7212dcae23 (patch)
tree4a773dcb9120d6a7fb96fce92422667e8702dbdf
parent0a8f940ffe1aaf77ba12ccc02d4e382be2118151 (diff)
downloadsciteco-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.cpp49
-rw-r--r--interface-gtk.h6
-rw-r--r--interface-ncurses.cpp158
-rw-r--r--interface-ncurses.h19
-rw-r--r--interface.h39
-rw-r--r--main.cpp30
-rw-r--r--qbuffers.cpp15
-rw-r--r--qbuffers.h10
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
diff --git a/main.cpp b/main.cpp
index 300d2f8..a6919c5 100644
--- a/main.cpp
+++ b/main.cpp
@@ -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.
diff --git a/qbuffers.h b/qbuffers.h
index 61aab64..8ceaffa 100644
--- a/qbuffers.h
+++ b/qbuffers.h
@@ -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);
}