aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore4
-rw-r--r--Makefile17
-rw-r--r--cmdline.cpp103
-rw-r--r--main.cpp159
-rw-r--r--parser.cpp95
-rw-r--r--parser.h45
-rw-r--r--sciteco.h31
-rw-r--r--undo.cpp57
-rw-r--r--undo.h82
9 files changed, 593 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..da4c764
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*.o
+sciteco
+
+*~
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..673debf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+GTK_CFLAGS:=$(shell pkg-config --cflags gtk+-2.0)
+GTK_LDFLAGS:=$(shell pkg-config --libs gtk+-2.0)
+
+SCI_CFLAGS:=-I../scintilla/include -DGTK -DSCI_LEXER
+SCI_LDFLAGS:=../scintilla/bin/scintilla.a
+
+CPPFLAGS:=
+CXXFLAGS:=-Wall -g -O0 $(GTK_CFLAGS) $(SCI_CFLAGS)
+LDFLAGS:=$(GTK_LDFLAGS) $(SCI_LDFLAGS)
+
+all : sciteco
+
+sciteco : main.o cmdline.o parser.o undo.o
+ $(CXX) -o $@ $^ $(LDFLAGS)
+
+clean:
+ $(RM) sciteco *.o
diff --git a/cmdline.cpp b/cmdline.cpp
new file mode 100644
index 0000000..2135f9a
--- /dev/null
+++ b/cmdline.cpp
@@ -0,0 +1,103 @@
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "sciteco.h"
+
+static gchar *macro_echo(const gchar *macro, const gchar *prefix = "");
+
+gchar *cmdline = NULL;
+
+void
+cmdline_keypress(gchar key)
+{
+ gchar insert[255] = "";
+ gint old_cmdline_len = 0;
+ gchar *echo;
+
+ /*
+ * Process immediate editing commands
+ */
+ switch (key) {
+ case '\b':
+ if (!cmdline || !*cmdline)
+ break;
+ old_cmdline_len = strlen(cmdline);
+
+ undo.pop(old_cmdline_len);
+ cmdline[old_cmdline_len-1] = '\0';
+ macro_pc--;
+ break;
+ default:
+ insert[0] = key;
+ insert[1] = '\0';
+ }
+
+ /*
+ * Parse/execute characters
+ */
+ if (cmdline) {
+ old_cmdline_len = strlen(cmdline);
+ cmdline = (gchar *)g_realloc(cmdline, old_cmdline_len +
+ strlen(insert) + 1);
+ } else {
+ cmdline = (gchar *)g_malloc(2);
+ *cmdline = '\0';
+ }
+
+ for (gchar *p = insert; *p; p++) {
+ strcat(cmdline, (gchar[]){key, '\0'});
+
+ if (!macro_execute(cmdline)) {
+ cmdline[old_cmdline_len] = '\0';
+ break;
+ }
+ }
+
+ /*
+ * Echo command line
+ */
+ echo = macro_echo(cmdline, "*");
+ cmdline_display(echo);
+ g_free(echo);
+}
+
+static gchar *
+macro_echo(const gchar *macro, const gchar *prefix)
+{
+ gchar *result, *rp;
+
+ if (!macro)
+ return g_strdup(prefix);
+
+ result = (gchar *)g_malloc(strlen(prefix) + strlen(macro)*5 + 1);
+ rp = g_stpcpy(result, prefix);
+
+ for (const gchar *p = macro; *p; p++) {
+ switch (*p) {
+ case '\x1B':
+ *rp++ = '$';
+ break;
+ case '\r':
+ rp = g_stpcpy(rp, "<CR>");
+ break;
+ case '\n':
+ rp = g_stpcpy(rp, "<LF>");
+ break;
+ case '\t':
+ rp = g_stpcpy(rp, "<TAB>");
+ break;
+ default:
+ if (IS_CTL(*p)) {
+ *rp++ = '^';
+ *rp++ = CTL_ECHO(*p);
+ } else {
+ *rp++ = *p;
+ }
+ }
+ }
+ *rp = '\0';
+
+ return result;
+}
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..452b9a8
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,159 @@
+#include <stdarg.h>
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkkeysyms.h>
+
+#include <gtk/gtk.h>
+
+#include <Scintilla.h>
+#include <SciLexer.h>
+#include <ScintillaWidget.h>
+
+#include "sciteco.h"
+
+static GtkWidget *editor_widget;
+static GtkWidget *cmdline_widget;
+static GtkWidget *info_widget, *message_widget;
+
+void
+cmdline_display(const gchar *cmdline_str)
+{
+ gtk_entry_set_text(GTK_ENTRY(cmdline_widget), cmdline_str);
+ gtk_editable_set_position(GTK_EDITABLE(cmdline_widget), -1);
+}
+
+void
+message_display(GtkMessageType type, const gchar *fmt, ...)
+{
+ va_list ap;
+ gchar buf[255];
+
+ gtk_info_bar_set_message_type(GTK_INFO_BAR(info_widget), type);
+
+ va_start(ap, fmt);
+ g_vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ gtk_label_set_text(GTK_LABEL(message_widget), buf);
+}
+
+void
+editor_msg(unsigned int iMessage, uptr_t wParam, sptr_t lParam)
+{
+ scintilla_send_message(SCINTILLA(editor_widget),
+ iMessage, wParam, lParam);
+}
+
+static gboolean
+cmdline_key_pressed(GtkWidget *widget, GdkEventKey *event,
+ gpointer user_data __attribute__((unused)))
+{
+ gchar key = '\0';
+
+ g_printf("KEY \"%s\" (%d) SHIFT=%d CNTRL=%d\n",
+ event->string, *event->string,
+ event->state & GDK_SHIFT_MASK, event->state & GDK_CONTROL_MASK);
+
+ switch (event->keyval) {
+ case GDK_BackSpace:
+ key = '\b';
+ break;
+ case GDK_Tab:
+ key = '\t';
+ break;
+ default:
+ key = *event->string;
+ }
+
+ if (key)
+ cmdline_keypress(key);
+
+ return TRUE;
+}
+
+static gboolean
+exit_app(GtkWidget *w __attribute__((unused)),
+ GdkEventAny *e __attribute__((unused)),
+ gpointer p __attribute__((unused)))
+{
+ gtk_main_quit();
+ return TRUE;
+}
+
+static void
+widget_set_font(GtkWidget *widget, const gchar *font_name)
+{
+ PangoFontDescription *font_desc;
+
+ font_desc = pango_font_description_from_string(font_name);
+ gtk_widget_modify_font(widget, font_desc);
+ pango_font_description_free(font_desc);
+}
+
+int
+main(int argc, char **argv)
+{
+ GtkWidget *window, *vbox;
+ GtkWidget *info_content;
+
+ gtk_init(&argc, &argv);
+
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window), "SciTECO");
+ g_signal_connect(G_OBJECT(window), "delete-event",
+ G_CALLBACK(exit_app), NULL);
+
+ vbox = gtk_vbox_new(FALSE, 0);
+
+ editor_widget = scintilla_new();
+ scintilla_set_id(SCINTILLA(editor_widget), 0);
+ gtk_widget_set_usize(editor_widget, 500, 300);
+ gtk_widget_set_can_focus(editor_widget, FALSE);
+ gtk_box_pack_start(GTK_BOX(vbox), editor_widget, TRUE, TRUE, 0);
+
+ info_widget = gtk_info_bar_new();
+ info_content = gtk_info_bar_get_content_area(GTK_INFO_BAR(info_widget));
+ message_widget = gtk_label_new("");
+ gtk_misc_set_alignment(GTK_MISC(message_widget), 0., 0.);
+ gtk_container_add(GTK_CONTAINER(info_content), message_widget);
+ gtk_box_pack_start(GTK_BOX(vbox), info_widget, FALSE, FALSE, 0);
+
+ cmdline_widget = gtk_entry_new();
+ gtk_entry_set_has_frame(GTK_ENTRY(cmdline_widget), FALSE);
+ gtk_editable_set_editable(GTK_EDITABLE(cmdline_widget), FALSE);
+ widget_set_font(cmdline_widget, "Courier");
+ g_signal_connect(G_OBJECT(cmdline_widget), "key-press-event",
+ G_CALLBACK(cmdline_key_pressed), NULL);
+ gtk_box_pack_start(GTK_BOX(vbox), cmdline_widget, FALSE, FALSE, 0);
+
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+
+ editor_msg(SCI_SETFOCUS, 1);
+ editor_msg(SCI_SETCARETSTYLE, 2);
+ editor_msg(SCI_STYLESETFONT, STYLE_DEFAULT, (sptr_t)"Courier");
+ editor_msg(SCI_STYLECLEARALL);
+ editor_msg(SCI_SETLEXER, SCLEX_CPP);
+ editor_msg(SCI_SETKEYWORDS, 0, (sptr_t)"int char");
+ editor_msg(SCI_STYLESETFORE, SCE_C_COMMENT, 0x008000);
+ editor_msg(SCI_STYLESETFORE, SCE_C_COMMENTLINE, 0x008000);
+ editor_msg(SCI_STYLESETFORE, SCE_C_NUMBER, 0x808000);
+ editor_msg(SCI_STYLESETFORE, SCE_C_WORD, 0x800000);
+ editor_msg(SCI_STYLESETFORE, SCE_C_STRING, 0x800080);
+ editor_msg(SCI_STYLESETBOLD, SCE_C_OPERATOR, 1);
+ editor_msg(SCI_INSERTTEXT, 0, (sptr_t)
+ "int main(int argc, char **argv) {\n"
+ " // Start up the gnome\n"
+ " gnome_init(\"stest\", \"1.0\", argc, argv);\n}"
+ );
+
+ cmdline_display("*");
+ gtk_widget_grab_focus(cmdline_widget);
+
+ gtk_widget_show_all(window);
+ gtk_main();
+
+ return 0;
+}
diff --git a/parser.cpp b/parser.cpp
new file mode 100644
index 0000000..0b218ab
--- /dev/null
+++ b/parser.cpp
@@ -0,0 +1,95 @@
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "sciteco.h"
+#include "parser.h"
+
+gint macro_pc = 0;
+
+static struct {
+ StateStart start;
+} states;
+
+static State *current_state = &states.start;
+
+gboolean
+macro_execute(const gchar *macro)
+{
+ while (macro[macro_pc]) {
+ if (!State::input(macro[macro_pc])) {
+ message_display(GTK_MESSAGE_ERROR,
+ "Syntax error \"%c\"",
+ macro[macro_pc]);
+ return FALSE;
+ }
+
+ macro_pc++;
+ }
+
+ return TRUE;
+}
+
+State::State()
+{
+ for (int i = 0; i < MAX_TRANSITIONS; i++)
+ transitions[i] = NULL;
+}
+
+gboolean
+State::input(gchar chr)
+{
+ State *state = current_state;
+
+ for (;;) {
+ State *next = state->get_next_state(chr);
+
+ if (!next)
+ /* Syntax error */
+ return FALSE;
+
+ if (next == state)
+ break;
+
+ state = next;
+ chr = '\0';
+ }
+
+ if (state != current_state) {
+ undo.push_var<State *>(current_state);
+ current_state = state;
+ }
+
+ return TRUE;
+}
+
+StateStart::StateStart() : State()
+{
+ transitions['\0'] = this;
+ transitions[' '] = this;
+ transitions['\r'] = this;
+ transitions['\n'] = this;
+ transitions['\v'] = this;
+}
+
+State *
+StateStart::custom(gchar chr)
+{
+#if 0
+ if (IS_CTL(chr))
+ return states.ctl.get_next_state(CTL_ECHO(chr));
+#endif
+
+ switch (g_ascii_toupper(chr)) {
+ /*
+ * commands
+ */
+ case 'C':
+ editor_msg(SCI_CHARRIGHT);
+ undo.push_msg(SCI_CHARLEFT);
+ break;
+ default:
+ return NULL;
+ }
+
+ return this;
+}
diff --git a/parser.h b/parser.h
new file mode 100644
index 0000000..11a6d56
--- /dev/null
+++ b/parser.h
@@ -0,0 +1,45 @@
+#ifndef __PARSER_H
+#define __PARSER_H
+
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "sciteco.h"
+
+class State {
+protected:
+ /* static transitions */
+ State *transitions[MAX_TRANSITIONS];
+
+public:
+ State();
+
+ inline State *&
+ operator [](int i)
+ {
+ return transitions[i];
+ }
+
+ static gboolean input(gchar chr);
+
+ inline State *
+ get_next_state(gchar chr)
+ {
+ return transitions[(int)g_ascii_toupper(chr)] ? : custom(chr);
+ }
+
+ virtual State *
+ custom(gchar chr)
+ {
+ return NULL;
+ }
+};
+
+class StateStart : public State {
+public:
+ StateStart();
+
+ State *custom(gchar chr);
+};
+
+#endif
diff --git a/sciteco.h b/sciteco.h
new file mode 100644
index 0000000..3d5b360
--- /dev/null
+++ b/sciteco.h
@@ -0,0 +1,31 @@
+#ifndef __SCITECO_H
+#define __SCITECO_H
+
+#include <stdarg.h>
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include <Scintilla.h>
+
+#include "undo.h"
+
+extern gchar *cmdline;
+extern gint macro_pc;
+
+void message_display(GtkMessageType type, const gchar *fmt, ...);
+
+void cmdline_keypress(gchar key);
+void cmdline_display(const gchar *cmdline);
+
+void editor_msg(unsigned int iMessage, uptr_t wParam = 0, sptr_t lParam = 0);
+
+gboolean macro_execute(const gchar *macro);
+
+#define IS_CTL(C) ((C) < ' ')
+#define CTL_ECHO(C) ((C) | 0x40)
+
+/* TECO uses only lower 7 bits for commands */
+#define MAX_TRANSITIONS 127
+
+#endif \ No newline at end of file
diff --git a/undo.cpp b/undo.cpp
new file mode 100644
index 0000000..f5fc81d
--- /dev/null
+++ b/undo.cpp
@@ -0,0 +1,57 @@
+#include <string.h>
+#include <bsd/sys/queue.h>
+
+#include <glib.h>
+
+#include <Scintilla.h>
+
+#include "sciteco.h"
+#include "undo.h"
+
+UndoStack undo;
+
+UndoToken::UndoToken() : pos(strlen(cmdline)) {}
+
+void
+UndoTokenMessage::run(void)
+{
+ editor_msg(iMessage, wParam, lParam);
+}
+
+void
+UndoStack::push_msg(unsigned int iMessage, uptr_t wParam, sptr_t lParam)
+{
+ UndoToken *token = new UndoTokenMessage(iMessage, wParam, lParam);
+ SLIST_INSERT_HEAD(&head, token, tokens);
+}
+
+#if 0
+template <typename Type>
+void
+UndoStack::push_var(Type &variable, Type value)
+{
+ UndoToken *token = new UndoTokenVariable<Type>(variable, value);
+ SLIST_INSERT_HEAD(&head, token, tokens);
+}
+#endif
+
+void
+UndoStack::pop(gint pos)
+{
+ while (!SLIST_EMPTY(&head) && SLIST_FIRST(&head)->pos >= pos) {
+ UndoToken *top = SLIST_FIRST(&head);
+
+ top->run();
+
+ SLIST_REMOVE_HEAD(&head, tokens);
+ delete top;
+ }
+}
+
+UndoStack::~UndoStack()
+{
+ UndoToken *token, *next;
+
+ SLIST_FOREACH_SAFE(token, &head, tokens, next)
+ delete token;
+}
diff --git a/undo.h b/undo.h
new file mode 100644
index 0000000..29ec70f
--- /dev/null
+++ b/undo.h
@@ -0,0 +1,82 @@
+#ifndef __UNDO_H
+#define __UNDO_H
+
+#include <bsd/sys/queue.h>
+
+#include <glib.h>
+
+#include <Scintilla.h>
+
+class UndoToken {
+public:
+ SLIST_ENTRY(UndoToken) tokens;
+
+ gint pos;
+
+ UndoToken();
+
+ virtual void run() = 0;
+};
+
+class UndoTokenMessage : public UndoToken {
+ unsigned int iMessage;
+ uptr_t wParam;
+ sptr_t lParam;
+
+public:
+ UndoTokenMessage(unsigned int _iMessage,
+ uptr_t _wParam = 0, sptr_t _lParam = 0)
+ : UndoToken(), iMessage(_iMessage),
+ wParam(_wParam), lParam(_lParam) {}
+
+ void run(void);
+};
+
+template <typename Type>
+class UndoTokenVariable : public UndoToken {
+ Type *ptr;
+ Type value;
+
+public:
+ UndoTokenVariable(Type &variable, Type _value)
+ : UndoToken(), ptr(&variable), value(_value) {}
+
+ void
+ run(void)
+ {
+ *ptr = value;
+ }
+};
+
+extern class UndoStack {
+ SLIST_HEAD(undo_head, UndoToken) head;
+
+public:
+ UndoStack()
+ {
+ SLIST_INIT(&head);
+ }
+ ~UndoStack();
+
+ void push_msg(unsigned int iMessage,
+ uptr_t wParam = 0, sptr_t lParam = 0);
+
+ template <typename Type>
+ void
+ push_var(Type &variable, Type value)
+ {
+ UndoToken *token = new UndoTokenVariable<Type>(variable, value);
+ SLIST_INSERT_HEAD(&head, token, tokens);
+ }
+
+ template <typename Type>
+ inline void
+ push_var(Type &variable)
+ {
+ push_var<Type>(variable, variable);
+ }
+
+ void pop(gint pos);
+} undo;
+
+#endif \ No newline at end of file