From 8cd29b9b30400e4275f1a65744cd0b3c2669cc98 Mon Sep 17 00:00:00 2001 From: Robin Haberkorn Date: Tue, 6 Nov 2012 20:42:40 +0100 Subject: initial commit of SciTECO based on THECO --- .gitignore | 4 ++ Makefile | 17 +++++++ cmdline.cpp | 103 +++++++++++++++++++++++++++++++++++++++ main.cpp | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ parser.cpp | 95 ++++++++++++++++++++++++++++++++++++ parser.h | 45 +++++++++++++++++ sciteco.h | 31 ++++++++++++ undo.cpp | 57 ++++++++++++++++++++++ undo.h | 82 +++++++++++++++++++++++++++++++ 9 files changed, 593 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 cmdline.cpp create mode 100644 main.cpp create mode 100644 parser.cpp create mode 100644 parser.h create mode 100644 sciteco.h create mode 100644 undo.cpp create mode 100644 undo.h 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 + +#include +#include + +#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, ""); + break; + case '\n': + rp = g_stpcpy(rp, ""); + break; + case '\t': + rp = g_stpcpy(rp, ""); + 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 + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#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 +#include + +#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(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 +#include + +#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 + +#include +#include + +#include + +#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 +#include + +#include + +#include + +#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 +void +UndoStack::push_var(Type &variable, Type value) +{ + UndoToken *token = new UndoTokenVariable(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 + +#include + +#include + +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 +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 + void + push_var(Type &variable, Type value) + { + UndoToken *token = new UndoTokenVariable(variable, value); + SLIST_INSERT_HEAD(&head, token, tokens); + } + + template + inline void + push_var(Type &variable) + { + push_var(variable, variable); + } + + void pop(gint pos); +} undo; + +#endif \ No newline at end of file -- cgit v1.2.3