diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | Makefile | 17 | ||||
-rw-r--r-- | cmdline.cpp | 103 | ||||
-rw-r--r-- | main.cpp | 159 | ||||
-rw-r--r-- | parser.cpp | 95 | ||||
-rw-r--r-- | parser.h | 45 | ||||
-rw-r--r-- | sciteco.h | 31 | ||||
-rw-r--r-- | undo.cpp | 57 | ||||
-rw-r--r-- | undo.h | 82 |
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; +} @@ -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 |