diff options
author | 2007-08-06 04:58:17 -0400 | |
---|---|---|
committer | 2007-08-06 04:58:17 -0400 | |
commit | 1aa6c6702415079a6546f1325789c3a86f733f34 (patch) | |
tree | f8cf70e3d7b1c68917a666e299cf7a919be1e885 |
Initial import of core C files.
-rw-r--r-- | src/Makefile | 20 | ||||
-rw-r--r-- | src/find_replace.c | 122 | ||||
-rw-r--r-- | src/lua_interface.c | 951 | ||||
-rw-r--r-- | src/pm.c | 183 | ||||
-rw-r--r-- | src/properties.h | 86 | ||||
-rw-r--r-- | src/textadept.c | 261 | ||||
-rw-r--r-- | src/textadept.h | 99 |
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 |