aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar mitchell <70453897+667e-11@users.noreply.github.com>2007-08-06 04:58:17 -0400
committerGravatar mitchell <70453897+667e-11@users.noreply.github.com>2007-08-06 04:58:17 -0400
commit1aa6c6702415079a6546f1325789c3a86f733f34 (patch)
treef8cf70e3d7b1c68917a666e299cf7a919be1e885
Initial import of core C files.
-rw-r--r--src/Makefile20
-rw-r--r--src/find_replace.c122
-rw-r--r--src/lua_interface.c951
-rw-r--r--src/pm.c183
-rw-r--r--src/properties.h86
-rw-r--r--src/textadept.c261
-rw-r--r--src/textadept.h99
7 files changed, 1722 insertions, 0 deletions
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 00000000..0b4b40fc
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,20 @@
+# Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+.SUFFIXES: .c .o .h .a
+
+INCLUDEDIRS=-Iscintilla-st/include
+ifdef DEBUG
+CXXFLAGS=-DDEBUG -g -DGTK -DSCI_LEXER -W -Wall
+else
+CXXFLAGS=-DNDEBUG -0s -DGTK -DSCI_LEXER -W -Wall
+endif
+LEXEROBJS=$(wildcard scintilla-st/gtk/Lex*.o)
+
+all: textadept
+
+.c.o:
+ g++ `pkg-config --cflags gtk+-2.0` $(INCLUDEDIRS) $(CXXFLAGS) -c $< -o $@
+textadept: textadept.o lua_interface.o pm.o find_replace.o $(LEXEROBJS) scintilla-st/bin/scintilla.a -llua
+ g++ `pkg-config --libs gtk+-2.0 gthread-2.0` -lstdc++ -DGTK $^ -o $@
+clean:
+ rm -rf textadept *.o
diff --git a/src/find_replace.c b/src/find_replace.c
new file mode 100644
index 00000000..4d31e178
--- /dev/null
+++ b/src/find_replace.c
@@ -0,0 +1,122 @@
+// Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+#include "textadept.h"
+
+#define attach(w, x1, x2, y1, y2, xo, yo, xp, yp) \
+ gtk_table_attach(GTK_TABLE(findbox), w, x1, x2, y1, y2, xo, yo, xp, yp)
+#define find_text gtk_entry_get_text(GTK_ENTRY(find_entry))
+#define repl_text gtk_entry_get_text(GTK_ENTRY(replace_entry))
+#define toggled(w) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))
+
+GtkWidget *findbox, *find_entry, *replace_entry;
+GtkWidget *fnext_button, *fprev_button, *r_button, *ra_button;
+GtkWidget *match_case_opt, *whole_word_opt, /**incremental_opt,*/ *lua_opt;
+GtkAttachOptions
+ normal = static_cast<GtkAttachOptions>(GTK_SHRINK | GTK_FILL),
+ expand = static_cast<GtkAttachOptions>(GTK_EXPAND | GTK_FILL);
+
+static bool
+ fe_keypress(GtkWidget*, GdkEventKey *event, gpointer),
+ re_keypress(GtkWidget*, GdkEventKey *event, gpointer);
+
+static void button_clicked(GtkWidget *button, gpointer);
+
+GtkWidget* find_create_ui() {
+ findbox = gtk_table_new(2, 6, false);
+
+ GtkWidget *flabel = gtk_label_new_with_mnemonic("_Find:");
+ GtkWidget *rlabel = gtk_label_new_with_mnemonic("R_eplace:");
+ find_entry = gtk_entry_new();
+ replace_entry = gtk_entry_new();
+ fnext_button = gtk_button_new_with_mnemonic("Find _Next");
+ fprev_button = gtk_button_new_with_mnemonic("Find _Prev");
+ r_button = gtk_button_new_with_mnemonic("_Replace");
+ ra_button = gtk_button_new_with_mnemonic("Replace _All");
+ match_case_opt = gtk_check_button_new_with_mnemonic("_Match case");
+ whole_word_opt = gtk_check_button_new_with_mnemonic("_Whole word");
+ //incremental_opt = gtk_check_button_new_with_mnemonic("_Incremental");
+ lua_opt = gtk_check_button_new_with_mnemonic("_Lua pattern");
+
+ gtk_label_set_mnemonic_widget(GTK_LABEL(flabel), find_entry);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(rlabel), replace_entry);
+ //gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lua_opt), true);
+
+ attach(find_entry, 1, 2, 0, 1, expand, normal, 5, 0);
+ attach(replace_entry, 1, 2, 1, 2, expand, normal, 5, 0);
+ attach(flabel, 0, 1, 0, 1, normal, normal, 5, 0);
+ attach(rlabel, 0, 1, 1, 2, normal, normal, 5, 0);
+ attach(fnext_button, 2, 3, 0, 1, normal, normal, 0, 0);
+ attach(fprev_button, 3, 4, 0, 1, normal, normal, 0, 0);
+ attach(r_button, 2, 3, 1, 2, normal, normal, 0, 0);
+ attach(ra_button, 3, 4, 1, 2, normal, normal, 0, 0);
+ attach(match_case_opt, 4, 5, 0, 1, normal, normal, 5, 0);
+ attach(whole_word_opt, 4, 5, 1, 2, normal, normal, 5, 0);
+ //attach(incremental_opt, 5, 6, 0, 1, normal, normal, 5, 0);
+ attach(lua_opt, 5, 6, 0, 1, normal, normal, 5, 0);
+
+ g_signal_connect(G_OBJECT(find_entry), "key_press_event",
+ G_CALLBACK(fe_keypress), 0);
+ g_signal_connect(G_OBJECT(replace_entry), "key_press_event",
+ G_CALLBACK(re_keypress), 0);
+ g_signal_connect(G_OBJECT(fnext_button), "clicked",
+ G_CALLBACK(button_clicked), 0);
+ g_signal_connect(G_OBJECT(fprev_button), "clicked",
+ G_CALLBACK(button_clicked), 0);
+ g_signal_connect(G_OBJECT(r_button), "clicked",
+ G_CALLBACK(button_clicked), 0);
+ g_signal_connect(G_OBJECT(ra_button), "clicked",
+ G_CALLBACK(button_clicked), 0);
+
+ GTK_WIDGET_UNSET_FLAGS(fnext_button, GTK_CAN_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS(fprev_button, GTK_CAN_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS(r_button, GTK_CAN_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS(ra_button, GTK_CAN_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS(match_case_opt, GTK_CAN_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS(whole_word_opt, GTK_CAN_FOCUS);
+ //GTK_WIDGET_UNSET_FLAGS(incremental_opt, GTK_CAN_FOCUS);
+ GTK_WIDGET_UNSET_FLAGS(lua_opt, GTK_CAN_FOCUS);
+
+ return findbox;
+}
+
+void find_toggle_focus() {
+ if (!GTK_WIDGET_HAS_FOCUS(focused_editor)) {
+ gtk_widget_grab_focus(focused_editor);
+ gtk_widget_hide(findbox);
+ } else {
+ gtk_widget_show(findbox);
+ gtk_widget_grab_focus(find_entry);
+ }
+}
+
+static int get_flags() {
+ int flags = 0;
+ if (toggled(match_case_opt)) flags |= SCFIND_MATCHCASE; // 2
+ if (toggled(whole_word_opt)) flags |= SCFIND_WHOLEWORD; // 4
+ if (toggled(lua_opt)) flags |= 8;
+ return flags;
+}
+
+static bool fe_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
+ // TODO: if incremental, call l_find()
+ if (event->keyval == 0xff0d) {
+ l_find(find_text, get_flags(), true);
+ return true;
+ } else return false;
+}
+
+static bool re_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
+ if (event->keyval == 0xff0d) {
+ l_find(find_text, get_flags(), true);
+ return true;
+ } else return false;
+}
+
+static void button_clicked(GtkWidget *button, gpointer) {
+ if (button == ra_button)
+ l_find_replace_all(find_text, repl_text, get_flags());
+ else if (button == r_button) {
+ l_find_replace(repl_text);
+ l_find(find_text, get_flags(), true);
+ } else l_find(find_text, get_flags(), button == fnext_button);
+}
diff --git a/src/lua_interface.c b/src/lua_interface.c
new file mode 100644
index 00000000..62678c96
--- /dev/null
+++ b/src/lua_interface.c
@@ -0,0 +1,951 @@
+// Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+#include "textadept.h"
+
+#define LS lua_State
+#define LF static int
+#define streq(s1, s2) strcmp(s1, s2) == 0
+#define l_insert(l, i) lua_insert(l, i < 0 ? lua_gettop(l) + i : i)
+#define l_append(l, i) lua_rawseti(l, i, lua_objlen(l, i) + 1)
+#define l_cfunc(l, f, k) { lua_pushcfunction(l, f); lua_setfield(l, -2, k); }
+#define l_todocpointer(l, i) static_cast<sptr_t>(lua_tonumber(l, i))
+#define l_togtkwidget(l, i) reinterpret_cast<GtkWidget*>(lua_touserdata(l, i))
+#define l_mt(l, k, i, ni) { \
+ if (luaL_newmetatable(l, k)) { \
+ l_cfunc(l, i, "__index"); \
+ l_cfunc(l, ni, "__newindex"); \
+ } lua_setmetatable(l, -2); }
+
+LS *lua;
+bool closing = false;
+
+static int // parameter/return types
+ tVOID = 0, tINT = 1, tLENGTH = 2, tPOSITION = 3, tCOLOUR = 4,
+ tBOOL = 5, tKEYMOD = 6, tSTRING = 7, tSTRINGRESULT = 8;
+
+LF l_buffer_mt_index(LS *lua), l_buffer_mt_newindex(LS *lua),
+ l_bufferp_mt_index(LS *lua), l_bufferp_mt_newindex(LS *lua),
+ l_view_mt_index(LS *lua), l_view_mt_newindex(LS *lua),
+ l_ta_mt_index(LS *lua), l_ta_mt_newindex(LS *lua),
+ l_pm_mt_index(LS *lua), l_pm_mt_newindex(LS *lua),
+ l_find_mt_index(LS *lua), l_find_mt_newindex(LS *lua);
+
+LF l_cf_ta_buffer_new(LS *lua),
+ l_cf_buffer_delete(LS *lua),
+ l_cf_buffer_find(LS *lua),
+ l_cf_buffer_text_range(LS *lua),
+ l_cf_view_focus(LS *lua),
+ l_cf_view_split(LS *lua), l_cf_view_unsplit(LS *lua),
+ l_cf_ta_get_split_table(LS *lua),
+ l_cf_ta_focus_command(LS *lua),
+ l_cf_ta_goto_window(LS *lua),
+ l_cf_view_goto_buffer(LS *lua),
+ l_cf_pm_focus(LS *lua), l_cf_pm_clear(LS *lua), l_cf_pm_activate(LS *lua),
+ l_cf_find_focus(LS *lua);
+
+void l_init(int argc, char **argv) {
+ lua = lua_open();
+ luaL_openlibs(lua);
+ lua_newtable(lua);
+ for (int i = 0; i < argc; i++) {
+ lua_pushstring( lua, argv[i] ); lua_rawseti(lua, -2, i);
+ }
+ lua_setglobal(lua, "arg");
+
+ lua_newtable(lua);
+ lua_newtable(lua); lua_setfield(lua, -2, "buffers");
+ lua_newtable(lua); lua_setfield(lua, -2, "views");
+ lua_newtable(lua);
+ l_cfunc(lua, l_cf_pm_focus, "focus");
+ l_cfunc(lua, l_cf_pm_clear, "clear");
+ l_cfunc(lua, l_cf_pm_activate, "activate");
+ l_mt(lua, "_pm_mt", l_pm_mt_index, l_pm_mt_newindex);
+ lua_setfield(lua, -2, "pm");
+ lua_newtable(lua);
+ l_cfunc(lua, l_cf_find_focus, "focus");
+ l_mt(lua, "_find_mt", l_find_mt_index, l_find_mt_newindex);
+ lua_setfield(lua, -2, "find");
+ l_cfunc(lua, l_cf_ta_buffer_new, "new_buffer");
+ l_cfunc(lua, l_cf_ta_goto_window, "goto_view");
+ l_cfunc(lua, l_cf_ta_get_split_table, "get_split_table");
+ l_cfunc(lua, l_cf_ta_focus_command, "focus_command");
+ l_mt(lua, "_textadept_mt", l_ta_mt_index, l_ta_mt_newindex);
+ lua_setglobal(lua, "textadept");
+ lua_pushstring(lua, textadept_home); lua_setglobal(lua, "_HOME");
+
+ l_load_script("core/init.lua");
+}
+
+void l_load_script(const char *script_file) {
+ char *script = g_strconcat(textadept_home, "/", script_file, NULL);
+ if (luaL_dofile(lua, script) != 0) l_handle_error(lua);
+ g_free(script);
+}
+
+bool l_ta_get(LS *lua, const char *k) {
+ lua_getglobal(lua, "textadept");
+ lua_pushstring(lua, k); lua_rawget(lua, -2);
+ lua_remove(lua, -2); // textadept
+ return lua_istable(lua, -1);
+}
+
+// value is at stack top
+void l_ta_set(LS *lua, const char *k) {
+ lua_getglobal(lua, "textadept");
+ lua_pushstring(lua, k);
+ lua_pushvalue(lua, -3);
+ lua_rawset(lua, -3);
+ lua_pop(lua, 2); // value and textadept
+}
+
+/** Checks for a view and returns the GtkWidget associated with it. */
+static GtkWidget* l_checkview(LS *lua, int narg, const char *errstr=NULL) {
+ if (lua_type(lua, narg) == LUA_TTABLE) {
+ lua_pushstring(lua, "widget_pointer");
+ lua_rawget(lua, narg > 0 ? narg : narg - 1);
+ if (lua_type(lua, -1) != LUA_TLIGHTUSERDATA)
+ luaL_error(lua, errstr ? errstr : "View argument expected.");
+ } else luaL_error(lua, errstr ? errstr : "View argument expected.");
+ GtkWidget *editor = l_togtkwidget(lua, -1);
+ lua_pop(lua, 1); // widget_pointer
+ return editor;
+}
+
+void l_add_scintilla_window(GtkWidget *editor) {
+ if (!l_ta_get(lua, "views")) { lua_pop(lua, 1); return; }
+ lua_newtable(lua);
+ lua_pushlightuserdata(lua, const_cast<GtkWidget*>(editor));
+ lua_setfield(lua, -2, "widget_pointer");
+ l_cfunc(lua, l_cf_view_split, "split");
+ l_cfunc(lua, l_cf_view_unsplit, "unsplit");
+ l_cfunc(lua, l_cf_view_goto_buffer, "goto_buffer");
+ l_cfunc(lua, l_cf_view_focus, "focus");
+ l_mt(lua, "_view_mt", l_view_mt_index, l_view_mt_newindex);
+ l_append(lua, -2); // pops table
+ lua_pop(lua, 1); // views
+}
+
+void l_remove_scintilla_window(GtkWidget *editor) {
+ lua_newtable(lua);
+ if (l_ta_get(lua, "views")) {
+ lua_pushnil(lua);
+ while (lua_next(lua, -2))
+ editor != l_checkview(lua, -1) ? l_append(lua, -4) : lua_pop(lua, 1);
+ } lua_pop(lua, 1); // views
+ l_ta_set(lua, "views");
+}
+
+void l_goto_scintilla_window(GtkWidget *editor, int n, bool absolute) {
+ if (!l_ta_get(lua, "views")) { lua_pop(lua, 1); return; }
+ if (!absolute) {
+ unsigned int idx = 1;
+ lua_pushnil(lua);
+ while (lua_next(lua, -2))
+ if (editor == l_checkview(lua, -1)) {
+ idx = static_cast<int>(lua_tointeger(lua, -2));
+ lua_pop(lua, 2); // key and value
+ break;
+ } else lua_pop(lua, 1); // value
+ idx += n;
+ if (idx > lua_objlen(lua, -1))
+ idx = 1;
+ else if (idx < 1)
+ idx = lua_objlen(lua, -1);
+ lua_rawgeti(lua, -1, idx);
+ } else lua_rawgeti(lua, -1, n);
+ editor = l_checkview(lua, -1, "No view exists at that index.");
+ gtk_widget_grab_focus(editor);
+ if (!closing) l_handle_signal("view_switch");
+ lua_pop(lua, 2); // view table and views
+}
+
+void l_set_view_global(GtkWidget *editor) {
+ if (l_ta_get(lua, "views")) {
+ lua_pushnil(lua);
+ while (lua_next(lua, -2))
+ if (editor == l_checkview(lua, -1)) {
+ lua_setglobal(lua, "view"); // value (view table)
+ lua_pop(lua, 1); // key
+ break;
+ } else lua_pop(lua, 1); // value
+ } lua_pop(lua, 1); // views
+}
+
+/** Checks for a buffer and returns the doc_pointer associated with it. */
+static sptr_t l_checkdocpointer(LS *lua, int narg, const char *errstr=NULL) {
+ if (lua_type(lua, narg) == LUA_TTABLE) {
+ lua_pushstring(lua, "doc_pointer");
+ lua_rawget(lua, narg > 0 ? narg : narg - 1);
+ if (lua_type(lua, -1) != LUA_TNUMBER)
+ luaL_error(lua, errstr ? errstr : "Buffer argument expected.");
+ } else luaL_error(lua, errstr ? errstr : "Buffer argument expected.");
+ sptr_t doc = l_todocpointer(lua, -1);
+ lua_pop(lua, 1); // doc_pointer
+ return doc;
+}
+
+int l_add_scintilla_buffer(sptr_t doc) {
+ if (!l_ta_get(lua, "buffers")) { lua_pop(lua, 1); return 1; }
+ lua_newtable(lua);
+ lua_pushnumber(lua, doc); lua_setfield(lua, -2, "doc_pointer");
+ lua_pushboolean(lua, false); lua_setfield(lua, -2, "dirty");
+ l_cfunc(lua, l_cf_buffer_find, "find");
+ l_cfunc(lua, l_cf_buffer_text_range, "text_range");
+ l_cfunc(lua, l_cf_buffer_delete, "delete");
+ l_mt(lua, "_buffer_mt", l_buffer_mt_index, l_buffer_mt_newindex);
+ l_append(lua, -2); // pops table
+ int index = lua_objlen(lua, -1);
+ lua_pop(lua, 1); // buffers
+ return index;
+}
+
+void l_remove_scintilla_buffer(sptr_t doc) {
+ // Switch documents for all views that show the current document.
+ if (l_ta_get(lua, "views")) {
+ lua_pushnil(lua);
+ while (lua_next(lua, -2)) {
+ GtkWidget *editor = l_checkview(lua, -1);
+ sptr_t that_doc = SS(SCINTILLA(editor), SCI_GETDOCPOINTER);
+ if (that_doc == doc) l_goto_scintilla_buffer(editor, -1, false);
+ lua_pop(lua, 1); // value
+ }
+ } lua_pop(lua, 1); // views
+
+ lua_newtable(lua);
+ if (l_ta_get(lua, "buffers")) {
+ lua_pushnil(lua);
+ while (lua_next(lua, -2))
+ doc != l_checkdocpointer(lua, -1) ? l_append(lua, -4) : lua_pop(lua, 1);
+ } lua_pop(lua, 1); // buffers
+ l_ta_set(lua, "buffers");
+}
+
+unsigned int l_get_docpointer_index(sptr_t doc) {
+ if (!l_ta_get(lua, "buffers")) { lua_pop(lua, 1); return 1; }
+ unsigned int idx = 1;
+ lua_pushnil(lua);
+ while (lua_next(lua, -2))
+ if (doc == l_checkdocpointer(lua, -1)) {
+ idx = static_cast<int>(lua_tointeger(lua, -2));
+ lua_pop(lua, 2); // key and value
+ break;
+ } else lua_pop(lua, 1); // value
+ lua_pop(lua, 1); // buffers
+ return idx;
+}
+
+#define l_set_buffer_prop(k, v) \
+ { lua_pushstring(lua, k); lua_pushinteger(lua, v); lua_rawset(lua, -3); }
+#define l_get_buffer_prop(k, i) { lua_pushstring(lua, k); lua_rawget(lua, i); }
+
+void l_goto_scintilla_buffer(GtkWidget *editor, int n, bool absolute) {
+ if (!l_ta_get(lua, "buffers")) { lua_pop(lua, 1); return; }
+ ScintillaObject *sci = SCINTILLA(editor);
+ if (!absolute) {
+ sptr_t doc = SS(sci, SCI_GETDOCPOINTER);
+ unsigned int idx = l_get_docpointer_index(doc);
+ idx += n;
+ if (idx > lua_objlen(lua, -1))
+ idx = 1;
+ else if (idx < 1)
+ idx = lua_objlen(lua, -1);
+ lua_rawgeti(lua, -1, idx);
+ } else lua_rawgeti(lua, -1, n);
+ sptr_t doc = l_checkdocpointer(lua, -1, "No buffer exists at that index.");
+ // Save previous buffer's properties.
+ lua_getglobal(lua, "buffer");
+ l_set_buffer_prop("_anchor", SS(sci, SCI_GETANCHOR));
+ l_set_buffer_prop("_current_pos", SS(sci, SCI_GETCURRENTPOS));
+ l_set_buffer_prop("_first_visible_line",
+ SS(sci, SCI_DOCLINEFROMVISIBLE, SS(sci, SCI_GETFIRSTVISIBLELINE)));
+ lua_pop(lua, 1); // buffer
+ // Change the view.
+ SS(sci, SCI_SETDOCPOINTER, 0, doc);
+ l_set_buffer_global(sci);
+ // Restore this buffer's properties.
+ lua_getglobal(lua, "buffer");
+ l_get_buffer_prop("_first_visible_line", -2);
+ SS(sci, SCI_LINESCROLL, 0,
+ SS(sci, SCI_VISIBLEFROMDOCLINE, lua_tointeger(lua, -1)));
+ l_get_buffer_prop("_anchor", -3);
+ l_get_buffer_prop("_current_pos", -4);
+ SS(sci, SCI_SETSEL, lua_tointeger(lua, -2), lua_tointeger(lua, -1));
+ lua_pop(lua, 4); // _first_visible_line, _anchor, _current_pos, and buffer
+ if (!closing) l_handle_signal("buffer_switch");
+ lua_pop(lua, 2); // buffer table and buffers
+}
+
+void l_set_buffer_global(ScintillaObject *sci) {
+ sptr_t doc = SS(sci, SCI_GETDOCPOINTER);
+ if (l_ta_get(lua, "buffers")) {
+ lua_pushnil(lua);
+ while (lua_next(lua, -2))
+ if (doc == l_checkdocpointer(lua, -1)) {
+ lua_setglobal(lua, "buffer"); // value (buffer table)
+ lua_pop(lua, 1); // key
+ break;
+ } else lua_pop(lua, 1); // value
+ } lua_pop(lua, 1); // buffers
+}
+
+void l_close() {
+ closing = true;
+ while (unsplit_window(focused_editor));
+ if (l_ta_get(lua, "buffers")) {
+ lua_pushnil(lua);
+ while (lua_next(lua, -2)) {
+ sptr_t doc = l_checkdocpointer(lua, -1);
+ remove_scintilla_buffer(doc);
+ lua_pop(lua, 1); // value
+ }
+ } lua_pop(lua, 1); // buffers
+ gtk_widget_destroy(focused_editor);
+ lua_close(lua);
+}
+
+// Notification/signal handlers
+
+bool l_is_ta_table_function(const char *table, const char *function) {
+ if (l_ta_get(lua, table)) {
+ lua_getfield(lua, -1, function);
+ lua_remove(lua, -2); // table
+ if (lua_isfunction(lua, -1)) return true;
+ lua_pop(lua, 1); // non-function
+ } else lua_pop(lua, 1); // non-table
+ return false;
+}
+
+bool l_call_function(int nargs, int retn=0, bool keep_return=false) {
+ int ret = lua_pcall(lua, nargs, retn, 0);
+ if (ret == 0) {
+ bool result = retn > 0 ? lua_toboolean(lua, -1) == 1 : true;
+ if (retn > 0 && !keep_return) lua_pop(lua, retn); // retn
+ return result;
+ } else l_handle_error(lua);
+ return false;
+}
+
+// error message is at stack top
+void l_handle_error(LS *lua, const char *errmsg) {
+ if (focused_editor && l_is_ta_table_function("handlers", "error")) {
+ l_insert(lua, -1); // shift error message down
+ if (errmsg) lua_pushstring(lua, errmsg);
+ l_call_function(errmsg ? 2 : 1);
+ } else {
+ printf("Lua Error: %s\n", lua_tostring(lua, -1));
+ if (errmsg) printf("%s\n", errmsg);
+ }
+ lua_settop(lua, 0);
+}
+
+bool l_handle_signal(const char *s) {
+ return l_is_ta_table_function("handlers", s) ? l_call_function(0, 1) : true;
+}
+
+bool l_handle_keypress(int keyval, GdkEventKey *event) {
+ if (!l_is_ta_table_function("handlers", "keypress")) return false;
+ lua_pushinteger(lua, keyval);
+ lua_pushboolean(lua, (event->state & GDK_SHIFT_MASK) > 0 ? 1 : 0);
+ lua_pushboolean(lua, (event->state & GDK_CONTROL_MASK) > 0 ? 1 : 0);
+ lua_pushboolean(lua, (event->state & GDK_MOD1_MASK) > 0 ? 1 : 0);
+ return l_call_function(4, 1);
+}
+
+void l_handle_completion(const char *command) {
+ if (!l_is_ta_table_function("handlers",
+ command ? "show_completions" : "hide_completions")) return;
+ if (command) lua_pushstring(lua, command);
+ l_call_function(command ? 1 : 0);
+}
+
+#define l_scn_int(i, n) { lua_pushinteger(lua, i); lua_setfield(lua, -2, n); }
+#define l_scn_str(s, n) { lua_pushstring(lua, s); lua_setfield(lua, -2, n); }
+
+void l_handle_scnnotification(SCNotification *n) {
+ if (!l_is_ta_table_function("handlers", "notification")) return;
+ lua_newtable(lua);
+ l_scn_int(n->nmhdr.code, "code");
+ l_scn_int(n->position, "position");
+ l_scn_int(n->ch, "ch");
+ l_scn_int(n->modifiers, "modifiers");
+ l_scn_int(n->modificationType, "modification_type");
+ l_scn_str(n->text, "text");
+ l_scn_int(n->length, "length");
+ l_scn_int(n->linesAdded, "lines_added");
+ l_scn_int(n->message, "message");
+ if (n->nmhdr.code == SCN_MACRORECORD) {
+ l_scn_str(reinterpret_cast<char*>(n->wParam), "wParam");
+ l_scn_str(reinterpret_cast<char*>(n->lParam), "lParam");
+ } else {
+ l_scn_int(static_cast<int>(n->wParam), "wParam");
+ l_scn_int(static_cast<int>(n->lParam), "lParam");
+ }
+ l_scn_int(n->line, "line");
+ l_scn_int(n->foldLevelNow, "fold_level_now");
+ l_scn_int(n->foldLevelPrev, "fold_level_prev");
+ l_scn_int(n->margin, "margin");
+ l_scn_int(n->x, "x");
+ l_scn_int(n->y, "y");
+ l_call_function(1);
+}
+
+void l_ta_command(const char *command) {
+ int top = lua_gettop(lua);
+ if (luaL_dostring(lua, command) == 0) {
+ l_handle_signal("update_ui");
+ lua_settop(lua, top);
+ } else l_handle_error(lua, "Error executing command.");
+}
+
+// full_path is at stack top if entry_text is NULL
+bool l_pm_get_contents_for(const char *entry_text, bool expanding) {
+ if (!l_is_ta_table_function("pm", "get_contents_for")) return false;
+ if (entry_text) {
+ lua_newtable(lua);
+ lua_pushstring(lua, entry_text);
+ lua_rawseti(lua, -2, 1);
+ } else l_insert(lua, -1); // shift full_path down
+ lua_pushboolean(lua, expanding);
+ return l_call_function(2, 1, true);
+}
+
+// table is at stack top
+void l_pm_populate(GtkTreeIter *initial_iter) {
+ GtkTreeIter iter, child;
+ if (!lua_istable(lua, -1)) return;
+ //luaL_error(lua, "pm.get_contents_for return not a table.");
+ if (!initial_iter) gtk_tree_store_clear(pm_store);
+ lua_pushnil(lua);
+ while (lua_next(lua, -2)) {
+ if (lua_istable(lua, -1) && lua_type(lua, -2) == LUA_TSTRING) {
+ gtk_tree_store_append(pm_store, &iter, initial_iter);
+ gtk_tree_store_set(pm_store, &iter, 1, lua_tostring(lua, -2), -1);
+ lua_getfield(lua, -1, "parent");
+ if (lua_toboolean(lua, -1)) {
+ gtk_tree_store_append(pm_store, &child, &iter);
+ gtk_tree_store_set(pm_store, &child, 1, "\0dummy", -1);
+ }
+ lua_pop(lua, 1); // parent
+ lua_getfield(lua, -1, "pixbuf");
+ if (lua_isstring(lua, -1))
+ gtk_tree_store_set(pm_store, &iter, 0, lua_tostring(lua, -1), -1);
+ //else
+ //luaL_error(lua, "pm.populate: pixbuf key must have string value.");
+ lua_pop(lua, 1); // pixbuf
+ lua_getfield(lua, -1, "text");
+ gtk_tree_store_set(pm_store, &iter, 2, lua_isstring(lua, -1) ?
+ lua_tostring(lua, -1) : lua_tostring(lua, -3));
+ lua_pop(lua, 1); // display text
+ } //else luaL_error(lua, "pm.populate: id key must have table value.");
+ lua_pop(lua, 1); // value
+ }
+ lua_pop(lua, 1); // returned table
+}
+
+void l_pm_get_full_path(GtkTreePath *path) {
+ lua_newtable(lua); // will be at stack top
+ lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(pm_entry)));
+ lua_rawseti(lua, -2, 1);
+ if (!path) return;
+ GtkTreeIter iter;
+ char *filename;
+ while (gtk_tree_path_get_depth(path) > 0) {
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(pm_store), &iter, path);
+ gtk_tree_model_get(GTK_TREE_MODEL(pm_store), &iter, 1, &filename, -1);
+ lua_pushstring(lua, filename);
+ lua_rawseti(lua, -2, gtk_tree_path_get_depth(path) + 1);
+ g_free(filename);
+ gtk_tree_path_up(path);
+ }
+}
+
+void l_pm_popup_context_menu(GdkEventButton *event, GCallback callback) {
+ if (!l_is_ta_table_function("pm", "get_context_menu")) return;
+ GtkTreeIter iter;
+ GtkTreePath *path = 0;
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pm_view));
+ if (gtk_tree_selection_get_selected(sel, NULL, &iter))
+ path = gtk_tree_model_get_path(GTK_TREE_MODEL(pm_store), &iter);
+ l_pm_get_full_path(path);
+ if (path) gtk_tree_path_free(path);
+ if (lua_objlen(lua, -1) == 0) {
+ lua_pop(lua, 2); // function and full_path
+ return;
+ }
+ if (l_call_function(1, 1, true) && lua_istable(lua, -1)) {
+ GtkWidget *menu = gtk_menu_new();
+ lua_pushnil(lua);
+ while (lua_next(lua, -2)) {
+ GtkWidget *menu_item;
+ const char *label = lua_tostring(lua, -1);
+ if (g_str_has_prefix(label, "gtk-"))
+ menu_item = gtk_image_menu_item_new_from_stock(label, NULL);
+ else if (streq(label, "separator"))
+ menu_item = gtk_separator_menu_item_new();
+ else
+ menu_item = gtk_menu_item_new_with_mnemonic(label);
+ g_signal_connect(menu_item, "activate", callback, 0);
+ gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
+ lua_pop(lua, 1); // value
+ }
+ lua_pop(lua, 1); // returned table
+ gtk_widget_show_all(menu);
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ event ? event->button : 0,
+ gdk_event_get_time(reinterpret_cast<GdkEvent*>(event)));
+ } //else luaL_error(lua, "pm.get_context_menu return was not a table.");
+}
+
+// full_path is at stack top
+void l_pm_perform_action() {
+ if (!l_is_ta_table_function("pm", "perform_action")) return;
+ l_insert(lua, -1); // shift full_path down
+ l_call_function(1);
+}
+
+// full_path is at stack top
+void l_pm_perform_menu_action(const char *menu_item) {
+ if (!l_is_ta_table_function("pm", "perform_menu_action")) return;
+ l_insert(lua, -1); // shift full_path down
+ lua_pushstring(lua, menu_item);
+ l_insert(lua, -1); // shift full_path down
+ l_call_function(2);
+}
+
+void l_find(const char *ftext, int flags, bool next) {
+ if (!l_is_ta_table_function("find", "find")) return;
+ lua_pushstring(lua, ftext);
+ lua_pushinteger(lua, flags);
+ lua_pushboolean(lua, next);
+ l_call_function(3);
+}
+
+void l_find_replace(const char *rtext) {
+ if (!l_is_ta_table_function("find", "replace")) return;
+ lua_pushstring(lua, rtext);
+ l_call_function(1);
+}
+
+void l_find_replace_all(const char *ftext, const char *rtext, int flags) {
+ if (!l_is_ta_table_function("find", "replace_all")) return;
+ lua_pushstring(lua, ftext);
+ lua_pushstring(lua, rtext);
+ lua_pushinteger(lua, flags);
+ l_call_function(3);
+}
+
+/** Behaves like lua_rawgeti but casts the result to an integer. */
+static int l_rawgeti_int(LS *lua, int index, int n) {
+ lua_rawgeti(lua, index, n);
+ int ret = static_cast<int>(lua_tointeger(lua, -1));
+ lua_pop(lua, 1); // integer
+ return ret;
+}
+
+/** Behaves like lua_rawget but casts the result to a string. */
+static const char* l_rawget_str(LS *lua, int index, const char *k) {
+ lua_pushstring(lua, k); lua_rawget(lua, index);
+ const char *str = lua_tostring(lua, -1);
+ lua_pop(lua, 1); // string
+ return str;
+}
+
+/** Get a long for Scintilla w or l parameter based on type. */
+static long l_toscintillaparam(LS *lua, int type, int &arg_idx) {
+ if (type == tSTRING)
+ return reinterpret_cast<long>(lua_tostring(lua, arg_idx++));
+ else if (type == tBOOL)
+ return lua_toboolean(lua, arg_idx++);
+ else if (type == tKEYMOD)
+ return static_cast<int>(luaL_checkinteger(lua, arg_idx++)) & 0xFFFF |
+ ((static_cast<int>(luaL_checkinteger(lua, arg_idx++)) &
+ (SCMOD_SHIFT | SCMOD_CTRL | SCMOD_ALT)) << 16);
+ else if (type > tVOID && type < tBOOL)
+ return static_cast<long>(luaL_checknumber(lua, arg_idx++));
+ else
+ return 0;
+}
+
+static bool l_is_ta_table_key(LS *lua, const char *table, const char *key) {
+ if (l_ta_get(lua, table)) {
+ lua_getfield(lua, -1, key);
+ lua_remove(lua, -2); // table
+ if (lua_istable(lua, -1)) return true;
+ lua_pop(lua, 1); // non-table
+ } else lua_pop(lua, 1); // non-table
+ return false;
+}
+
+static void l_check_focused_buffer(LS *lua, int narg) {
+ ScintillaObject *sci = SCINTILLA(focused_editor);
+ sptr_t cur_doc = SS(sci, SCI_GETDOCPOINTER);
+ if (cur_doc != l_checkdocpointer(lua, narg))
+ luaL_error(lua, "The indexed buffer is not the focused one.");
+}
+
+// Lua functions to follow
+
+/** Calls Scintilla returning appropriate values.
+ * The p1, p2, and rt types are integer types of the w, l, and return
+ * parameters respectively. arg is the Lua stack index where user arguments
+ * begin. The appropriate value(s) are returned to Lua.
+ */
+LF l_call_scintilla(LS *lua, ScintillaObject *sci, int msg,
+ int p1_type, int p2_type, int rt_type, int arg) {
+ if (!sci) luaL_error(lua, "Scintilla object not initialized.");
+ long params[2] = {0, 0};
+ int params_needed = 2;
+ bool string_return = false;
+ char *return_string = 0;
+
+ // Set the w and l parameters appropriately for Scintilla.
+ if (p1_type == tLENGTH && p2_type == tSTRING) {
+ params[0] = static_cast<long>(lua_strlen(lua, arg));
+ params[1] = reinterpret_cast<long>(lua_tostring(lua, arg));
+ params_needed = 0;
+ } else if (p2_type == tSTRINGRESULT) {
+ string_return = true;
+ params_needed = 1;
+ }
+ if (params_needed > 0) params[0] = l_toscintillaparam(lua, p1_type, arg);
+ if (params_needed > 1) params[1] = l_toscintillaparam(lua, p2_type, arg);
+ if (string_return) { // if a string return, create a buffer for it
+ int len = SS(sci, msg, params[0], 0);
+ if (p1_type == tLENGTH) params[0] = len;
+ return_string = new char[len + 1]; return_string[len] = '\0';
+ params[1] = reinterpret_cast<long>(return_string);
+ }
+
+ // Send the message to Scintilla and return the appropriate values.
+ int result = SS(sci, msg, params[0], params[1]);
+ arg = lua_gettop(lua);
+ if (string_return) lua_pushstring(lua, return_string);
+ if (rt_type == tBOOL) lua_pushboolean(lua, result);
+ if (rt_type > tVOID && rt_type < tBOOL) lua_pushnumber(lua, result);
+ delete[] return_string;
+ return lua_gettop(lua) - arg;
+}
+
+LF l_call_buffer_function(LS *lua) {
+ int sci_idx = lua_upvalueindex(1); // closure from __index
+ ScintillaObject *sci =
+ reinterpret_cast<ScintillaObject*>(lua_touserdata(lua, sci_idx));
+ int buffer_func_table_idx = lua_upvalueindex(2);
+ int msg = l_rawgeti_int(lua, buffer_func_table_idx, 1);
+ int rt_type = l_rawgeti_int(lua, buffer_func_table_idx, 2);
+ int p1_type = l_rawgeti_int(lua, buffer_func_table_idx, 3);
+ int p2_type = l_rawgeti_int(lua, buffer_func_table_idx, 4);
+ return l_call_scintilla(lua, sci, msg, p1_type, p2_type, rt_type, 2);
+}
+
+LF l_buffer_mt_index(LS *lua) {
+ ScintillaObject *sci = SCINTILLA(focused_editor);
+ const char *key = luaL_checkstring(lua, 2);
+ if (l_is_ta_table_key(lua, "buffer_functions", key)) {
+ l_check_focused_buffer(lua, 1);
+ // Of the form { msg, rt_type, p1_type, p2_type }
+ lua_pushlightuserdata(lua, const_cast<ScintillaObject*>(sci));
+ l_insert(lua, -1); // shift buffer_functions down
+ lua_pushcclosure(lua, l_call_buffer_function, 2);
+ } else if (l_is_ta_table_key(lua, "buffer_properties", key)) {
+ l_check_focused_buffer(lua, 1);
+ // Of the form { get_id, set_id, rt_type, p1_type }
+ int msg = l_rawgeti_int(lua, -1, 1); // getter
+ int rt_type = l_rawgeti_int(lua, -1, 3);
+ int p1_type = l_rawgeti_int(lua, -1, 4);
+ if (p1_type != tVOID) { // indexible property
+ sptr_t doc = SS(sci, SCI_GETDOCPOINTER);
+ lua_newtable(lua);
+ lua_pushstring(lua, key); lua_setfield(lua, -2, "property");
+ lua_pushnumber(lua, doc); lua_setfield(lua, -2, "doc_pointer");
+ l_mt(lua, "_bufferp_mt", l_bufferp_mt_index, l_bufferp_mt_newindex);
+ } else return l_call_scintilla(lua, sci, msg, p1_type, tVOID, rt_type, 2);
+ } else lua_rawget(lua, 1);
+ return 1;
+}
+
+/** Helper function for the buffer property metatable.
+ * n indicates a getter or setter (1 or 2) and arg is the Lua stack index
+ * where user arguments begin. For setting buffer properties, it is 3 because
+ * the index is not an argument, but for getting and setting indexed buffer
+ * properties it is 2 because the index is an argument.
+ */
+LF l_bufferp_mt_(LS *lua, int n, const char *prop, int arg) {
+ ScintillaObject *sci = SCINTILLA(focused_editor);
+ if (l_is_ta_table_key(lua, "buffer_properties", prop)) {
+ l_check_focused_buffer(lua, 1);
+ int msg = l_rawgeti_int(lua, -1, n); // getter (1) or setter (2)
+ int rt_type = n == 1 ? l_rawgeti_int(lua, -1, 3) : tVOID;
+ int p1_type = l_rawgeti_int(lua, -1, n == 1 ? 4 : 3);
+ int p2_type = n == 2 ? l_rawgeti_int(lua, -1, 4) : tVOID;
+ if (n == 2 && (p2_type != tVOID || p2_type == tVOID && p1_type == tSTRING))
+ p1_type ^= p2_type ^= p1_type ^= p2_type;
+ if (msg != 0)
+ return l_call_scintilla(lua, sci, msg, p1_type, p2_type, rt_type, arg);
+ else luaL_error(lua, "The property '%s' is %s-only.", prop,
+ n == 1 ? "write" : "read");
+ } else (lua_gettop(lua) > 2) ? lua_rawset(lua, 1) : lua_rawget(lua, 1);
+ return 0;
+}
+
+LF l_buffer_mt_newindex(LS *lua) {
+ if (streq(lua_tostring(lua, 2), "doc_pointer"))
+ return luaL_error(lua, "'doc_pointer' is read-only.");
+ else
+ return l_bufferp_mt_(lua, 2, lua_tostring(lua, 2), 3);
+}
+
+LF l_bufferp_mt_index(LS *lua) {
+ return l_bufferp_mt_(lua, 1, l_rawget_str(lua, 1, "property"), 2);
+}
+
+LF l_bufferp_mt_newindex(LS *lua) {
+ return l_bufferp_mt_(lua, 2, l_rawget_str(lua, 1, "property"), 2);
+}
+
+LF l_view_mt_index(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "doc_pointer"))
+ lua_pushnumber(lua, SS(SCINTILLA(l_checkview(lua, 1)), SCI_GETDOCPOINTER));
+ else if (streq(key, "size")) {
+ GtkWidget *editor = l_checkview(lua, 1);
+ if (GTK_IS_PANED(gtk_widget_get_parent(editor)))
+ lua_pushnumber(lua,
+ gtk_paned_get_position(GTK_PANED(gtk_widget_get_parent(editor))));
+ else
+ lua_pushnil(lua);
+ } else lua_rawget(lua, 1);
+ return 1;
+}
+
+LF l_view_mt_newindex(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "doc_pointer") || streq(key, "widget_pointer"))
+ luaL_error(lua, "'%s' is read-only.", key);
+ else if (streq(key, "size")) {
+ GtkWidget *pane = gtk_widget_get_parent(l_checkview(lua, 1));
+ if (GTK_IS_PANED(pane))
+ gtk_paned_set_position(GTK_PANED(pane), lua_tonumber(lua, 3));
+ } else lua_rawset(lua, 1);
+ return 0;
+}
+
+LF l_ta_mt_index(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "title"))
+ lua_pushstring(lua, gtk_window_get_title(GTK_WINDOW(window)));
+ else if (streq(key, "focused_doc_pointer"))
+ lua_pushnumber(lua, SS(SCINTILLA(focused_editor), SCI_GETDOCPOINTER));
+ else if (streq(key, "clipboard_text")) {
+ char *text = gtk_clipboard_wait_for_text(
+ gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
+ if (text) lua_pushstring(lua, text);
+ g_free(text);
+ } else lua_rawget(lua, 1);
+ return 1;
+}
+
+LF l_ta_mt_newindex(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "title"))
+ gtk_window_set_title(GTK_WINDOW(window), lua_tostring(lua, 3));
+ else if (streq(key, "statusbar_text"))
+ set_statusbar_text(lua_tostring(lua, 3));
+ else if (streq(key, "docstatusbar_text"))
+ set_docstatusbar_text(lua_tostring(lua, 3));
+ else if (streq(key, "focused_doc_pointer") || streq(key, "clipboard_text"))
+ luaL_error(lua, "'%s' is read-only.", key);
+ else lua_rawset(lua, 1);
+ return 0;
+}
+
+LF l_pm_mt_index(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "entry_text"))
+ lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(pm_entry)));
+ else if (streq(key, "width"))
+ lua_pushnumber(lua,
+ gtk_paned_get_position(GTK_PANED(gtk_widget_get_parent(pm_container))));
+ else lua_rawget(lua, 1);
+ return 1;
+}
+
+LF l_pm_mt_newindex(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "entry_text"))
+ gtk_entry_set_text(GTK_ENTRY(pm_entry), lua_tostring(lua, 3));
+ else if (streq(key, "width"))
+ gtk_paned_set_position(GTK_PANED(gtk_widget_get_parent(pm_container)),
+ luaL_checkinteger(lua, 3));
+ else lua_rawset(lua, 1);
+ return 0;
+}
+
+LF l_find_mt_index(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "find_entry_text"))
+ lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(find_entry)));
+ else if (streq(key, "replace_entry_text"))
+ lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(replace_entry)));
+ else lua_rawget(lua, 1);
+ return 1;
+}
+
+LF l_find_mt_newindex(LS *lua) {
+ const char *key = lua_tostring(lua, 2);
+ if (streq(key, "find_entry_text"))
+ gtk_entry_set_text(GTK_ENTRY(find_entry), lua_tostring(lua, 3));
+ else if (streq(key, "replace_entry_text"))
+ gtk_entry_set_text(GTK_ENTRY(replace_entry), lua_tostring(lua, 3));
+ else lua_rawset(lua, 1);
+ return 0;
+}
+
+// Lua CFunctions
+
+LF l_cf_ta_buffer_new(LS *lua) {
+ new_scintilla_buffer(SCINTILLA(focused_editor), true, true);
+ l_ta_get(lua, "buffers"); lua_rawgeti(lua, -1, lua_objlen(lua, -1));
+ return 1;
+}
+
+LF l_cf_buffer_delete(LS *lua) {
+ l_check_focused_buffer(lua, 1);
+ sptr_t doc = l_checkdocpointer(lua, 1);
+ l_ta_get(lua, "buffers");
+ if (lua_objlen(lua, -1) > 1)
+ l_goto_scintilla_buffer(focused_editor, -1, false);
+ else
+ new_scintilla_buffer(SCINTILLA(focused_editor), true, true);
+ remove_scintilla_buffer(doc);
+ l_handle_signal("buffer_deleted");
+ return 0;
+}
+
+LF l_cf_buffer_find(LS *lua) {
+ l_check_focused_buffer(lua, 1);
+ TextToFind ttf = {{0, 0}, 0, {0, 0}};
+ ttf.lpstrText = const_cast<char*>(luaL_checkstring(lua, 2));
+ int args = lua_gettop(lua), flags = 0;
+ if (args > 2) flags = luaL_checkinteger(lua, 3);
+ if (args > 3) ttf.chrg.cpMin = luaL_checkinteger(lua, 4);
+ ttf.chrg.cpMax = args > 4 ? luaL_checkinteger(lua, 5)
+ : SS(SCINTILLA(focused_editor), SCI_GETLENGTH);
+ int pos = SS(SCINTILLA(focused_editor), SCI_FINDTEXT, flags,
+ reinterpret_cast<sptr_t>(&ttf));
+ if (pos > -1) {
+ lua_pushinteger(lua, ttf.chrgText.cpMin);
+ lua_pushinteger(lua, ttf.chrgText.cpMax);
+ return 2;
+ } else return 0;
+}
+
+LF l_cf_buffer_text_range(LS *lua) {
+ l_check_focused_buffer(lua, 1);
+ TextRange tr;
+ tr.chrg.cpMin = luaL_checkinteger(lua, 2);
+ tr.chrg.cpMax = luaL_checkinteger(lua, 3);
+ char *text = new char[tr.chrg.cpMax - tr.chrg.cpMin + 1];
+ tr.lpstrText = text;
+ SS(SCINTILLA(focused_editor), SCI_GETTEXTRANGE, 0,
+ reinterpret_cast<long>(&tr));
+ lua_pushstring(lua, text);
+ delete[] text;
+ return 1;
+}
+
+LF l_cf_view_focus(LS *lua) {
+ GtkWidget *editor = l_checkview(lua, 1);
+ gtk_widget_grab_focus(editor);
+ return 0;
+}
+
+LF l_cf_view_split(LS *lua) {
+ GtkWidget *editor = l_checkview(lua, 1);
+ bool vertical = lua_gettop(lua) > 1 ? lua_toboolean(lua, 2) : true;
+ split_window(editor, vertical);
+ lua_pushvalue(lua, 1); // old view
+ lua_getglobal(lua, "view"); // new view
+ return 2;
+}
+
+LF l_cf_view_unsplit(LS *lua) {
+ GtkWidget *editor = l_checkview(lua, 1);
+ lua_pushboolean(lua, unsplit_window(editor));
+ return 1;
+}
+
+#define child2(p) gtk_paned_get_child2(GTK_PANED(p))
+#define child1(p) gtk_paned_get_child1(GTK_PANED(p))
+#define vertical(p) GTK_IS_HPANED(p) ? true : false
+
+void l_create_entry(LS *lua, GtkWidget *c1, GtkWidget *c2, bool vertical) {
+ lua_newtable(lua);
+ if (GTK_IS_PANED(c1))
+ l_create_entry(lua, child1(c1), child2(c1), vertical(c1));
+ else
+ lua_pushinteger(lua,
+ l_get_docpointer_index(SS(SCINTILLA(c1), SCI_GETDOCPOINTER)));
+ lua_rawseti(lua, -2, 1);
+ if (GTK_IS_PANED(c2))
+ l_create_entry(lua, child1(c2), child2(c2), vertical(c2));
+ else
+ lua_pushinteger(lua,
+ l_get_docpointer_index(SS(SCINTILLA(c2), SCI_GETDOCPOINTER)));
+ lua_rawseti(lua, -2, 2);
+ lua_pushboolean(lua, vertical); lua_setfield(lua, -2, "vertical");
+ int size = gtk_paned_get_position(GTK_PANED(gtk_widget_get_parent(c1)));
+ lua_pushinteger(lua, size); lua_setfield(lua, -2, "size");
+}
+
+LF l_cf_ta_get_split_table(LS *lua) {
+ l_ta_get(lua, "views");
+ if (lua_objlen(lua, -1) > 1) {
+ GtkWidget *pane = gtk_widget_get_parent(focused_editor);
+ while (GTK_IS_PANED(gtk_widget_get_parent(pane)))
+ pane = gtk_widget_get_parent(pane);
+ l_create_entry(lua, child1(pane), child2(pane), vertical(pane));
+ } else lua_pushinteger(lua, l_get_docpointer_index(
+ SS(SCINTILLA(focused_editor), SCI_GETDOCPOINTER)));
+ return 1;
+}
+
+LF l_cf_ta_focus_command(LS *) { command_toggle_focus(); return 0; }
+
+LF l_cf_goto_(LS *lua, GtkWidget *editor, bool buffer=true) {
+ int n = static_cast<int>(luaL_checkinteger(lua, 1));
+ bool absolute = lua_gettop(lua) > 1 ? lua_toboolean(lua, 2) == 1 : true;
+ buffer ? l_goto_scintilla_buffer(editor, n, absolute)
+ : l_goto_scintilla_window(editor, n, absolute);
+ return 0;
+}
+
+LF l_cf_ta_goto_window(LS *lua) {
+ return l_cf_goto_(lua, focused_editor, false);
+}
+
+// If the indexed view is not currently focused, temporarily focus it so calls
+// to handlers will not throw 'indexed buffer is not the focused one' error.
+LF l_cf_view_goto_buffer(LS *lua) {
+ GtkWidget *editor = l_checkview(lua, 1);
+ bool switch_focus = editor != focused_editor;
+ GtkWidget *orig_focused_editor = focused_editor;
+ if (switch_focus) SS(SCINTILLA(editor), SCI_SETFOCUS, true);
+ lua_remove(lua, 1); // view table
+ l_cf_goto_(lua, editor, true);
+ if (switch_focus) {
+ SS(SCINTILLA(editor), SCI_SETFOCUS, false);
+ gtk_widget_grab_focus(orig_focused_editor);
+ }
+ return 0;
+}
+
+LF l_cf_pm_focus(LS *) { pm_toggle_focus(); return 0; }
+
+LF l_cf_pm_clear(LS *) { gtk_tree_store_clear(pm_store); return 0; }
+
+LF l_cf_pm_activate(LS *) {
+ g_signal_emit_by_name(G_OBJECT(pm_entry), "activate"); return 0;
+}
+
+LF l_cf_find_focus(LS *) { find_toggle_focus(); return 0; }
diff --git a/src/pm.c b/src/pm.c
new file mode 100644
index 00000000..0cd2cec7
--- /dev/null
+++ b/src/pm.c
@@ -0,0 +1,183 @@
+// Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+#include "textadept.h"
+
+GtkWidget *pm_view, *pm_entry, *pm_container;
+GtkTreeStore *pm_store;
+
+static int pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a,
+ GtkTreeIter *b, gpointer);
+static void pm_entry_activated(GtkWidget *widget, gpointer);
+static bool pm_entry_keypress(GtkWidget *, GdkEventKey *event, gpointer);
+static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter,
+ GtkTreePath *path, gpointer);
+static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter,
+ GtkTreePath *path, gpointer);
+static void pm_row_activated(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *,
+ gpointer);
+static bool pm_button_press(GtkTreeView *, GdkEventButton *event, gpointer);
+static bool pm_popup_menu(GtkWidget *, gpointer);
+static void pm_menu_activate(GtkWidget *menu_item, gpointer);
+
+GtkWidget* pm_create_ui() {
+ pm_container = gtk_vbox_new(false, 1);
+
+ pm_entry = gtk_entry_new();
+ gtk_widget_set_name(pm_entry, "textadept-pm-entry");
+ gtk_box_pack_start(GTK_BOX(pm_container), pm_entry, false, false, 0);
+
+ pm_store = gtk_tree_store_new(3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
+ GtkTreeSortable *sortable = GTK_TREE_SORTABLE(pm_store);
+ gtk_tree_sortable_set_sort_func(sortable, 1, pm_sort_iter_compare_func,
+ GINT_TO_POINTER(1), NULL);
+ gtk_tree_sortable_set_sort_column_id(sortable, 1, GTK_SORT_ASCENDING);
+
+ pm_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(pm_store));
+ g_object_unref(pm_store);
+ gtk_widget_set_name(pm_view, "textadept-pm-view");
+ gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pm_view), false);
+ gtk_tree_view_set_enable_search(GTK_TREE_VIEW(pm_view), true);
+ gtk_tree_view_set_search_column(GTK_TREE_VIEW(pm_view), 2);
+
+ GtkTreeViewColumn *column = gtk_tree_view_column_new();
+ GtkCellRenderer *renderer;
+ renderer = gtk_cell_renderer_pixbuf_new(); // pixbuf
+ gtk_tree_view_column_pack_start(column, renderer, FALSE);
+ gtk_tree_view_column_set_attributes(column, renderer, "stock-id", 0, NULL);
+ renderer = gtk_cell_renderer_text_new(); // markup text
+ gtk_tree_view_column_pack_start(column, renderer, TRUE);
+ gtk_tree_view_column_set_attributes(column, renderer, "markup", 2, NULL);
+ gtk_tree_view_append_column(GTK_TREE_VIEW(pm_view), column);
+
+ GtkWidget *scrolled = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
+ GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ gtk_container_add(GTK_CONTAINER(scrolled), pm_view);
+ gtk_box_pack_start(GTK_BOX(pm_container), scrolled, true, true, 0);
+
+ g_signal_connect(G_OBJECT(pm_entry), "activate",
+ G_CALLBACK(pm_entry_activated), 0);
+ g_signal_connect(G_OBJECT(pm_entry), "key_press_event",
+ G_CALLBACK(pm_entry_keypress), 0);
+ g_signal_connect(G_OBJECT(pm_view), "row_expanded",
+ G_CALLBACK(pm_row_expanded), 0);
+ g_signal_connect(G_OBJECT(pm_view), "row_collapsed",
+ G_CALLBACK(pm_row_collapsed), 0);
+ g_signal_connect(G_OBJECT(pm_view), "row_activated",
+ G_CALLBACK(pm_row_activated), 0);
+ g_signal_connect(G_OBJECT(pm_view), "button_press_event",
+ G_CALLBACK(pm_button_press), 0);
+ g_signal_connect(G_OBJECT(pm_view), "popup-menu",
+ G_CALLBACK(pm_popup_menu), 0);
+ return pm_container;
+}
+
+void pm_open_parent(GtkTreeIter *iter, GtkTreePath *path) {
+ l_pm_get_full_path(path);
+ if (l_pm_get_contents_for(NULL, true)) l_pm_populate(iter);
+ GtkTreeIter child;
+ char *filename;
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pm_store), &child, iter, 0);
+ gtk_tree_model_get(GTK_TREE_MODEL(pm_store), &child, 1, &filename, -1);
+ if (strcmp(reinterpret_cast<const char*>(filename), "\0dummy") == 0)
+ gtk_tree_store_remove(pm_store, &child);
+ g_free(filename);
+}
+
+void pm_close_parent(GtkTreeIter *iter, GtkTreePath *) {
+ GtkTreeIter child;
+ gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pm_store), &child, iter, 0);
+ while (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pm_store), iter))
+ gtk_tree_store_remove(pm_store, &child);
+ gtk_tree_store_append(pm_store, &child, iter);
+ gtk_tree_store_set(pm_store, &child, 1, "\0dummy", -1);
+}
+
+void pm_activate_selection() {
+ GtkTreeIter iter;
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+ gtk_tree_view_get_cursor(GTK_TREE_VIEW(pm_view), &path, &column);
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(pm_store), &iter, path);
+ if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(pm_store), &iter))
+ if (gtk_tree_view_row_expanded(GTK_TREE_VIEW(pm_view), path))
+ gtk_tree_view_collapse_row(GTK_TREE_VIEW(pm_view), path);
+ else
+ gtk_tree_view_expand_row(GTK_TREE_VIEW(pm_view), path, false);
+ else {
+ l_pm_get_full_path(path);
+ l_pm_perform_action();
+ }
+ gtk_tree_path_free(path);
+}
+
+void pm_popup_context_menu(GdkEventButton *event) {
+ l_pm_popup_context_menu(event, G_CALLBACK(pm_menu_activate));
+}
+
+void pm_process_selected_menu_item(GtkWidget *menu_item) {
+ GtkWidget *label = gtk_bin_get_child(GTK_BIN(menu_item));
+ const char *text = gtk_label_get_text(GTK_LABEL(label));
+ GtkTreePath *path;
+ GtkTreeViewColumn *column;
+ gtk_tree_view_get_cursor(GTK_TREE_VIEW(pm_view), &path, &column);
+ l_pm_get_full_path(path);
+ l_pm_perform_menu_action(text);
+}
+
+void pm_toggle_focus() {
+ gtk_widget_grab_focus(
+ GTK_WIDGET_HAS_FOCUS(focused_editor) ? pm_entry : focused_editor);
+}
+
+static gint pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a,
+ GtkTreeIter *b, gpointer) {
+ const gchar *a_text, *b_text;
+ gtk_tree_model_get(model, a, 1, &a_text, -1);
+ gtk_tree_model_get(model, b, 1, &b_text, -1);
+ if (a_text == NULL && b_text == NULL) return 0;
+ else if (a_text == NULL) return -1;
+ else if (b_text == NULL) return 1;
+ else return strcasecmp(a_text, b_text);
+}
+
+// Signals
+static void pm_entry_activated(GtkWidget *widget, gpointer) {
+ const char *entry_text = gtk_entry_get_text(GTK_ENTRY(widget));
+ if (l_pm_get_contents_for(entry_text)) l_pm_populate();
+}
+
+static bool pm_entry_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
+ if (event->keyval == 0xff09 && event->state == GDK_CONTROL_MASK) {
+ gtk_widget_grab_focus(focused_editor);
+ return true;
+ } else return false;
+}
+
+static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter,
+ GtkTreePath *path, gpointer) {
+ pm_open_parent(iter, path);
+}
+
+static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter,
+ GtkTreePath *path, gpointer) {
+ pm_close_parent(iter, path);
+}
+
+static void pm_row_activated(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *,
+ gpointer) {
+ pm_activate_selection();
+}
+
+static bool pm_button_press(GtkTreeView *, GdkEventButton *event, gpointer) {
+ if (event->type != GDK_BUTTON_PRESS || event->button != 3) return false;
+ pm_popup_context_menu(event); return true;
+}
+
+static bool pm_popup_menu(GtkWidget *, gpointer) {
+ pm_popup_context_menu(NULL); return true;
+}
+
+static void pm_menu_activate(GtkWidget *menu_item, gpointer) {
+ pm_process_selected_menu_item(menu_item);
+}
diff --git a/src/properties.h b/src/properties.h
new file mode 100644
index 00000000..1786bbc4
--- /dev/null
+++ b/src/properties.h
@@ -0,0 +1,86 @@
+// Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+#ifndef PROPERTIES_H
+#define PROPERTIES_H
+
+#include "textadept.h"
+
+#define sp(k, v) SSS(sci, SCI_SETPROPERTY, k, v)
+#define color(r, g, b) r | (g << 8) | (b << 16)
+
+void set_default_editor_properties(ScintillaObject *sci) {
+ sp("lexer.lua.home", "/usr/share/textadept/lexers/");
+ sp("lexer.lua.script", "/usr/share/textadept/lexers/lexer.lua");
+
+ // caret
+ SS(sci, SCI_SETCARETFORE, color(0xAA, 0xAA, 0xAA));
+ SS(sci, SCI_SETCARETLINEVISIBLE, true);
+ SS(sci, SCI_SETCARETLINEBACK, color(0x44, 0x44, 0x44));
+ SS(sci, SCI_SETXCARETPOLICY, CARET_SLOP, 20);
+ SS(sci, SCI_SETYCARETPOLICY, CARET_SLOP | CARET_STRICT | CARET_EVEN, 1);
+ SS(sci, SCI_SETCARETSTYLE, 2);
+ SS(sci, SCI_SETCARETPERIOD, 0);
+
+ // selection
+ SS(sci, SCI_SETSELFORE, 1, color(0x33, 0x33, 0x33));
+ SS(sci, SCI_SETSELBACK, 1, color(0x99, 0x99, 0x99));
+
+ SS(sci, SCI_SETBUFFEREDDRAW, 1);
+ SS(sci, SCI_SETTWOPHASEDRAW, 0);
+ SS(sci, SCI_CALLTIPUSESTYLE, 32);
+ SS(sci, SCI_USEPOPUP, 0);
+ SS(sci, SCI_SETFOLDFLAGS, 16);
+ SS(sci, SCI_SETMODEVENTMASK, SC_MOD_CHANGEFOLD);
+
+ SS(sci, SCI_SETMARGINWIDTHN, 0, 4 + 2 * // line number margin
+ SS(sci, SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast<long>("9")));
+
+ SS(sci, SCI_SETMARGINWIDTHN, 1, 0); // marker margin invisible
+
+ // fold margin
+ SS(sci, SCI_SETFOLDMARGINCOLOUR, 1, color(0xAA, 0xAA, 0xAA));
+ SS(sci, SCI_SETFOLDMARGINHICOLOUR, 1, color(0xAA, 0xAA, 0xAA));
+ SS(sci, SCI_SETMARGINTYPEN, 2, SC_MARGIN_SYMBOL);
+ SS(sci, SCI_SETMARGINWIDTHN, 2, 10);
+ SS(sci, SCI_SETMARGINMASKN, 2, SC_MASK_FOLDERS);
+ SS(sci, SCI_SETMARGINSENSITIVEN, 2, 1);
+
+ // fold margin markers
+ SS(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPEN, SC_MARK_ARROWDOWN);
+ SS(sci, SCI_MARKERSETFORE, SC_MARKNUM_FOLDEROPEN, 0);
+ SS(sci, SCI_MARKERSETBACK, SC_MARKNUM_FOLDEROPEN, 0);
+ SS(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDER, SC_MARK_ARROW);
+ SS(sci, SCI_MARKERSETFORE, SC_MARKNUM_FOLDER, 0);
+ SS(sci, SCI_MARKERSETBACK, SC_MARKNUM_FOLDER, 0);
+ SS(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERSUB, SC_MARK_EMPTY);
+ SS(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERTAIL, SC_MARK_EMPTY);
+ SS(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEREND, SC_MARK_EMPTY);
+ SS(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDEROPENMID, SC_MARK_EMPTY);
+ SS(sci, SCI_MARKERDEFINE, SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_EMPTY);
+
+ SS(sci, SCI_SETSCROLLWIDTH, 2000);
+ SS(sci, SCI_SETHSCROLLBAR, 1);
+ SS(sci, SCI_SETENDATLASTLINE, 1);
+ SS(sci, SCI_SETCARETSTICKY, 0);
+}
+
+void set_default_buffer_properties(ScintillaObject *sci) {
+ sp("fold", "1");
+ sp("fold.by.indentation", "1");
+
+ SS(sci, SCI_SETLEXER, SCLEX_LPEG);
+ SS(sci, SCI_SETLEXERLANGUAGE, 0, reinterpret_cast<long>("container"));
+
+ // Tabs and indentation
+ SS(sci, SCI_SETTABWIDTH, 2);
+ SS(sci, SCI_SETUSETABS, 0);
+ SS(sci, SCI_SETINDENT, 2);
+ SS(sci, SCI_SETTABINDENTS, 1);
+ SS(sci, SCI_SETBACKSPACEUNINDENTS, 1);
+ SS(sci, SCI_SETINDENTATIONGUIDES, 1);
+
+ SS(sci, SCI_SETEOLMODE, SC_EOL_LF);
+ SS(sci, SCI_AUTOCSETCHOOSESINGLE, 1);
+}
+
+#endif
diff --git a/src/textadept.c b/src/textadept.c
new file mode 100644
index 00000000..d2d94198
--- /dev/null
+++ b/src/textadept.c
@@ -0,0 +1,261 @@
+// Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+#include "textadept.h"
+#include "properties.h"
+
+#define signal(o, s, c) g_signal_connect(G_OBJECT(o), s, G_CALLBACK(c), 0)
+
+GtkWidget *window, *statusbar, *docstatusbar, *focused_editor, *command_entry;
+
+static void c_activated(GtkWidget *widget, gpointer);
+static bool c_keypress(GtkWidget *widget, GdkEventKey *event, gpointer);
+static void t_notification(GtkWidget*, gint, gpointer lParam, gpointer);
+static void t_command(GtkWidget *editor, gint wParam, gpointer, gpointer);
+static bool t_keypress(GtkWidget*, GdkEventKey *event, gpointer);
+static bool w_keypress(GtkWidget*, GdkEventKey *event, gpointer);
+static bool w_exit(GtkWidget*, GdkEventAny*, gpointer);
+
+int main(int argc, char **argv) {
+ gtk_init(&argc, &argv);
+ l_init(argc, argv);
+ create_ui();
+ l_load_script("init.lua");
+ gtk_main();
+ return 0;
+}
+
+void create_ui() {
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ signal(window, "delete_event", w_exit);
+ signal(window, "key_press_event", w_keypress);
+ GtkWidget *vbox = gtk_vbox_new(false, 0);
+ gtk_container_add(GTK_CONTAINER(window), vbox);
+ GtkWidget *pane = gtk_hpaned_new();
+ gtk_container_add(GTK_CONTAINER(vbox), pane);
+ GtkWidget *pm = pm_create_ui();
+ gtk_paned_add1(GTK_PANED(pane), pm);
+ GtkWidget *hbox = gtk_hbox_new(false, 0);
+ gtk_paned_add2(GTK_PANED(pane), hbox);
+ GtkWidget *editor = new_scintilla_window();
+ gtk_box_pack_start(GTK_BOX(hbox), editor, true, true, 0);
+ GtkWidget *find = find_create_ui();
+ gtk_box_pack_start(GTK_BOX(vbox), find, false, false, 5);
+ GtkWidget *hboxs = gtk_hbox_new(false, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), hboxs, false, false, 0);
+ statusbar = gtk_statusbar_new();
+ gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, "");
+ gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(statusbar), false);
+ gtk_box_pack_start(GTK_BOX(hboxs), statusbar, true, true, 0);
+ command_entry = gtk_entry_new();
+ signal(command_entry, "activate", c_activated);
+ signal(command_entry, "key_press_event", c_keypress);
+ g_object_set(G_OBJECT(command_entry), "width-request", 200, NULL);
+ gtk_box_pack_start(GTK_BOX(hboxs), command_entry, false, false, 0);
+ docstatusbar = gtk_statusbar_new();
+ gtk_statusbar_push(GTK_STATUSBAR(docstatusbar), 0, "");
+ g_object_set(G_OBJECT(docstatusbar), "width-request", 350, NULL);
+ gtk_box_pack_start(GTK_BOX(hboxs), docstatusbar, false, false, 0);
+ gtk_widget_show_all(window);
+ gtk_widget_hide(findbox); // hide initially
+ gtk_widget_grab_focus(editor);
+}
+
+GtkWidget* new_scintilla_window(sptr_t buffer_id) {
+ GtkWidget *editor = scintilla_new();
+ signal(editor, "key_press_event", t_keypress);
+ signal(editor, "command", t_command);
+ signal(editor, SCINTILLA_NOTIFY, t_notification);
+ set_default_editor_properties(SCINTILLA(editor));
+ l_add_scintilla_window(editor);
+ gtk_widget_grab_focus(editor); focused_editor = editor;
+ if (buffer_id) {
+ SS(SCINTILLA(editor), SCI_SETDOCPOINTER, 0, buffer_id);
+ new_scintilla_buffer(SCINTILLA(editor), false, false);
+ } else new_scintilla_buffer(SCINTILLA(editor), false, true);
+ l_set_view_global(editor);
+ l_handle_signal("view_new");
+ return editor;
+}
+
+void remove_scintilla_window(GtkWidget *editor) {
+ l_remove_scintilla_window(editor);
+ gtk_widget_destroy(editor);
+}
+
+void new_scintilla_buffer(ScintillaObject *sci, bool create, bool addref) {
+ sptr_t doc;
+ doc = SS(sci, SCI_GETDOCPOINTER);
+ if (create) { // create the new document
+ doc = SS(sci, SCI_CREATEDOCUMENT);
+ //SS(sci, SCI_SETDOCPOINTER, 0, doc);
+ int index = l_add_scintilla_buffer(doc);
+ l_goto_scintilla_buffer(focused_editor, index);
+ } else if (addref) {
+ l_add_scintilla_buffer(doc);
+ SS(sci, SCI_ADDREFDOCUMENT, 0, doc);
+ }
+ // Setup default styling and properties.
+ SS(sci, SCI_STYLESETFONT, 32,
+ reinterpret_cast<long>("!Bitstream Vera Sans Mono"));
+ SS(sci, SCI_STYLESETSIZE, 32, 8);
+ SS(sci, SCI_STYLESETFORE, 32, 0xAA | (0xAA << 8) | (0xAA << 16));
+ SS(sci, SCI_STYLESETBACK, 32, 0x33 | (0x33 << 8) | (0x33 << 16));
+ set_default_buffer_properties(sci);
+ l_set_buffer_global(sci);
+ l_handle_signal("buffer_new");
+}
+
+void remove_scintilla_buffer(sptr_t doc) {
+ l_remove_scintilla_buffer(doc);
+ SS(SCINTILLA(focused_editor), SCI_RELEASEDOCUMENT, 0, doc);
+}
+
+void split_window(GtkWidget *editor, bool vertical) {
+ g_object_ref(editor);
+ int first_line = SS(SCINTILLA(editor), SCI_GETFIRSTVISIBLELINE);
+ int current_pos = SS(SCINTILLA(editor), SCI_GETCURRENTPOS);
+ int anchor = SS(SCINTILLA(editor), SCI_GETANCHOR);
+ int middle = (vertical ? editor->allocation.width
+ : editor->allocation.height) / 2;
+
+ sptr_t curdoc = SS(SCINTILLA(editor), SCI_GETDOCPOINTER);
+ GtkWidget *neweditor = new_scintilla_window(curdoc);
+ GtkWidget *parent = gtk_widget_get_parent(editor);
+ gtk_container_remove(GTK_CONTAINER(parent), editor);
+ GtkWidget *pane = vertical ? gtk_hpaned_new() : gtk_vpaned_new();
+ gtk_paned_add1(GTK_PANED(pane), editor);
+ gtk_paned_add2(GTK_PANED(pane), neweditor);
+ gtk_container_add(GTK_CONTAINER(parent), pane);
+ gtk_paned_set_position(GTK_PANED(pane), middle);
+ gtk_widget_show_all(pane);
+ gtk_widget_grab_focus(neweditor);
+
+ SS(SCINTILLA(neweditor), SCI_SETSEL, anchor, current_pos);
+ int new_first_line = SS(SCINTILLA(neweditor), SCI_GETFIRSTVISIBLELINE);
+ SS(SCINTILLA(neweditor), SCI_LINESCROLL, first_line - new_first_line);
+ g_object_unref(editor);
+}
+
+void remove_scintilla_windows_in_pane(GtkWidget *pane) {
+ GtkWidget *child1 = gtk_paned_get_child1(GTK_PANED(pane));
+ GtkWidget *child2 = gtk_paned_get_child2(GTK_PANED(pane));
+ GTK_IS_PANED(child1) ? remove_scintilla_windows_in_pane(child1)
+ : remove_scintilla_window(child1);
+ GTK_IS_PANED(child2) ? remove_scintilla_windows_in_pane(child2)
+ : remove_scintilla_window(child2);
+}
+
+bool unsplit_window(GtkWidget *editor) {
+ GtkWidget *pane = gtk_widget_get_parent(editor);
+ if (GTK_IS_PANED(pane)) {
+ GtkWidget *other = gtk_paned_get_child1(GTK_PANED(pane));
+ if (other == editor) other = gtk_paned_get_child2(GTK_PANED(pane));
+ g_object_ref(editor); g_object_ref(other);
+ gtk_container_remove(GTK_CONTAINER(pane), editor);
+ gtk_container_remove(GTK_CONTAINER(pane), other);
+ GTK_IS_PANED(other) ? remove_scintilla_windows_in_pane(other)
+ : remove_scintilla_window(other);
+ GtkWidget *parent = gtk_widget_get_parent(pane);
+ gtk_container_remove(GTK_CONTAINER(parent), pane);
+ if (GTK_IS_PANED(parent))
+ if (!gtk_paned_get_child1(GTK_PANED(parent)))
+ gtk_paned_add1(GTK_PANED(parent), editor);
+ else
+ gtk_paned_add2(GTK_PANED(parent), editor);
+ else
+ gtk_container_add(GTK_CONTAINER(parent), editor);
+ gtk_widget_show_all(parent);
+ gtk_widget_grab_focus(GTK_WIDGET(editor));
+ g_object_unref(editor); g_object_unref(other);
+ return true;
+ } else return false;
+}
+
+void resize_split(GtkWidget *editor, int pos, bool increment) {
+ GtkWidget *pane = gtk_widget_get_parent(editor);
+ int width = gtk_paned_get_position(GTK_PANED(pane));
+ gtk_paned_set_position(GTK_PANED(pane), pos + (increment ? width : 0));
+}
+
+void set_statusbar_text(const char *text) {
+ gtk_statusbar_pop(GTK_STATUSBAR(statusbar), 0);
+ gtk_statusbar_push(GTK_STATUSBAR(statusbar), 0, text);
+}
+
+void set_docstatusbar_text(const char *text) {
+ gtk_statusbar_pop(GTK_STATUSBAR(docstatusbar), 0);
+ gtk_statusbar_push(GTK_STATUSBAR(docstatusbar), 0, text);
+}
+
+void command_toggle_focus() {
+ gtk_widget_grab_focus(
+ GTK_WIDGET_HAS_FOCUS(focused_editor) ? command_entry : focused_editor);
+}
+
+// Notifications/signals
+
+static void c_activated(GtkWidget *widget, gpointer) {
+ l_ta_command(gtk_entry_get_text(GTK_ENTRY(widget)));
+ gtk_widget_grab_focus(focused_editor);
+}
+
+void c_insert(const char *t) {
+ int pos = gtk_editable_get_position(GTK_EDITABLE(command_entry));
+ gtk_editable_insert_text(GTK_EDITABLE(command_entry), t, strlen(t), &pos);
+ gtk_editable_set_position(GTK_EDITABLE(command_entry), pos);
+}
+
+static bool c_keypress(GtkWidget *widget, GdkEventKey *event, gpointer) {
+ if (event->state == 0)
+ switch(event->keyval) {
+ case 0xff1b:
+ l_handle_completion(NULL);
+ gtk_widget_grab_focus(focused_editor);
+ return true;
+ case 0xff09:
+ l_handle_completion(gtk_entry_get_text(GTK_ENTRY(widget)));
+ return true;
+ }
+ else if (event->state == GDK_MOD1_MASK)
+ switch (event->keyval) {
+ case 0x062: c_insert("buffer"); return true;
+ case 0x066: c_insert("find"); return true;
+ case 0x070: c_insert("pm"); return true;
+ case 0x074: c_insert("textadept"); return true;
+ case 0x076: c_insert("view"); return true;
+ }
+ return false;
+}
+
+static void t_notification(GtkWidget*, gint, gpointer lParam, gpointer) {
+ SCNotification *n = reinterpret_cast<SCNotification*>(lParam);
+ l_handle_scnnotification(n);
+}
+
+static void t_command(GtkWidget *editor, gint wParam, gpointer, gpointer) {
+ if (wParam >> 16 == SCEN_SETFOCUS) {
+ focused_editor = editor;
+ l_set_view_global(editor);
+ l_set_buffer_global(SCINTILLA(editor));
+ }
+}
+
+static bool t_keypress(GtkWidget*, GdkEventKey *event, gpointer) {
+ return l_handle_keypress(event->keyval, event);
+}
+
+static bool w_keypress(GtkWidget* , GdkEventKey *event, gpointer) {
+ if (event->keyval == 0xff1b && GTK_WIDGET_VISIBLE(findbox)) {
+ gtk_widget_hide(findbox);
+ gtk_widget_grab_focus(focused_editor);
+ return true;
+ } else return false;
+}
+
+static bool w_exit(GtkWidget*, GdkEventAny*, gpointer) {
+ if (!l_handle_signal("quit")) return true;
+ l_close();
+ scintilla_release_resources();
+ gtk_main_quit();
+ return false;
+}
diff --git a/src/textadept.h b/src/textadept.h
new file mode 100644
index 00000000..406d0bec
--- /dev/null
+++ b/src/textadept.h
@@ -0,0 +1,99 @@
+// Copyright 2007 Mitchell mitchell<att>caladbolg.net. See LICENSE.
+
+#ifndef TEXTADEPT_H
+#define TEXTADEPT_H
+
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#define PLAT_GTK 1
+#include <Scintilla.h>
+#include <SciLexer.h>
+#include <ScintillaWidget.h>
+
+extern "C" {
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+}
+
+// globals
+extern GtkWidget
+ *window, *focused_editor, *command_entry,
+ *pm_container, *pm_entry, *pm_view,
+ *findbox, *find_entry, *replace_entry;
+extern GtkTreeStore *pm_store;
+extern lua_State *lua;
+static const char *textadept_home = "/usr/share/textadept/";
+
+// textadept.c
+void create_ui();
+GtkWidget* new_scintilla_window(sptr_t default_id=NULL);
+void remove_scintilla_window(GtkWidget *editor);
+void new_scintilla_buffer(ScintillaObject *sci, bool create=true,
+ bool addref=true);
+void remove_scintilla_buffer(sptr_t doc);
+void split_window(GtkWidget *editor, bool vertical=true);
+bool unsplit_window(GtkWidget *editor);
+void resize_split(GtkWidget *editor, int pos, bool increment=true);
+void set_statusbar_text(const char *text);
+void set_docstatusbar_text(const char *text);
+void command_toggle_focus();
+
+static long SS(ScintillaObject *sci, unsigned int msg, unsigned long wParam=0,
+ long lParam=0) {
+ return scintilla_send_message(sci, msg, wParam, lParam);
+}
+static long SSS(ScintillaObject *sci, unsigned int msg, const char *wParam=0,
+ const char *lParam=0) {
+ return scintilla_send_message(sci, msg, reinterpret_cast<long>(wParam),
+ reinterpret_cast<long>(lParam));
+}
+
+// lua_interface.c
+void l_init(int argc, char **argv);
+void l_close();
+void l_load_script(const char *script_file);
+void l_add_scintilla_window(GtkWidget *editor);
+void l_remove_scintilla_window(GtkWidget *editor);
+void l_goto_scintilla_window(GtkWidget *editor, int n, bool absolute=true);
+void l_set_view_global(GtkWidget *editor);
+int l_add_scintilla_buffer(sptr_t doc);
+void l_remove_scintilla_buffer(sptr_t doc);
+void l_goto_scintilla_buffer(GtkWidget *editor, int n, bool absolute=true);
+void l_set_buffer_global(ScintillaObject *sci);
+
+void l_handle_error(lua_State *lua, const char *errmsg=0);
+bool l_handle_signal(const char *s);
+bool l_handle_keypress(int keyval, GdkEventKey *event);
+void l_handle_completion(const char *command);
+void l_handle_scnnotification(SCNotification *n);
+
+void l_ta_command(const char *command);
+
+bool l_pm_get_contents_for(const char *entry_text, bool expanding=false);
+void l_pm_populate(GtkTreeIter *initial_iter=NULL);
+void l_pm_get_full_path(GtkTreePath *path);
+void l_pm_perform_action();
+void l_pm_popup_context_menu(GdkEventButton *event, GCallback callback);
+void l_pm_perform_menu_action(const char *menu_item);
+
+void l_find(const char *ftext, int flags, bool next=true);
+void l_find_replace(const char *rtext);
+void l_find_replace_all(const char *ftext, const char *rtext, int flags);
+
+// pm.c
+GtkWidget* pm_create_ui();
+void pm_toggle_focus();
+void pm_open_parent(GtkTreeIter *iter, GtkTreePath *path);
+void pm_close_parent(GtkTreeIter *iter, GtkTreePath *path);
+void pm_activate_selection();
+void pm_popup_context_menu(GdkEventButton *event, GCallback callback);
+void pm_process_selected_menu_item(GtkWidget *menu_item);
+
+// find_replace.c
+GtkWidget *find_create_ui();
+void find_toggle_focus();
+
+#endif