diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/textadept.c | 2344 |
1 files changed, 1072 insertions, 1272 deletions
diff --git a/src/textadept.c b/src/textadept.c index cd3611cc..a1fdfc9e 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -7,14 +7,14 @@ #include <gtk/gtk.h> #define PLAT_GTK 1 -#include <Scintilla.h> -#include <SciLexer.h> -#include <ScintillaWidget.h> +#include "Scintilla.h" +#include "SciLexer.h" +#include "ScintillaWidget.h" -#include <gcocoadialog.h> -#include <lua.h> -#include <lualib.h> -#include <lauxlib.h> +#include "gcocoadialog.h" +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" #if __WIN32__ #include <windows.h> @@ -29,38 +29,15 @@ #include <sys/sysctl.h> #endif -#define gbool gboolean #define SS(view, m, w, l) scintilla_send_message(SCINTILLA(view), m, w, l) #define signal(o, s, c) g_signal_connect(G_OBJECT(o), s, G_CALLBACK(c), 0) -#define streq(s1, s2) strcmp(s1, s2) == 0 -#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_emit_event_key(name, event) \ - l_emit_event(name, LUA_TNUMBER, event->keyval, LUA_TBOOLEAN, \ - event->state & GDK_SHIFT_MASK, LUA_TBOOLEAN, \ - event->state & GDK_CONTROL_MASK, LUA_TBOOLEAN, \ - event->state & GDK_MOD1_MASK, LUA_TBOOLEAN, \ - event->state & GDK_MOD5_MASK, -1) -#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); \ -} -#define l_togtkwidget(l, i) (GtkWidget *)lua_touserdata(l, i) // Defines for different GTK versions. #if !(GTK_CHECK_VERSION(2,18,0) || GTK_CHECK_VERSION(3,0,0)) #define gtk_widget_set_can_default(w,_) GTK_WIDGET_SET_FLAGS(w, GTK_CAN_DEFAULT) #define gtk_widget_set_can_focus(w,_) GTK_WIDGET_UNSET_FLAGS(w, GTK_CAN_FOCUS) -#define gtk_widget_get_allocation(e, a) { \ - (a)->width = e->allocation.width; \ - (a)->height = e->allocation.height; \ -} +#define gtk_widget_get_allocation(e, a) \ + ((a)->width = e->allocation.width, (a)->height = e->allocation.height) #define gtk_widget_has_focus GTK_WIDGET_HAS_FOCUS #define gtk_widget_get_visible GTK_WIDGET_VISIBLE #define gtk_widget_get_window(w) w->window @@ -79,20 +56,18 @@ // Window GtkWidget *window, *focused_view, *menubar, *statusbar[2]; -GtkAccelGroup *accel = 0; +GtkAccelGroup *accel; char *textadept_home; - -void create_ui(); -GtkWidget *new_view(sptr_t); -void new_buffer(GtkWidget *, int, int); - -static void s_notification(GtkWidget *, gint, gpointer, gpointer); +static void new_window(); +static GtkWidget *new_view(sptr_t); +static void new_buffer(sptr_t); +static void s_notify(GtkWidget *, gint, gpointer, gpointer); static void s_command(GtkWidget *, gint, gpointer, gpointer); -static gbool s_keypress(GtkWidget *, GdkEventKey *, gpointer); -static gbool s_buttonpress(GtkWidget *, GdkEventButton *, gpointer); -static gbool w_focus(GtkWidget *, GdkEventFocus *, gpointer); -static gbool w_keypress(GtkWidget *, GdkEventKey *, gpointer); -static gbool w_exit(GtkWidget *, GdkEventAny *, gpointer); +static gboolean s_keypress(GtkWidget *, GdkEventKey *, gpointer); +static gboolean s_buttonpress(GtkWidget *, GdkEventButton *, gpointer); +static gboolean w_focus(GtkWidget *, GdkEventFocus *, gpointer); +static gboolean w_keypress(GtkWidget *, GdkEventKey *, gpointer); +static gboolean w_exit(GtkWidget *, GdkEventAny *, gpointer); #if __OSX__ static OSErr w_ae_open(const AppleEvent *, AppleEvent *, long); static OSErr w_ae_quit(const AppleEvent *, AppleEvent *, long); @@ -102,22 +77,20 @@ static OSErr w_ae_quit(const AppleEvent *, AppleEvent *, long); GtkWidget *findbox, *find_entry, *replace_entry, *fnext_button, *fprev_button, *r_button, *ra_button, *match_case_opt, *whole_word_opt, *lua_opt, *in_files_opt, *flabel, *rlabel; -GtkWidget *find_create_ui(); GtkListStore *find_store, *repl_store; - -static void find_button_clicked(GtkWidget *, gpointer); +static GtkWidget *new_findbox(); +static void f_clicked(GtkWidget *, gpointer); // Command Entry GtkWidget *command_entry; GtkListStore *cc_store; GtkEntryCompletion *command_entry_completion; - -static int cc_match_func(GtkEntryCompletion *, const char *, GtkTreeIter *, - gpointer); -static gbool cc_match_selected(GtkEntryCompletion *, GtkTreeModel *, - GtkTreeIter *, gpointer); -static void c_activated(GtkWidget *, gpointer); -static gbool c_keypress(GtkWidget *, GdkEventKey *, gpointer); +static int cc_matchfunc(GtkEntryCompletion *, const char *, GtkTreeIter *, + gpointer); +static gboolean cc_matchselected(GtkEntryCompletion *, GtkTreeModel *, + GtkTreeIter *, gpointer); +static void c_activate(GtkWidget *, gpointer); +static gboolean c_keypress(GtkWidget *, GdkEventKey *, gpointer); // Lua lua_State *lua; @@ -125,42 +98,40 @@ int closing = FALSE; char *statusbar_text = 0; static int tVOID = 0, tINT = 1, tLENGTH = 2, /*tPOSITION = 3, tCOLOUR = 4,*/ tBOOL = 5, tKEYMOD = 6, tSTRING = 7, tSTRINGRESULT = 8; - -int l_init(int, char **, int); -void l_close(); -int l_load_script(const char *); -void l_add_view(GtkWidget *); -void l_remove_view(GtkWidget *); -void l_set_view_global(GtkWidget *); -int l_add_buffer(sptr_t); -void l_remove_buffer(sptr_t); -void l_goto_buffer(GtkWidget *, int, int); -void l_set_buffer_global(GtkWidget *); -int l_emit_event(const char *, ...); -void l_emit_scnotification(struct SCNotification *); -void l_gui_popup_context_menu(GdkEventButton *); -// Extra Lua libraries. -LUALIB_API int (luaopen_lpeg) (lua_State *L); -LUALIB_API int (luaopen_lfs) (lua_State *L); - -static void clear_table(lua_State *, int); -static int l_buffer_mt_index(lua_State *), l_buffer_mt_newindex(lua_State *), - l_bufferp_mt_index(lua_State *), l_bufferp_mt_newindex(lua_State *), - l_view_mt_index(lua_State *), l_view_mt_newindex(lua_State *), - l_gui_mt_index(lua_State *), l_gui_mt_newindex(lua_State *), - l_find_mt_index(lua_State *), l_find_mt_newindex(lua_State *), - l_ce_mt_index(lua_State *), l_ce_mt_newindex(lua_State *); -static int l_cf_buffer_delete(lua_State *), l_cf_buffer_text_range(lua_State *), - l_cf_view_focus(lua_State *), l_cf_view_split(lua_State *), - l_cf_view_unsplit(lua_State *), l_cf_buffer_new(lua_State *), - l_cf_gui_dialog(lua_State *), l_cf_gui_get_split_table(lua_State *), - l_cf_gui_goto_view(lua_State *), l_cf_view_goto_buffer(lua_State *), - l_cf_gui_gtkmenu(lua_State *), l_cf_string_iconv(lua_State *), - l_cf_reset(lua_State *), l_cf_quit(lua_State *), - l_cf_find_focus(lua_State *), l_cf_find_next(lua_State *), - l_cf_find_prev(lua_State *), l_cf_find_replace(lua_State *), - l_cf_find_replace_all(lua_State *), l_cf_ce_focus(lua_State *), - l_cf_ce_show_completions(lua_State *), l_cf_timeout(lua_State *); +static int lL_init(lua_State *, int, char **, int); +static void l_close(lua_State *); +static int lL_dofile(lua_State *, const char *); +static void lL_addview(lua_State *, GtkWidget *); +static void lL_removeview(lua_State *, GtkWidget *); +static void lL_adddoc(lua_State *, sptr_t); +static void lL_removedoc(lua_State *, sptr_t); +static void lL_gotodoc(lua_State *, GtkWidget *, int, int); +static int lL_event(lua_State *, const char *, ...); +static void lL_notify(lua_State *, struct SCNotification *); +static void lL_showcontextmenu(lua_State *, GdkEventButton *); +static void lL_cleartable(lua_State *, int); +static void l_pushview(lua_State *, GtkWidget *); +static void l_pushdoc(lua_State *, sptr_t); +#define l_setglobalview(l, v) (l_pushview(l, v), lua_setglobal(l, "view")) +#define l_setglobaldoc(l, d) (l_pushdoc(l, d), lua_setglobal(l, "buffer")) +LUALIB_API int (luaopen_lpeg) (lua_State *); +LUALIB_API int (luaopen_lfs) (lua_State *); +static int lbuf_property(lua_State *), + lview__index(lua_State *), lview__newindex(lua_State *), + lgui__index(lua_State *), lgui__newindex(lua_State *), + lfind__index(lua_State *), lfind__newindex(lua_State *), + lce__index(lua_State *), lce__newindex(lua_State *), + lbuffer_check_global(lua_State *), lbuffer_delete(lua_State *), + lbuffer_new(lua_State *), lbuffer_text_range(lua_State *), + lview_split(lua_State *), lview_unsplit(lua_State *), + lgui_dialog(lua_State *), lgui_get_split_table(lua_State *), + lgui_goto_view(lua_State *), lview_goto_buffer(lua_State *), + lgui_gtkmenu(lua_State *), lstring_iconv(lua_State *), + lquit(lua_State *), lreset(lua_State *), ltimeout(lua_State *), + lfind_focus(lua_State *), lfind_next(lua_State *), + lfind_prev(lua_State *), lfind_replace(lua_State *), + lfind_replace_all(lua_State *), + lce_focus(lua_State *), lce_show_completions(lua_State *); /******************************************************************************/ /******************************* GUI Interface ********************************/ @@ -168,8 +139,8 @@ static int l_cf_buffer_delete(lua_State *), l_cf_buffer_text_range(lua_State *), /** * Runs Textadept in Linux or Mac. - * Inits the Lua State, creates the user interface, loads the core/init.lua - * script, and also loads init.lua. + * Inits the Lua state, creates the user interface, and then runs core/init.lua + * followed by init.lua. * @param argc The number of command line params. * @param argv The array of command line params. */ @@ -182,8 +153,7 @@ int main(int argc, char **argv) { CFStringRef path = CFURLCopyFileSystemPath(bundle, kCFURLPOSIXPathStyle); const char *p = CFStringGetCStringPtr(path, kCFStringEncodingMacRoman); textadept_home = g_strconcat(p, "/Contents/Resources/", NULL); - CFRelease(path); - CFRelease(bundle); + CFRelease(path), CFRelease(bundle); } else textadept_home = calloc(1, 1); #elif __BSD__ textadept_home = malloc(FILENAME_MAX); @@ -194,9 +164,9 @@ int main(int argc, char **argv) { char *last_slash = strrchr(textadept_home, G_DIR_SEPARATOR); if (last_slash) *last_slash = '\0'; gtk_init(&argc, &argv); - if (!l_init(argc, argv, FALSE)) return 1; - create_ui(); - l_load_script("init.lua"); + if (lua = lua_open(), !lL_init(lua, argc, argv, FALSE)) return 1; + new_window(); + lL_dofile(lua, "init.lua"); gtk_main(); free(textadept_home); return 0; @@ -207,8 +177,7 @@ int main(int argc, char **argv) { * Runs Textadept in Windows. * @see main */ -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, - LPSTR lpCmdLine, int nCmdShow) { +int WINAPI WinMain(HINSTANCE _, HINSTANCE __, LPSTR lpCmdLine, int ___) { textadept_home = malloc(FILENAME_MAX); GetModuleFileName(0, textadept_home, FILENAME_MAX); return main(1, &lpCmdLine); @@ -216,17 +185,12 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, #endif /** - * Creates the user interface. - * The UI consists of: - * - A menubar initially hidden and empty. It should be populated by script - * and then shown. - * - A frame for Scintilla views. - * - A find text frame initially hidden. - * - A command entry initially hidden. This entry accepts and runs Lua code - * in the current Lua state. - * - Two status bars: one for notifications, the other for document status. + * Creates the Textadept window. + * The window contains a menubar, frame for Scintilla views, hidden find box, + * hidden command entry, and two status bars: one for notifications and the + * other for buffer status. */ -void create_ui() { +static void new_window() { GList *icon_list = NULL; const char *icons[] = { "16x16", "32x32", "48x48", "64x64", "128x128" }; for (int i = 0; i < 5; i++) { @@ -266,18 +230,18 @@ void create_ui() { GtkWidget *view = new_view(0); gtk_box_pack_start(GTK_BOX(hbox), view, TRUE, TRUE, 0); - GtkWidget *find = find_create_ui(); + GtkWidget *find = new_findbox(); gtk_box_pack_start(GTK_BOX(vbox), find, FALSE, FALSE, 5); command_entry = gtk_entry_new(); gtk_widget_set_name(command_entry, "textadept-command-entry"); - signal(command_entry, "activate", c_activated); + signal(command_entry, "activate", c_activate); signal(command_entry, "key-press-event", c_keypress); gtk_box_pack_start(GTK_BOX(vbox), command_entry, FALSE, FALSE, 0); command_entry_completion = gtk_entry_completion_new(); - signal(command_entry_completion, "match-selected", cc_match_selected); - gtk_entry_completion_set_match_func(command_entry_completion, cc_match_func, + signal(command_entry_completion, "match-selected", cc_matchselected); + gtk_entry_completion_set_match_func(command_entry_completion, cc_matchfunc, NULL, NULL); gtk_entry_completion_set_popup_set_width(command_entry_completion, FALSE); gtk_entry_completion_set_text_column(command_entry_completion, 0); @@ -306,96 +270,86 @@ void create_ui() { gtk_widget_hide(menubar); // hide initially gtk_widget_hide(findbox); // hide initially gtk_widget_hide(command_entry); // hide initially - gtk_widget_grab_focus(view); } /** * Creates a new Scintilla view. - * The Scintilla view is the GTK widget that displays a Scintilla buffer. * Generates a 'view_new' event. - * @param buffer_id A Scintilla buffer ID to load into the new window. If NULL, - * creates a new Scintilla buffer and loads it into the new window. - * @return the Scintilla view. - * @see l_add_view + * @param doc The document to load in the new view. Almost never zero, except + * for the first Scintilla view created, in which there is no doc pointer. + * @return Scintilla view + * @see lL_addview */ -GtkWidget *new_view(sptr_t buffer_id) { +static GtkWidget *new_view(sptr_t doc) { GtkWidget *view = scintilla_new(); gtk_widget_set_size_request(view, 1, 1); // minimum size SS(view, SCI_USEPOPUP, 0, 0); - signal(view, SCINTILLA_NOTIFY, s_notification); + signal(view, SCINTILLA_NOTIFY, s_notify); signal(view, "command", s_command); signal(view, "key-press-event", s_keypress); signal(view, "button-press-event", s_buttonpress); - l_add_view(view); + lL_addview(lua, view); gtk_widget_grab_focus(view); focused_view = view; - if (buffer_id) { - SS(view, SCI_SETDOCPOINTER, 0, buffer_id); - l_set_buffer_global(view); - } else new_buffer(view, FALSE, TRUE); - l_set_view_global(view); - l_emit_event("view_new", -1); + if (doc) { + SS(view, SCI_SETDOCPOINTER, 0, doc); + l_setglobaldoc(lua, doc); + } else new_buffer(SS(view, SCI_GETDOCPOINTER, 0, 0)); + l_setglobalview(lua, view); + lL_event(lua, "view_new", -1); return view; } /** * Removes a Scintilla view. * @param view The Scintilla view to remove. - * @see l_remove_view + * @see lL_removeview */ -void remove_view(GtkWidget *view) { - l_remove_view(view); +static void delete_view(GtkWidget *view) { + lL_removeview(lua, view); gtk_widget_destroy(view); } /** - * Creates a new Scintilla buffer for a newly created Scintilla view. - * Generates a 'buffer_new' event. - * @param view The Scintilla view to associate the buffer with. - * @param create Flag indicating whether or not to create a buffer. If FALSE, - * the Scintilla view already has a buffer associated with it (typically - * because new_view was passed a non-NULL buffer_id). - * @param addref Flag indicating whether or not to add a reference to the buffer - * in the Scintilla view when create is FALSE. This is necessary for creating - * Scintilla views in split views. If a buffer appears in two separate - * Scintilla views, that buffer should have multiple references so when one - * Scintilla view closes, the buffer is not deleted because its reference - * count is not zero. - * @see l_add_buffer + * Creates a new Scintilla document and adds it to the Lua state. + * Generates 'buffer_before_switch' and 'buffer_new' events. + * @param doc Almost always zero, except for the first Scintilla view created, + * in which its doc pointer would be given here. + * @see lL_adddoc */ -void new_buffer(GtkWidget *view, int create, int addref) { - sptr_t doc; - doc = SS(view, SCI_GETDOCPOINTER, 0, 0); - if (create) { // create the new document - doc = SS(view, SCI_CREATEDOCUMENT, 0, 0); - l_emit_event("buffer_before_switch", -1); - l_goto_buffer(focused_view, l_add_buffer(doc), TRUE); - } else if (addref) { - l_add_buffer(doc); - SS(view, SCI_ADDREFDOCUMENT, 0, doc); +static void new_buffer(sptr_t doc) { + if (!doc) { // create the new document + doc = SS(focused_view, SCI_CREATEDOCUMENT, 0, 0); + lL_event(lua, "buffer_before_switch", -1); + lL_adddoc(lua, doc); + lL_gotodoc(lua, focused_view, -1, FALSE); + } else { + // The first Scintilla window already has a pre-created buffer. + lL_adddoc(lua, doc); + SS(focused_view, SCI_ADDREFDOCUMENT, 0, doc); } - l_set_buffer_global(view); - l_emit_event("buffer_new", -1); + l_setglobaldoc(lua, doc); + lL_event(lua, "buffer_new", -1); } /** * Removes the Scintilla buffer from the current Scintilla view. - * @param doc The Scintilla buffer ID to remove. - * @see l_remove_buffer + * @param doc The Scintilla document. + * @see lL_removedoc */ -void remove_buffer(sptr_t doc) { - l_remove_buffer(doc); +static void delete_buffer(sptr_t doc) { + lL_removedoc(lua, doc); SS(focused_view, SCI_RELEASEDOCUMENT, 0, doc); } /** - * Splits a Scintilla view into two windows separated by a GTK pane. - * The buffer in the original pane is also shown in the new pane. + * Splits the given Scintilla view into two views. + * The new view shows the same document as the original one. * @param view The Scintilla view to split. - * @param vertical Flag indicating whether to split the window vertically or + * @param vertical Flag indicating whether to split the view vertically or * horozontally. */ -void split_view(GtkWidget *view, int vertical) { +static void split_view(GtkWidget *view, int vertical) { g_object_ref(view); int first_line = SS(view, SCI_GETFIRSTVISIBLELINE, 0, 0); int current_pos = SS(view, SCI_GETCURRENTPOS, 0, 0); @@ -405,53 +359,50 @@ void split_view(GtkWidget *view, int vertical) { int middle = (vertical ? allocation.width : allocation.height) / 2; sptr_t curdoc = SS(view, SCI_GETDOCPOINTER, 0, 0); - GtkWidget *newview = new_view(curdoc); + GtkWidget *view2 = new_view(curdoc); GtkWidget *parent = gtk_widget_get_parent(view); gtk_container_remove(GTK_CONTAINER(parent), view); GtkWidget *pane = vertical ? gtk_hpaned_new() : gtk_vpaned_new(); - gtk_paned_add1(GTK_PANED(pane), view); - gtk_paned_add2(GTK_PANED(pane), newview); + gtk_paned_add1(GTK_PANED(pane), view), gtk_paned_add2(GTK_PANED(pane), view2); gtk_container_add(GTK_CONTAINER(parent), pane); gtk_paned_set_position(GTK_PANED(pane), middle); gtk_widget_show_all(pane); - gtk_widget_grab_focus(newview); + gtk_widget_grab_focus(view2); - SS(newview, SCI_SETSEL, anchor, current_pos); - int new_first_line = SS(newview, SCI_GETFIRSTVISIBLELINE, 0, 0); - SS(newview, SCI_LINESCROLL, first_line - new_first_line, 0); + SS(view2, SCI_SETSEL, anchor, current_pos); + int new_first_line = SS(view2, SCI_GETFIRSTVISIBLELINE, 0, 0); + SS(view2, SCI_LINESCROLL, first_line - new_first_line, 0); g_object_unref(view); } /** - * For a given GTK pane, remove the Scintilla views inside it recursively. + * Remove all Scintilla views from the given pane and delete them. * @param pane The GTK pane to remove Scintilla views from. - * @see remove_view + * @see delete_view */ -void remove_views_in_pane(GtkWidget *pane) { +static void remove_views_from_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_views_in_pane(child1) : remove_view(child1); - GTK_IS_PANED(child2) ? remove_views_in_pane(child2) : remove_view(child2); + GTK_IS_PANED(child1) ? remove_views_from_pane(child1) : delete_view(child1); + GTK_IS_PANED(child2) ? remove_views_from_pane(child2) : delete_view(child2); } /** - * Unsplits the pane a given Scintilla view is in and keeps that window. - * If the pane to discard contains other Scintilla views, they are removed - * recursively. + * Unsplits the pane a given Scintilla view is in and keeps the view. + * All views in the other pane are deleted. * @param view The Scintilla view to keep when unsplitting. - * @see remove_views_in_pane - * @see remove_view + * @see remove_views_from_pane + * @see delete_view */ -int unsplit_view(GtkWidget *view) { +static int unsplit_view(GtkWidget *view) { GtkWidget *pane = gtk_widget_get_parent(view); if (!GTK_IS_PANED(pane)) return FALSE; GtkWidget *other = gtk_paned_get_child1(GTK_PANED(pane)); if (other == view) other = gtk_paned_get_child2(GTK_PANED(pane)); - g_object_ref(view); - g_object_ref(other); + g_object_ref(view), g_object_ref(other); gtk_container_remove(GTK_CONTAINER(pane), view); gtk_container_remove(GTK_CONTAINER(pane), other); - GTK_IS_PANED(other) ? remove_views_in_pane(other) : remove_view(other); + GTK_IS_PANED(other) ? remove_views_from_pane(other) : delete_view(other); GtkWidget *parent = gtk_widget_get_parent(pane); gtk_container_remove(GTK_CONTAINER(parent), pane); if (GTK_IS_PANED(parent)) { @@ -462,73 +413,40 @@ int unsplit_view(GtkWidget *view) { } else gtk_container_add(GTK_CONTAINER(parent), view); gtk_widget_show_all(parent); gtk_widget_grab_focus(GTK_WIDGET(view)); - g_object_unref(view); - g_object_unref(other); + g_object_unref(view), g_object_unref(other); return TRUE; } -/** - * Sets a user-defined GTK menubar and displays it. - * @param new_menubar The GTK menubar. - * @see l_gui_mt_newindex - */ -void set_menubar(GtkWidget *new_menubar) { - GtkWidget *vbox = gtk_widget_get_parent(menubar); - gtk_container_remove(GTK_CONTAINER(vbox), menubar); - menubar = new_menubar; - gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); - gtk_box_reorder_child(GTK_BOX(vbox), menubar, 0); - gtk_widget_show_all(menubar); -#if __OSX__ - ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(menubar)); - gtk_widget_hide(menubar); -#endif -} - -/** - * Sets the notification statusbar text. - * @param text The text to display. - * @param bar Statusbar. 0 for statusbar, 1 for docstatusbar. - */ -void set_statusbar_text(const char *text, int bar) { - if (!statusbar[0] || !statusbar[1]) return; // unavailable on startup - gtk_statusbar_pop(GTK_STATUSBAR(statusbar[bar]), 0); - gtk_statusbar_push(GTK_STATUSBAR(statusbar[bar]), 0, text); -} - /******************************************************************************/ /************************* GUI Notifications/Signals **************************/ /******************************************************************************/ /** - * Helper function for switching the focused view to the given one. + * Change focus to the given Scintilla view. + * Generates 'view_before_switch' and 'view_after_switch' events. * @param view The Scintilla view to focus. - * @see s_notification - * @see s_command - * @see l_goto_view */ -static void switch_to_view(GtkWidget *view) { - if (!closing) l_emit_event("view_before_switch", -1); +static void goto_view(GtkWidget *view) { + if (!closing) lL_event(lua, "view_before_switch", -1); focused_view = view; - l_set_view_global(view); - l_set_buffer_global(view); - if (!closing) l_emit_event("view_after_switch", -1); + l_setglobalview(lua, view); + l_setglobaldoc(lua, SS(view, SCI_GETDOCPOINTER, 0, 0)); + if (!closing) lL_event(lua, "view_after_switch", -1); } /** * Signal for a Scintilla notification. */ -static void s_notification(GtkWidget *view, gint wParam, gpointer lParam, - gpointer udata) { +static void s_notify(GtkWidget *view, gint _, gpointer lParam, gpointer __) { struct SCNotification *n = (struct SCNotification *)lParam; if (focused_view == view || n->nmhdr.code == SCN_URIDROPPED) { - if (focused_view != view) switch_to_view(view); - l_emit_scnotification(n); + if (focused_view != view) goto_view(view); + lL_notify(lua, n); } else if (n->nmhdr.code == SCN_SAVEPOINTLEFT) { GtkWidget *prev = focused_view; - switch_to_view(view); - l_emit_scnotification(n); - switch_to_view(prev); // do not let a split view steal focus + goto_view(view); + lL_notify(lua, n); + goto_view(prev); // do not let a split view steal focus } } @@ -536,35 +454,34 @@ static void s_notification(GtkWidget *view, gint wParam, gpointer lParam, * Signal for a Scintilla command. * Currently handles SCEN_SETFOCUS. */ -static void s_command(GtkWidget *view, gint wParam, gpointer lParam, - gpointer udata) { - if (wParam >> 16 == SCEN_SETFOCUS) switch_to_view(view); +static void s_command(GtkWidget *view, gint wParam, gpointer _, gpointer __) { + if (wParam >> 16 == SCEN_SETFOCUS) goto_view(view); } /** * Signal for a Scintilla keypress. - * Collects the modifier states as flags and calls Lua to handle the keypress. */ -static gbool s_keypress(GtkWidget *view, GdkEventKey *event, gpointer udata) { - return l_emit_event_key("keypress", event) ? TRUE : FALSE; +static gboolean s_keypress(GtkWidget *view, GdkEventKey *event, gpointer _) { + return lL_event(lua, "keypress", LUA_TNUMBER, event->keyval, LUA_TBOOLEAN, + event->state & GDK_SHIFT_MASK, LUA_TBOOLEAN, + event->state & GDK_CONTROL_MASK, LUA_TBOOLEAN, + event->state & GDK_MOD1_MASK, LUA_TBOOLEAN, + event->state & GDK_MOD5_MASK, -1); } /** * Signal for a Scintilla mouse click. - * If it is a right-click, popup a context menu. - * @see l_gui_popup_context_menu */ -static gbool s_buttonpress(GtkWidget *view, GdkEventButton *event, - gpointer udata) { - if (event->type != GDK_BUTTON_PRESS || event->button != 3) return FALSE; - l_gui_popup_context_menu(event); - return TRUE; +static gboolean s_buttonpress(GtkWidget*_, GdkEventButton *event, gpointer __) { + if (event->type == GDK_BUTTON_PRESS && event->button == 3) + return (lL_showcontextmenu(lua, event), TRUE); + return FALSE; } /** * Signal for a Textadept window focus change. */ -static gbool w_focus(GtkWidget *window, GdkEventFocus *event, gpointer udata) { +static gboolean w_focus(GtkWidget*_, GdkEventFocus *event, gpointer __) { if (focused_view && !gtk_widget_has_focus(focused_view)) gtk_widget_grab_focus(focused_view); return FALSE; @@ -573,9 +490,9 @@ static gbool w_focus(GtkWidget *window, GdkEventFocus *event, gpointer udata) { /** * Signal for a Textadept keypress. * Currently handled keypresses: - * - Escape - hides the search frame if it's open. + * - Escape: hides the find box if it is open. */ -static gbool w_keypress(GtkWidget *window, GdkEventKey *event, gpointer udata) { +static gboolean w_keypress(GtkWidget*_, GdkEventKey *event, gpointer __) { if (event->keyval == 0xff1b && gtk_widget_get_visible(findbox) && !gtk_widget_has_focus(command_entry)) { gtk_widget_hide(findbox); @@ -586,13 +503,13 @@ static gbool w_keypress(GtkWidget *window, GdkEventKey *event, gpointer udata) { /** * Signal for exiting Textadept. - * Closes the Lua State and releases resources. * Generates a 'quit' event. + * Closes the Lua state and releases resources. * @see l_close */ -static gbool w_exit(GtkWidget *window, GdkEventAny *event, gpointer udata) { - if (!l_emit_event("quit", -1)) return TRUE; - l_close(); +static gboolean w_exit(GtkWidget*_, GdkEventAny*__, gpointer ___) { + if (!lL_event(lua, "quit", -1)) return TRUE; + l_close(lua); scintilla_release_resources(); gtk_main_quit(); return FALSE; @@ -601,9 +518,9 @@ static gbool w_exit(GtkWidget *window, GdkEventAny *event, gpointer udata) { #if __OSX__ /** * Signal for an Open Document AppleEvent. - * Generates a 'appleevent_odoc' event for each document sent. + * Generates an 'appleevent_odoc' event for each document sent. */ -static OSErr w_ae_open(const AppleEvent *event, AppleEvent *reply, long ref) { +static OSErr w_ae_open(const AppleEvent *event, AppleEvent*_, long __) { AEDescList file_list; if (AEGetParamDesc(event, keyDirectObject, typeAEList, &file_list) == noErr) { long count = 0; @@ -616,9 +533,8 @@ static OSErr w_ae_open(const AppleEvent *event, AppleEvent *reply, long ref) { if (url) { CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle); const char *p = CFStringGetCStringPtr(path, kCFStringEncodingMacRoman); - l_emit_event("appleevent_odoc", LUA_TSTRING, p, -1); - CFRelease(path); - CFRelease(url); + lL_event(lua, "appleevent_odoc", LUA_TSTRING, p, -1); + CFRelease(path), CFRelease(url); } } AEDisposeDesc(&file_list); @@ -628,10 +544,8 @@ static OSErr w_ae_open(const AppleEvent *event, AppleEvent *reply, long ref) { /** * Signal for a Quit Application AppleEvent. - * Calls the signal for exiting Textadept. - * @see w_exit */ -static OSErr w_ae_quit(const AppleEvent *event, AppleEvent *reply, long ref) { +static OSErr w_ae_quit(const AppleEvent*_, AppleEvent*__, long ___) { return w_exit(NULL, NULL, NULL) ? (OSErr) noErr : errAEEventNotHandled; } #endif @@ -644,11 +558,10 @@ static OSErr w_ae_quit(const AppleEvent *event, AppleEvent *reply, long ref) { gtk_table_attach(GTK_TABLE(findbox), w, x1, x2, y1, y2, xo, yo, xp, yp) #define ao_expand (GtkAttachOptions)(GTK_EXPAND | GTK_FILL) #define ao_normal (GtkAttachOptions)(GTK_SHRINK | GTK_FILL) - /** - * Creates the Find/Replace text frame. + * Creates the Find box. */ -GtkWidget *find_create_ui() { +static GtkWidget *new_findbox() { findbox = gtk_table_new(2, 6, FALSE); find_store = gtk_list_store_new(1, G_TYPE_STRING); repl_store = gtk_list_store_new(1, G_TYPE_STRING); @@ -696,10 +609,10 @@ GtkWidget *find_create_ui() { attach(lua_opt, 5, 6, 0, 1, ao_normal, ao_normal, 5, 0); attach(in_files_opt, 5, 6, 1, 2, ao_normal, ao_normal, 5, 0); - signal(fnext_button, "clicked", find_button_clicked); - signal(fprev_button, "clicked", find_button_clicked); - signal(r_button, "clicked", find_button_clicked); - signal(ra_button, "clicked", find_button_clicked); + signal(fnext_button, "clicked", f_clicked); + signal(fprev_button, "clicked", f_clicked); + signal(r_button, "clicked", f_clicked); + signal(ra_button, "clicked", f_clicked); gtk_widget_set_can_default(fnext_button, TRUE); gtk_widget_set_can_focus(fnext_button, FALSE); @@ -714,24 +627,12 @@ GtkWidget *find_create_ui() { return findbox; } -/** - * Toggles the focus between the Find/Replace frame and the current Scintilla - * window. - */ -void find_toggle_focus() { - if (!gtk_widget_has_focus(findbox)) { - gtk_widget_show(findbox); - gtk_widget_grab_focus(find_entry); - gtk_widget_grab_default(fnext_button); - } else { - gtk_widget_grab_focus(focused_view); - gtk_widget_hide(findbox); - } -} +/******************************************************************************/ +/**************************** Find/Replace Signals ****************************/ +/******************************************************************************/ /** - * Adds the given text to the Find/Replace history list if it's not the first - * item. + * Adds the given text to the find/replace history list if it is not at the top. * @param text The text to add. * @param store The GtkListStore to add the text to. */ @@ -750,30 +651,25 @@ static void find_add_to_history(const char *text, GtkListStore *store) { } } -/******************************************************************************/ -/**************************** Find/Replace Signals ****************************/ -/******************************************************************************/ - /** - * Signal for a Find frame button click. - * Performs the appropriate action depending on the button clicked. + * Signal for a find box button click. */ -static void find_button_clicked(GtkWidget *button, gpointer udata) { +static void f_clicked(GtkWidget *button, gpointer _) { const char *find_text = gtk_entry_get_text(GTK_ENTRY(find_entry)); const char *repl_text = gtk_entry_get_text(GTK_ENTRY(replace_entry)); if (strlen(find_text) == 0) return; if (button == fnext_button || button == fprev_button) { find_add_to_history(find_text, find_store); - l_emit_event("find", LUA_TSTRING, find_text, LUA_TBOOLEAN, - button == fnext_button, -1); + lL_event(lua, "find", LUA_TSTRING, find_text, LUA_TBOOLEAN, + button == fnext_button, -1); } else { find_add_to_history(repl_text, repl_store); if (button == r_button) { - l_emit_event("replace", LUA_TSTRING, repl_text, -1); - l_emit_event("find", LUA_TSTRING, find_text, LUA_TBOOLEAN, 1, -1); + lL_event(lua, "replace", LUA_TSTRING, repl_text, -1); + lL_event(lua, "find", LUA_TSTRING, find_text, LUA_TBOOLEAN, 1, -1); } else - l_emit_event("replace_all", LUA_TSTRING, find_text, LUA_TSTRING, - repl_text, -1); + lL_event(lua, "replace_all", LUA_TSTRING, find_text, LUA_TSTRING, + repl_text, -1); } } @@ -782,49 +678,29 @@ static void find_button_clicked(GtkWidget *button, gpointer udata) { /******************************************************************************/ /** - * Toggles focus between a Scintilla view and the Command Entry. - * When the entry is visible, the statusbars are temporarily hidden. - */ -void ce_toggle_focus() { - if (!gtk_widget_has_focus(command_entry)) { - gtk_widget_show(command_entry); - gtk_widget_grab_focus(command_entry); - } else { - gtk_widget_hide(command_entry); - gtk_widget_grab_focus(focused_view); - } -} - -/** - * Sets every item in the Command Entry Model to be a match. - * For each attempted completion, the Command Entry Model is filled with the - * results from a call to Lua to make a list of possible completions. Therefore, - * every item in the list is valid. + * The match function for the command entry. + * Since the completion list is filled by Lua, every item is a "match". */ -static int cc_match_func(GtkEntryCompletion *entry, const char *key, - GtkTreeIter *iter, gpointer udata) { return 1; } +static int cc_matchfunc(GtkEntryCompletion*_, const char *__, GtkTreeIter*___, + gpointer ____) { return 1; } /** - * Enters the requested completion text into the Command Entry. - * The last word at the cursor is replaced with the completion. A word consists - * of any alphanumeric character or underscore. + * Replaces the current word (consisting of alphanumeric and underscore + * characters) with the match text. */ -static gbool cc_match_selected(GtkEntryCompletion *entry, GtkTreeModel *model, - GtkTreeIter *iter, gpointer udata) { - const char *entry_text = gtk_entry_get_text(GTK_ENTRY(command_entry)); - const char *p = entry_text + strlen(entry_text) - 1; - while (g_ascii_isalnum(*p) || *p == '_') { +static gboolean cc_matchselected(GtkEntryCompletion*_, GtkTreeModel *model, + GtkTreeIter *iter, gpointer __) { + const char *text = gtk_entry_get_text(GTK_ENTRY(command_entry)), *p; + for (p = text + strlen(text) - 1; g_ascii_isalnum(*p) || *p == '_'; p--) g_signal_emit_by_name(G_OBJECT(command_entry), "move-cursor", GTK_MOVEMENT_VISUAL_POSITIONS, -1, TRUE, 0); - p--; - } - if (p < entry_text + strlen(entry_text) - 1) + if (p < text + strlen(text) - 1) g_signal_emit_by_name(G_OBJECT(command_entry), "backspace", 0); - char *text; - gtk_tree_model_get(model, iter, 0, &text, -1); - g_signal_emit_by_name(G_OBJECT(command_entry), "insert-at-cursor", text, 0); - g_free(text); + char *match; + gtk_tree_model_get(model, iter, 0, &match, -1); + g_signal_emit_by_name(G_OBJECT(command_entry), "insert-at-cursor", match, 0); + g_free(match); gtk_list_store_clear(cc_store); return TRUE; @@ -837,411 +713,328 @@ static gbool cc_match_selected(GtkEntryCompletion *entry, GtkTreeModel *model, /** * Signal for the 'enter' key being pressed in the Command Entry. */ -static void c_activated(GtkWidget *entry, gpointer udata) { - l_emit_event("command_entry_command", LUA_TSTRING, - gtk_entry_get_text(GTK_ENTRY(entry)), -1); +static void c_activate(GtkWidget *entry, gpointer _) { + lL_event(lua, "command_entry_command", LUA_TSTRING, + gtk_entry_get_text(GTK_ENTRY(entry)), -1); } /** * Signal for a keypress inside the Command Entry. */ -static gbool c_keypress(GtkWidget *entry, GdkEventKey *event, gpointer udata) { - return l_emit_event_key("command_entry_keypress", event); +static gboolean c_keypress(GtkWidget*_, GdkEventKey *event, gpointer __) { + return lL_event(lua, "command_entry_keypress", LUA_TNUMBER, event->keyval, + LUA_TBOOLEAN, event->state & GDK_SHIFT_MASK, LUA_TBOOLEAN, + event->state & GDK_CONTROL_MASK, LUA_TBOOLEAN, + event->state & GDK_MOD1_MASK, LUA_TBOOLEAN, + event->state & GDK_MOD5_MASK, -1); } /******************************************************************************/ /******************************** Lua Interface *******************************/ /******************************************************************************/ -#define l_openlib(l, n, f) { \ - lua_pushcfunction(l, f); \ - lua_pushstring(l, n); \ - lua_call(l, 1, 0); \ -} -#define l_archive(l, k) { \ - lua_pushstring(l, k); \ - lua_rawget(l, -2); \ - lua_setfield(l, LUA_REGISTRYINDEX, k); \ +#define lL_openlib(l, n, f) \ + (lua_pushcfunction(l, f), lua_pushstring(l, n), lua_call(l, 1, 0)) +#define l_setcfunction(l, n, k, f) \ + (lua_pushcfunction(l, f), lua_setfield(l, (n > 0) ? n : n - 1, k)) +#define l_setmetatable(l, n, k, i, ni) { \ + if (luaL_newmetatable(l, k)) { \ + l_setcfunction(l, -1, "__index", i); \ + l_setcfunction(l, -1, "__newindex", ni); \ + } \ + lua_setmetatable(l, (n > 0) ? n : n - 1); \ } /** - * Inits or re-inits the Lua State. + * Initializes or re-initializes the Lua state. * Populates the state with global variables and functions, then runs the * 'core/init.lua' script. * @param argc The number of command line parameters. * @param argv The array of command line parameters. - * @param reinit Flag indicating whether or not to reinitialize the Lua State. - * @return TRUE on success, FALSE on failure. + * @param reinit Flag indicating whether or not to reinitialize the Lua state. + * @return TRUE on success, FALSE otherwise. */ -int l_init(int argc, char **argv, int reinit) { +static int lL_init(lua_State *L, int argc, char **argv, int reinit) { if (!reinit) { - lua = lua_open(); - lua_newtable(lua); - for (int i = 0; i < argc; i++) { - lua_pushstring(lua, argv[i]); - lua_rawseti(lua, -2, i); - } - lua_setfield(lua, LUA_REGISTRYINDEX, "arg"); - lua_newtable(lua); - lua_setfield(lua, LUA_REGISTRYINDEX, "buffers"); - lua_newtable(lua); - lua_setfield(lua, LUA_REGISTRYINDEX, "views"); + lua_newtable(L); + for (int i = 0; i < argc; i++) + lua_pushstring(L, argv[i]), lua_rawseti(L, -2, i); + lua_setfield(L, LUA_REGISTRYINDEX, "ta_arg"); + lua_newtable(L), lua_setfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_newtable(L), lua_setfield(L, LUA_REGISTRYINDEX, "ta_views"); } else { // clear package.loaded and _G - lua_getglobal(lua, "package"); - lua_getfield(lua, -1, "loaded"); - clear_table(lua, lua_gettop(lua)); - lua_pop(lua, 2); // package and package.loaded - clear_table(lua, LUA_GLOBALSINDEX); + lua_getglobal(L, "package"), lua_getfield(L, -1, "loaded"); + lL_cleartable(L, lua_gettop(L)); + lua_pop(L, 2); // package and package.loaded + lL_cleartable(L, LUA_GLOBALSINDEX); } - luaL_openlibs(lua); - l_openlib(lua, "lpeg", luaopen_lpeg); - l_openlib(lua, "lfs", luaopen_lfs); - - lua_newtable(lua); - lua_newtable(lua); - l_cfunc(lua, l_cf_find_next, "find_next"); - l_cfunc(lua, l_cf_find_prev, "find_prev"); - l_cfunc(lua, l_cf_find_focus, "focus"); - l_cfunc(lua, l_cf_find_replace, "replace"); - l_cfunc(lua, l_cf_find_replace_all, "replace_all"); - l_mt(lua, "_find_mt", l_find_mt_index, l_find_mt_newindex); - lua_setfield(lua, -2, "find"); - lua_newtable(lua); - l_cfunc(lua, l_cf_ce_focus, "focus"); - l_cfunc(lua, l_cf_ce_show_completions, "show_completions"); - l_mt(lua, "_ce_mt", l_ce_mt_index, l_ce_mt_newindex); - lua_setfield(lua, -2, "command_entry"); - l_cfunc(lua, l_cf_gui_dialog, "dialog"); - l_cfunc(lua, l_cf_gui_get_split_table, "get_split_table"); - l_cfunc(lua, l_cf_gui_goto_view, "goto_view"); - l_cfunc(lua, l_cf_gui_gtkmenu, "gtkmenu"); - l_mt(lua, "_gui_mt", l_gui_mt_index, l_gui_mt_newindex); - lua_setglobal(lua, "gui"); - - lua_getglobal(lua, "_G"); - l_cfunc(lua, l_cf_buffer_new, "new_buffer"); - l_cfunc(lua, l_cf_quit, "quit"); - l_cfunc(lua, l_cf_reset, "reset"); - l_cfunc(lua, l_cf_timeout, "timeout"); - lua_pop(lua, 1); // _G - - lua_getglobal(lua, "string"); - l_cfunc(lua, l_cf_string_iconv, "iconv"); - lua_pop(lua, 1); // string - - lua_getfield(lua, LUA_REGISTRYINDEX, "arg"); - lua_setglobal(lua, "arg"); - lua_getfield(lua, LUA_REGISTRYINDEX, "buffers"); - lua_setglobal(lua, "_BUFFERS"); - lua_getfield(lua, LUA_REGISTRYINDEX, "views"); - lua_setglobal(lua, "_VIEWS"); - lua_pushstring(lua, textadept_home); - lua_setglobal(lua, "_HOME"); + luaL_openlibs(L); + lL_openlib(L, "lpeg", luaopen_lpeg); + lL_openlib(L, "lfs", luaopen_lfs); + + lua_newtable(L); + lua_newtable(L); + l_setcfunction(L, -1, "find_next", lfind_next); + l_setcfunction(L, -1, "find_prev", lfind_prev); + l_setcfunction(L, -1, "focus", lfind_focus); + l_setcfunction(L, -1, "replace", lfind_replace); + l_setcfunction(L, -1, "replace_all", lfind_replace_all); + l_setmetatable(L, -1, "ta_find", lfind__index, lfind__newindex); + lua_setfield(L, -2, "find"); + lua_newtable(L); + l_setcfunction(L, -1, "focus", lce_focus); + l_setcfunction(L, -1, "show_completions", lce_show_completions); + l_setmetatable(L, -1, "ta_command_entry", lce__index, lce__newindex); + lua_setfield(L, -2, "command_entry"); + l_setcfunction(L, -1, "dialog", lgui_dialog); + l_setcfunction(L, -1, "get_split_table", lgui_get_split_table); + l_setcfunction(L, -1, "goto_view", lgui_goto_view); + l_setcfunction(L, -1, "gtkmenu", lgui_gtkmenu); + l_setmetatable(L, -1, "ta_gui", lgui__index, lgui__newindex); + lua_setglobal(L, "gui"); + + lua_getglobal(L, "_G"); + l_setcfunction(L, -1, "new_buffer", lbuffer_new); + l_setcfunction(L, -1, "quit", lquit); + l_setcfunction(L, -1, "reset", lreset); + l_setcfunction(L, -1, "timeout", ltimeout); + lua_pop(L, 1); // _G + + lua_getglobal(L, "string"); + l_setcfunction(L, -1, "iconv", lstring_iconv); + lua_pop(L, 1); // string + + lua_getfield(L, LUA_REGISTRYINDEX, "ta_arg"), lua_setglobal(L, "arg"); + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_setglobal(L, "_BUFFERS"); + lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"), lua_setglobal(L, "_VIEWS"); + lua_pushstring(L, textadept_home), lua_setglobal(L, "_HOME"); #if __WIN32__ - lua_pushboolean(lua, 1); - lua_setglobal(lua, "WIN32"); + lua_pushboolean(L, 1), lua_setglobal(L, "WIN32"); #elif __OSX__ - lua_pushboolean(lua, 1); - lua_setglobal(lua, "OSX"); + lua_pushboolean(L, 1), lua_setglobal(L, "OSX"); #endif const char *charset = 0; g_get_charset(&charset); - lua_pushstring(lua, charset); - lua_setglobal(lua, "_CHARSET"); - - if (l_load_script("core/init.lua")) { - lua_getglobal(lua, "_SCINTILLA"); - l_archive(lua, "constants"); - l_archive(lua, "functions"); - l_archive(lua, "properties"); - lua_pop(lua, 1); // _SCINTILLA + lua_pushstring(L, charset), lua_setglobal(L, "_CHARSET"); + + if (lL_dofile(L, "core/init.lua")) { + lua_getglobal(L, "_SCINTILLA"); + lua_getfield(L, -1, "constants"); + lua_setfield(L, LUA_REGISTRYINDEX, "ta_constants"); + lua_getfield(L, -1, "functions"); + lua_setfield(L, LUA_REGISTRYINDEX, "ta_functions"); + lua_getfield(L, -1, "properties"); + lua_setfield(L, LUA_REGISTRYINDEX, "ta_properties"); + lua_pop(L, 1); // _SCINTILLA return TRUE; } - lua_close(lua); + lua_close(L); return FALSE; } /** - * Loads and runs a given Lua script. - * @param script_file The path of the Lua script relative to textadept_home. - * @return TRUE on success, FALSE otherwise. + * Loads and runs the given file. + * @param L The Lua state. + * @param filename The file name relative to textadept_home. + * @return 1 if there are no errors or 0 in case of errors. */ -int l_load_script(const char *script_file) { - char *script = g_strconcat(textadept_home, "/", script_file, NULL); - int retval = luaL_dofile(lua, script) == 0; - if (!retval) { - const char *errmsg = lua_tostring(lua, -1); - lua_settop(lua, 0); +static int lL_dofile(lua_State *L, const char *filename) { + char *file = g_strconcat(textadept_home, "/", filename, NULL); + int ok = (luaL_dofile(L, file) == 0); + if (!ok) { GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, - GTK_BUTTONS_OK, "%s\n", errmsg); + GTK_BUTTONS_OK, "%s\n", + lua_tostring(L, -1)); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); + lua_settop(L, 0); } - g_free(script); - return retval; + g_free(file); + return ok; } /** - * Checks a specified stack element to see if it is a Scintilla view and returns - * it as a GtkWidget. - * Throws an error if the check is not satisfied. - * @param lua The Lua State. - * @param narg Relative stack index to check for a Scintilla view. - * @return GtkWidget Scintilla view. + * Returns the view at the given acceptable index as a Scintilla view. + * @param L The Lua state. + * @param index Stack index of the view. + * @return Scintilla view */ -static GtkWidget *l_checkview(lua_State *lua, int narg) { - luaL_argcheck(lua, lua_istable(lua, narg), narg, "View expected"); - lua_pushstring(lua, "widget_pointer"); - lua_rawget(lua, (narg > 0) ? narg : narg - 1); - luaL_argcheck(lua, lua_islightuserdata(lua, -1), narg, "View expected"); - GtkWidget *view = l_togtkwidget(lua, -1); - lua_pop(lua, 1); // widget_pointer +static GtkWidget *l_toview(lua_State *L, int index) { + lua_getfield(L, index, "widget_pointer"); + GtkWidget *view = (GtkWidget *)lua_touserdata(L, -1); + lua_pop(L, 1); // widget pointer return view; } /** - * Adds a Scintilla view to the global '_VIEWS' table with a metatable. + * Adds the Scintilla view with a metatable to the 'views' registry table. + * @param L The Lua state. * @param view The Scintilla view to add. */ -void l_add_view(GtkWidget *view) { - lua_getfield(lua, LUA_REGISTRYINDEX, "views"); - lua_newtable(lua); - lua_pushlightuserdata(lua, (GtkWidget *)view); - lua_setfield(lua, -2, "widget_pointer"); - l_cfunc(lua, l_cf_view_focus, "focus"); - l_cfunc(lua, l_cf_view_goto_buffer, "goto_buffer"); - l_cfunc(lua, l_cf_view_split, "split"); - l_cfunc(lua, l_cf_view_unsplit, "unsplit"); - l_mt(lua, "_view_mt", l_view_mt_index, l_view_mt_newindex); - l_append(lua, -2); // pops table - lua_pop(lua, 1); // views +static void lL_addview(lua_State *L, GtkWidget *view) { + lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); + lua_newtable(L); + lua_pushlightuserdata(L, view); + lua_pushvalue(L, -1), lua_setfield(L, -3, "widget_pointer"); + l_setcfunction(L, -2, "goto_buffer", lview_goto_buffer); + l_setcfunction(L, -2, "split", lview_split); + l_setcfunction(L, -2, "unsplit", lview_unsplit); + l_setmetatable(L, -2, "ta_view", lview__index, lview__newindex); + // vs[userdata] = v, vs[#vs + 1] = v, vs[v] = #vs + lua_pushvalue(L, -2), lua_settable(L, -4); + lua_pushvalue(L, -1), lua_rawseti(L, -3, lua_objlen(L, -3) + 1); + lua_pushinteger(L, lua_objlen(L, -2)), lua_settable(L, -3); + lua_pop(L, 1); // views } /** - * Removes a Scintilla view from the global '_VIEWS' table. + * Removes the Scintilla view from the 'views' registry table. + * The view must have been previously added with lL_addview. + * @param L The Lua state. * @param view The Scintilla view to remove. + * @see lL_addview */ -void l_remove_view(GtkWidget *view) { - lua_newtable(lua); - lua_getfield(lua, LUA_REGISTRYINDEX, "views"); - lua_pushnil(lua); - while (lua_next(lua, -2)) - (view != l_checkview(lua, -1)) ? l_append(lua, -4) : lua_pop(lua, 1); - lua_pop(lua, 1); // views - lua_pushvalue(lua, -1); - lua_setfield(lua, LUA_REGISTRYINDEX, "views"); - lua_setglobal(lua, "_VIEWS"); -} - -/** - * Changes focus a Scintilla view in the global '_VIEWS' table. - * @param view The currently focused Scintilla view. - * @param n The index of the window in the '_VIEWS' table to focus. - * @param absolute Flag indicating whether or not the index specified in - * '_VIEWS' is absolute. If FALSE, focuses the window relative to the - * currently focused window for the given index. - * Throws an error if the view does not exist. - */ -void l_goto_view(GtkWidget *view, int n, int absolute) { - lua_getfield(lua, LUA_REGISTRYINDEX, "views"); - if (!absolute) { - unsigned int idx = 1; - lua_pushnil(lua); - while (lua_next(lua, -2)) - if (view == l_checkview(lua, -1)) { - idx = 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 { - luaL_argcheck(lua, n >= 0 && n <= lua_objlen(lua, -1), 1, - "no View exists at that index"); - lua_rawgeti(lua, -1, n); +static void lL_removeview(lua_State *L, GtkWidget *view) { + lua_newtable(L); + lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isnumber(L, -2) && view != l_toview(L, -1)) { + lua_getfield(L, -1, "widget_pointer"); + // vs[userdata] = v, vs[#vs + 1] = v, vs[v] = #vs + lua_pushvalue(L, -2), lua_rawseti(L, -6, lua_objlen(L, -6) + 1); + lua_pushvalue(L, -2), lua_settable(L, -6); + lua_pushinteger(L, lua_objlen(L, -4)), lua_settable(L, -5); + } else lua_pop(L, 1); // value } - view = l_checkview(lua, -1); - gtk_widget_grab_focus(view); - // gui.dialog() interferes with focus so gtk_widget_grab_focus() does not - // always work. If this is the case, ensure switch_to_view() is called. - if (!gtk_widget_has_focus(view)) switch_to_view(view); - lua_pop(lua, 2); // view table and views -} - -/** - * Sets the global 'view' variable to be the specified Scintilla view. - * @param view The Scintilla view to set 'view' to. - */ -void l_set_view_global(GtkWidget *view) { - lua_getfield(lua, LUA_REGISTRYINDEX, "views"); - lua_pushnil(lua); - while (lua_next(lua, -2)) - if (view == 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 + lua_pop(L, 1); // views + lua_pushvalue(L, -1), lua_setfield(L, LUA_REGISTRYINDEX, "ta_views"); + lua_setglobal(L, "_VIEWS"); } /** - * Checks a specified element to see if it is a buffer table and returns the - * Scintilla document pointer associated with it. - * Throws an error if the check is not satisfied. - * @param lua The Lua State. - * @param narg Relative stack index to check for a buffer table. + * Returns the buffer at the given acceptable index as a Scintilla document. + * @param L The Lua state. + * @param index Stack index of the buffer. + * @return Scintilla document */ -static sptr_t l_checkdocpointer(lua_State *lua, int narg) { - luaL_argcheck(lua, lua_istable(lua, narg), narg, "Buffer expected"); - lua_pushstring(lua, "doc_pointer"); - lua_rawget(lua, (narg > 0) ? narg : narg - 1); - luaL_argcheck(lua, lua_isnumber(lua, -1), narg, "Buffer expected"); - sptr_t doc = lua_tointeger(lua, -1); - lua_pop(lua, 1); // doc_pointer +static sptr_t l_todoc(lua_State *L, int index) { + lua_getfield(L, index, "doc_pointer"); + sptr_t doc = (sptr_t)lua_touserdata(L, -1); + lua_pop(L, 1); // doc_pointer return doc; } /** - * Adds a Scintilla document to the global '_BUFFERS' table with a metatable. + * Adds a Scintilla document with a metatable to the 'buffers' registry table. + * @param L The Lua state. * @param doc The Scintilla document to add. - * @return integer index of the new buffer in _BUFFERS. */ -int l_add_buffer(sptr_t doc) { - lua_getfield(lua, LUA_REGISTRYINDEX, "buffers"); - lua_newtable(lua); - lua_pushinteger(lua, doc); - lua_setfield(lua, -2, "doc_pointer"); - l_cfunc(lua, l_cf_buffer_delete, "delete"); - l_cfunc(lua, l_cf_buffer_text_range, "text_range"); - 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; +static void lL_adddoc(lua_State *L, sptr_t doc) { + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_newtable(L); + lua_pushlightuserdata(L, (sptr_t *)doc); // TODO: can this fail? + lua_pushvalue(L, -1), lua_setfield(L, -3, "doc_pointer"); + l_setcfunction(L, -2, "check_global", lbuffer_check_global); + l_setcfunction(L, -2, "delete", lbuffer_delete); + l_setcfunction(L, -2, "text_range", lbuffer_text_range); + l_setmetatable(L, -2, "ta_buffer", lbuf_property, lbuf_property); + // bs[userdata] = b, bs[#bs + 1] = b, bs[b] = #bs + lua_pushvalue(L, -2), lua_settable(L, -4); + lua_pushvalue(L, -1), lua_rawseti(L, -3, lua_objlen(L, -3) + 1); + lua_pushinteger(L, lua_objlen(L, -2)), lua_settable(L, -3); + lua_pop(L, 1); // buffers } /** - * Removes a Scintilla document from the global '_BUFFERS' table. - * If any views currently show the document to be removed, change the documents - * they show first. - * @param doc The Scintilla buffer to remove. + * Removes the Scintilla document from the 'buffers' registry table. + * The document must have been previously added with lL_adddoc. + * It is removed from any other views showing it first. Therefore, ensure the + * length of 'buffers' is more than one unless quitting the application. + * @param L The Lua state. + * @param doc The Scintilla document to remove. + * @see lL_adddoc */ -void l_remove_buffer(sptr_t doc) { - lua_getfield(lua, LUA_REGISTRYINDEX, "views"); - lua_pushnil(lua); - while (lua_next(lua, -2)) { - GtkWidget *view = l_checkview(lua, -1); - sptr_t that_doc = SS(view, SCI_GETDOCPOINTER, 0, 0); - if (that_doc == doc) l_goto_buffer(view, -1, FALSE); - lua_pop(lua, 1); // value +static void lL_removedoc(lua_State *L, sptr_t doc) { + lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isnumber(L, -2)) { + GtkWidget *view = l_toview(L, -1); + if (doc == SS(view, SCI_GETDOCPOINTER, 0, 0)) + lL_gotodoc(L, view, -1, TRUE); + } + lua_pop(L, 1); // value } - lua_pop(lua, 1); // views - lua_newtable(lua); - lua_getfield(lua, LUA_REGISTRYINDEX, "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 - lua_pushvalue(lua, -1); - lua_setfield(lua, LUA_REGISTRYINDEX, "buffers"); - lua_setglobal(lua, "_BUFFERS"); -} - -/** - * Retrieves the index in the global '_BUFFERS' table for a given Scintilla - * document. - * @param doc The Scintilla document to get the index of. - * @return int buffer index. - */ -unsigned int l_get_docpointer_index(sptr_t doc) { - lua_getfield(lua, LUA_REGISTRYINDEX, "buffers"); - unsigned int idx = 1; - lua_pushnil(lua); - while (lua_next(lua, -2)) - if (doc == l_checkdocpointer(lua, -1)) { - idx = 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; + lua_pop(L, 1); // views + lua_newtable(L); + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isnumber(L, -2) && doc != l_todoc(L, -1)) { + lua_getfield(L, -1, "doc_pointer"); + // bs[userdata] = b, bs[#bs + 1] = b, bs[b] = #bs + lua_pushvalue(L, -2), lua_rawseti(L, -6, lua_objlen(L, -6) + 1); + lua_pushvalue(L, -2), lua_settable(L, -6); + lua_pushinteger(L, lua_objlen(L, -4)), lua_settable(L, -5); + } else lua_pop(L, 1); // value + } + lua_pop(L, 1); // buffers + lua_pushvalue(L, -1), lua_setfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_setglobal(L, "_BUFFERS"); } /** - * Changes a Scintilla view's document to one in the global '_BUFFERS' table. - * Before doing so, it saves the scroll and caret positions in the current - * Scintilla document. Then when the new document is shown, its scroll and caret - * positions are restored. - * @param view The Scintilla view to change the document of. - * @param n The index of the document in '_BUFFERS' to focus. - * @param absolute Flag indicating whether or not the index specified in - * '_BUFFERS' is absolute. If FALSE, focuses the document relative to the - * currently focused document for the given index. - * Throws an error if the buffer does not exist. + * Switches to a document in the given view. + * @param L The Lua state. + * @param view The Scintilla view. + * @param n Relative or absolute index of the document to switch to. An absolute + * n of -1 represents the last document. + * @param relative Flag indicating whether or not n is relative. */ -void l_goto_buffer(GtkWidget *view, int n, int absolute) { - lua_getfield(lua, LUA_REGISTRYINDEX, "buffers"); - if (!absolute) { - sptr_t doc = SS(view, SCI_GETDOCPOINTER, 0, 0); - 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); +static void lL_gotodoc(lua_State *L, GtkWidget *view, int n, int relative) { + if (relative && n == 0) return; + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + if (relative) { + l_pushdoc(L, SS(view, SCI_GETDOCPOINTER, 0, 0)), lua_gettable(L, -2); + n = lua_tointeger(L, -1) + n; + lua_pop(L, 1); // index + if (n > lua_objlen(L, -1)) + n = 1; + else if (n < 1) + n = lua_objlen(L, -1); + lua_rawgeti(L, -1, n); } else { - luaL_argcheck(lua, n >= 0 && n <= lua_objlen(lua, -1), 2, + luaL_argcheck(L, (n > 0 && n <= lua_objlen(L, -1)) || n == -1, 2, "no Buffer exists at that index"); - lua_rawgeti(lua, -1, n); + lua_rawgeti(L, -1, (n > 0) ? n : lua_objlen(L, -1)); } - sptr_t doc = l_checkdocpointer(lua, -1); + sptr_t doc = l_todoc(L, -1); SS(view, SCI_SETDOCPOINTER, 0, doc); - l_set_buffer_global(view); - lua_pop(lua, 2); // buffer table and buffers + l_setglobaldoc(L, doc); + lua_pop(L, 2); // buffer table and buffers } /** - * Sets the global 'buffer' variable to be the document in the specified - * Scintilla object. - * @param view The Scintilla widget housing the buffer to be 'buffer'. + * Closes the Lua state. + * Unsplits and destroys all Scintilla views and removes all Scintilla + * documents, before closing the state. + * @param L The Lua state. */ -void l_set_buffer_global(GtkWidget *view) { - sptr_t doc = SS(view, SCI_GETDOCPOINTER, 0, 0); - lua_getfield(lua, LUA_REGISTRYINDEX, "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 -} - -/** - * Closes the Lua State. - * Unsplits all Scintilla views recursively, removes all Scintilla documents, - * and deletes the last Scintilla view before closing the state. - */ -void l_close() { +static void l_close(lua_State *L) { closing = TRUE; while (unsplit_view(focused_view)) ; // need space to fix compiler warning - lua_getfield(lua, LUA_REGISTRYINDEX, "buffers"); - lua_pushnil(lua); - while (lua_next(lua, -2)) { - sptr_t doc = l_checkdocpointer(lua, -1); - remove_buffer(doc); - lua_pop(lua, 1); // value + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isnumber(L, -2)) delete_buffer(l_todoc(L, -1)); + lua_pop(L, 1); // value } - lua_pop(lua, 1); // buffers + lua_pop(L, 1); // buffers gtk_widget_destroy(focused_view); - lua_close(lua); + lua_close(L); } /******************************************************************************/ @@ -1249,179 +1042,201 @@ void l_close() { /******************************************************************************/ /** - * Recurses through a Lua table, setting each of its keys and values to nil, - * effectively clearing the table. - * @param lua The Lua State. - * @param abs_index The absolute stack index of the table to clear. + * Clears a table at the given valid index by setting all of its keys to nil. + * Cannot be called with a pseudo-index. + * @param L The Lua state. + * @param index The stack index of the table. */ -static void clear_table(lua_State *lua, int abs_index) { - lua_pushnil(lua); - while (lua_next(lua, abs_index)) { - lua_pop(lua, 1); // value - lua_pushnil(lua); - lua_rawset(lua, abs_index); - lua_pushnil(lua); // get 'new' first key +static void lL_cleartable(lua_State *L, int index) { + lua_pushnil(L); + while (lua_next(L, index)) { + lua_pop(L, 1); // value + lua_pushnil(L), lua_rawset(L, index); + lua_pushnil(L); // get 'new' first key } } /** - * Prints a warning. - * @param s The warning to print. + * Pushes the Scintilla view onto the stack. + * The view must have previously been added with lL_addview. + * @param L The Lua state. + * @param view The Scintilla view to push. + * @see lL_addview */ -static void warn(const char *s) { printf("Warning: %s\n", s); } +static void l_pushview(lua_State *L, GtkWidget *view) { + lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); + lua_pushlightuserdata(L, view), lua_gettable(L, -2); + lua_remove(L, -2); // views +} /** - * Calls a Lua function with a number of arguments and expected return values. - * The last argument is at the stack top, and each argument in reverse order is - * one element lower on the stack with the Lua function being under the first - * argument. - * @param nargs The number of arguments to pass to the Lua function to call. - * @param retn Optional number of expected return values. Defaults to 0. - * @param keep_return Optional flag indicating whether or not to keep the return - * values at the top of the stack. If FALSE, discards the return values. - * Defaults to FALSE. - * @return FALSE if an error occured or the function returns false explicitly; - * TRUE otherwise. + * Pushes the Scintilla document onto the stack. + * The document must have previously been added with lL_adddoc. + * @param L The Lua state. + * @param doc The document to push. + * @see lL_adddoc */ -static int l_call_function(int nargs, int retn, int keep_return) { - int ret = lua_pcall(lua, nargs, retn, 0); - if (ret == 0) { - int result = (retn > 0) ? lua_toboolean(lua, -1) == 1 : TRUE; - if (retn > 0 && !keep_return) lua_pop(lua, retn); // retn - return result; - } else { - if (focused_view) - l_emit_event("error", LUA_TSTRING, lua_tostring(lua, -1), -1); - else - printf("Lua Error: %s\n", lua_tostring(lua, -1)); - lua_settop(lua, 0); - } - return FALSE; +static void l_pushdoc(lua_State *L, sptr_t doc) { + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_pushlightuserdata(L, (sptr_t *)doc), lua_gettable(L, -2); + lua_remove(L, -2); // buffers } /** - * Performs a Lua rawget on a table at a given stack index and returns an int. - * @param lua The Lua State. - * @param index The relative index of the table to rawget from. - * @param n The index in the table to rawget. - * @return int result of lua_rawgeti(). + * Prints a warning. + * @param s The warning to print. */ -static int l_rawgeti_int(lua_State *lua, int index, int n) { - lua_rawgeti(lua, index, n); - int ret = lua_tointeger(lua, -1); - lua_pop(lua, 1); // integer - return ret; -} +static void warn(const char *s) { printf("Warning: %s\n", s); } /** - * Performs a Lua rawget on a table at a given stack index and returns a string. - * @param lua The Lua State. - * @param index The relative index of the table to rawget from. - * @param k String key in the table to rawget. - * @return string result of lua_rawget(). + * Returns the value t[n] as an integer where t is the value at the given valid + * index. + * The access is raw; that is, it does not invoke metamethods. + * @param L The Lua state. + * @param index The stack index of the table. + * @param n The index in the table to get. + * @return integer */ -static const char *l_rawget_str(lua_State *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; +static int l_rawgetiint(lua_State *L, int index, int n) { + lua_rawgeti(L, index, n); + int ret = lua_tointeger(L, -1); + lua_pop(L, 1); // integer + return ret; } /** - * Creates a GtkMenu from a table at the top of the Lua stack. - * The table has a key 'title' and a numeric list of subitems. - * @param lua The Lua State. - * @param callback A GCallback associated with each menu item. - * @param submenu Flag indicating whether or not this menu is a submenu. - * @return GtkWidget menu. + * Returns the value t[k] as a string where t is the value at the given valid + * index. + * The access is raw; that is, it does not invoke metamethods. + * @param L The Lua state. + * @param index The stack index of the table. + * @param k String key in the table to get. + * @return string */ -GtkWidget *l_create_gtkmenu(lua_State *lua, GCallback callback, int submenu) { - GtkWidget *menu = gtk_menu_new(), *menu_item = 0, *submenu_root = 0; - const char *label; - lua_getfield(lua, -1, "title"); - if (!lua_isnil(lua, -1) || submenu) { // title required for submenu - label = !lua_isnil(lua, -1) ? lua_tostring(lua, -1) : "notitle"; - submenu_root = gtk_menu_item_new_with_mnemonic(label); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu_root), menu); - } - lua_pop(lua, 1); // title - lua_pushnil(lua); - while (lua_next(lua, -2)) { - if (lua_istable(lua, -1)) { - lua_getfield(lua, -1, "title"); - int is_submenu = !lua_isnil(lua, -1); - lua_pop(lua, 1); // title - if (is_submenu) - gtk_menu_shell_append(GTK_MENU_SHELL(menu), - l_create_gtkmenu(lua, callback, TRUE)); - else - if (lua_objlen(lua, -1) == 2 || lua_objlen(lua, -1) == 4) { - lua_rawgeti(lua, -1, 1); - lua_rawgeti(lua, -2, 2); - label = lua_tostring(lua, -2); - int menu_id = lua_tointeger(lua, -1); - lua_rawgeti(lua, -3, 3); - lua_rawgeti(lua, -4, 4); - int key = !lua_isnil(lua, -2) ? lua_tointeger(lua, -2) : 0; - int modifiers = !lua_isnil(lua, -1) ? lua_tointeger(lua, -1) : 0; - lua_pop(lua, 4); // label, id, key, and modifiers - if (label) { - 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); - if (key || modifiers) - gtk_widget_add_accelerator(menu_item, "activate", accel, key, - modifiers, GTK_ACCEL_VISIBLE); - g_signal_connect(menu_item, "activate", callback, - GINT_TO_POINTER(menu_id)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); - } - } else warn("gtkmenu: { 'label', id_num [, keycode, mods] } expected"); - } - lua_pop(lua, 1); // value - } - return !submenu_root ? menu : submenu_root; +static const char *l_rawgetstr(lua_State *L, int index, const char *k) { + lua_pushstring(L, k); + lua_rawget(L, index); + const char *str = lua_tostring(L, -1); + lua_pop(L, 1); // string + return str; } /** - * Convert the stack element at a specified index to a Scintilla w and/or l long - * parameter based on type. - * @param lua The Lua State. - * @param type The Lua type the top stack element is. - * @param arg_idx The initial stack index to start converting at. It is - * incremented as parameters are read from the stack. - * @return long for Scintilla. + * Checks whether the function argument narg is the given Scintilla parameter + * type and returns it cast to the proper type. + * @param L The Lua state. + * @param index The stack index of the Scintilla parameter. + * @param type The Scintilla type to convert to. + * @return Scintilla param */ -static long l_toscintillaparam(lua_State *lua, int type, int *arg_idx) { +static long lL_checkscintillaparam(lua_State *L, int *narg, int type) { if (type == tSTRING) - return (long)luaL_checkstring(lua, (*arg_idx)++); + return (long)luaL_checkstring(L, (*narg)++); else if (type == tBOOL) - return lua_toboolean(lua, (*arg_idx)++); + return lua_toboolean(L, (*narg)++); else if (type == tKEYMOD) { - int key = luaL_checkinteger(lua, (*arg_idx)++) & 0xFFFF; - return key | ((luaL_checkinteger(lua, (*arg_idx)++) & + int key = luaL_checkinteger(L, (*narg)++) & 0xFFFF; + return key | ((luaL_checkinteger(L, (*narg)++) & (SCMOD_SHIFT | SCMOD_CTRL | SCMOD_ALT)) << 16); } else if (type > tVOID && type < tBOOL) - return luaL_checklong(lua, (*arg_idx)++); + return luaL_checklong(L, (*narg)++); else return 0; } /** - * Checks if the Scintilla document of the buffer table at the index specified - * is the document of the focused Scintilla view. - * Throws an error if the check is not satisfied. - * @param lua The Lua State. - * @param narg The relative stack position of the buffer table. + * Checks whether the function argument narg is a Scintilla view and returns + * this view cast to a GtkWidget. + * @param L The Lua state. + * @param narg The stack index of the Scintilla view. + * @return Scintilla view + */ +static GtkWidget *lL_checkview(lua_State *L, int narg) { + luaL_getmetatable(L, "ta_view"); + lua_getmetatable(L, narg); + luaL_argcheck(L, lua_equal(L, -1, -2), narg, "View expected"); + lua_getfield(L, (narg > 0) ? narg : narg - 2, "widget_pointer"); + GtkWidget *view = (GtkWidget *)lua_touserdata(L, -1); + lua_pop(L, 3); // widget_pointer, metatable, metatable + return view; +} + +/** + * Checks whether the function argument narg is a Scintilla document. If not, + * raises an error. + * @param L The Lua state. + * @param narg The stack index of the Scintilla document. + * @return Scintilla document */ -static void l_check_focused_buffer(lua_State *lua, int narg) { - sptr_t cur_doc = SS(focused_view, SCI_GETDOCPOINTER, 0, 0); - luaL_argcheck(lua, cur_doc == l_checkdocpointer(lua, narg), 1, +static void lL_globaldoccheck(lua_State *L, int narg) { + luaL_getmetatable(L, "ta_buffer"); + lua_getmetatable(L, (narg > 0) ? narg : narg - 1); + luaL_argcheck(L, lua_equal(L, -1, -2), narg, "Buffer expected"); + lua_getfield(L, (narg > 0) ? narg : narg - 2, "doc_pointer"); + sptr_t doc = (sptr_t)lua_touserdata(L, -1); + luaL_argcheck(L, doc == SS(focused_view, SCI_GETDOCPOINTER, 0, 0), narg, "this buffer is not the current one"); + lua_pop(L, 3); // doc_pointer, metatable, metatable +} + +/** + * Pushes a GTK menu created from the table at the given valid index onto the + * stack. + * Consult the LuaDoc for the table format. + * @param L The Lua state. + * @param callback A GCallback associated with each menu item. + * @param submenu Flag indicating whether or not this menu is a submenu. + */ +static void l_pushgtkmenu(lua_State *L, int index, GCallback callback, + int submenu) { + GtkWidget *menu = gtk_menu_new(), *menu_item = 0, *submenu_root = 0; + const char *label; + lua_pushvalue(L, index); // copy to stack top so pseudo-indices can be used + lua_getfield(L, -1, "title"); + if (!lua_isnil(L, -1) || submenu) { // title required for submenu + label = !lua_isnil(L, -1) ? lua_tostring(L, -1) : "notitle"; + submenu_root = gtk_menu_item_new_with_mnemonic(label); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(submenu_root), menu); + } + lua_pop(L, 1); // title + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_istable(L, -1)) { + lua_getfield(L, -1, "title"); + int is_submenu = !lua_isnil(L, -1); + lua_pop(L, 1); // title + if (is_submenu) { + l_pushgtkmenu(L, -1, callback, TRUE); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), + (GtkWidget *)lua_touserdata(L, -1)); + lua_pop(L, 1); // gtkmenu + } else if (lua_objlen(L, -1) == 2 || lua_objlen(L, -1) == 4) { + lua_rawgeti(L, -1, 1); + label = lua_tostring(L, -1); + lua_pop(L, 1); // label + int menu_id = l_rawgetiint(L, -1, 2); + int key = l_rawgetiint(L, -1, 3); + int modifiers = l_rawgetiint(L, -1, 4); + if (label) { + if (g_str_has_prefix(label, "gtk-")) + menu_item = gtk_image_menu_item_new_from_stock(label, NULL); + else if (strcmp(label, "separator") == 0) + menu_item = gtk_separator_menu_item_new(); + else + menu_item = gtk_menu_item_new_with_mnemonic(label); + if (key || modifiers) + gtk_widget_add_accelerator(menu_item, "activate", accel, key, + modifiers, GTK_ACCEL_VISIBLE); + g_signal_connect(menu_item, "activate", callback, + GINT_TO_POINTER(menu_id)); + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); + } + } else warn("gtkmenu: { 'label', id_num [, keycode, mods] } expected"); + } + lua_pop(L, 1); // value + } + lua_pop(L, 1); // table copy + lua_pushlightuserdata(L, !submenu_root ? menu : submenu_root); } /******************************************************************************/ @@ -1429,99 +1244,107 @@ static void l_check_focused_buffer(lua_State *lua, int narg) { /******************************************************************************/ /** - * Handles a Textadept event. - * @param s String event name. - * @param ... Optional arguments to pass to the handler. The variable argument - * list should contain Lua types followed by the data of that type to pass. - * The list is terminated by a -1. - * @return FALSE on error or if event returns false explicitly; TRUE otherwise. + * Emits an event. + * @param L The Lua state. + * @param name The event name. + * @param ... Arguments to pass with the event. Each pair of arguments should be + * a Lua type followed by the data value itself. For LUA_TLIGHTUSERDATA and + * LUA_TTABLE types, push the data values to the stack and give the value + * returned by luaL_ref(); luaL_unref() will be called appropriately. The list + * must be terminated with a -1. + * @return TRUE or FALSE depending on the boolean value returned by the event + * handler, if any. */ -int l_emit_event(const char *s, ...) { - lua_getglobal(lua, "events"); - if (lua_istable(lua, -1)) { - lua_getfield(lua, -1, "emit"); - lua_remove(lua, -2); // events table - if (lua_isfunction(lua, -1)) { - lua_pushstring(lua, s); +static int lL_event(lua_State *L, const char *name, ...) { + int ret = FALSE; + lua_getglobal(L, "events"); + if (lua_istable(L, -1)) { + lua_getfield(L, -1, "emit"); + lua_remove(L, -2); // events table + if (lua_isfunction(L, -1)) { + lua_pushstring(L, name); int n = 1; va_list ap; - va_start(ap, s); + va_start(ap, name); int type = va_arg(ap, int); while (type != -1) { void *arg = va_arg(ap, void*); if (type == LUA_TNIL) - lua_pushnil(lua); + lua_pushnil(L); else if (type == LUA_TBOOLEAN) - lua_pushboolean(lua, (long)arg); + lua_pushboolean(L, (long)arg); else if (type == LUA_TNUMBER) - lua_pushinteger(lua, (long)arg); + lua_pushinteger(L, (long)arg); else if (type == LUA_TSTRING) - lua_pushstring(lua, (char *)arg); + lua_pushstring(L, (char *)arg); else if (type == LUA_TLIGHTUSERDATA || type == LUA_TTABLE) { long ref = (long)arg; - lua_rawgeti(lua, LUA_REGISTRYINDEX, ref); - luaL_unref(lua, LUA_REGISTRYINDEX, ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, ref); + luaL_unref(L, LUA_REGISTRYINDEX, ref); } else warn("events.emit: ignored invalid argument type"); n++; type = va_arg(ap, int); } va_end(ap); - return l_call_function(n, 1, FALSE); - } else lua_pop(lua, 1); // non-function - } else lua_pop(lua, 1); // non-table - return FALSE; -} - -#define l_pushscninteger(i, n) { \ - lua_pushinteger(lua, i); \ - lua_setfield(lua, -2, n); \ + if (lua_pcall(L, n, 1, 0) == 0) + ret = lua_toboolean(L, -1); + else + lL_event(L, "error", LUA_TSTRING, lua_tostring(L, -1), -1); + lua_pop(L, 1); // result + } else lua_pop(L, 1); // non-function + } else lua_pop(L, 1); // non-table + return ret; } /** - * Handles a Scintilla notification. + * Emits a Scintilla notification event. + * @param L The Lua state. * @param n The Scintilla notification struct. + * @see lL_event */ -void l_emit_scnotification(struct SCNotification *n) { - lua_newtable(lua); - l_pushscninteger(n->nmhdr.code, "code"); - l_pushscninteger(n->position, "position"); - l_pushscninteger(n->ch, "ch"); - l_pushscninteger(n->modifiers, "modifiers"); - //l_pushscninteger(n->modificationType, "modification_type"); - lua_pushstring(lua, n->text); - lua_setfield(lua, -2, "text"); - //l_pushscninteger(n->length, "length"); - //l_pushscninteger(n->linesAdded, "lines_added"); - //l_pushscninteger(n->message, "message"); - l_pushscninteger(n->wParam, "wParam"); - l_pushscninteger(n->lParam, "lParam"); - l_pushscninteger(n->line, "line"); - //l_pushscninteger(n->foldLevelNow, "fold_level_now"); - //l_pushscninteger(n->foldLevelPrev, "fold_level_prev"); - l_pushscninteger(n->margin, "margin"); - l_pushscninteger(n->x, "x"); - l_pushscninteger(n->y, "y"); - l_emit_event("SCN", LUA_TTABLE, luaL_ref(lua, LUA_REGISTRYINDEX), -1); +static void lL_notify(lua_State *L, struct SCNotification *n) { + lua_newtable(L); + lua_pushinteger(L, n->nmhdr.code), lua_setfield(L, -2, "code"); + lua_pushinteger(L, n->position), lua_setfield(L, -2, "position"); + lua_pushinteger(L, n->ch), lua_setfield(L, -2, "ch"); + lua_pushinteger(L, n->modifiers), lua_setfield(L, -2, "modifiers"); + //lua_pushinteger(L, n->modificationType); + //lua_setfield(L, -2, "modification_type"); + lua_pushstring(L, n->text), lua_setfield(L, -2, "text"); + //lua_pushinteger(L, n->length), lua_setfield(L, -2, "length"); + //lua_pushinteger(L, n->linesAdded), lua_setfield(L, -2, "lines_added"); + //lua_pushinteger(L, n->message), lua_setfield(L, -2, "message"); + lua_pushinteger(L, n->wParam), lua_setfield(L, -2, "wParam"); + lua_pushinteger(L, n->lParam), lua_setfield(L, -2, "lParam"); + lua_pushinteger(L, n->line), lua_setfield(L, -2, "line"); + //lua_pushinteger(L, n->foldLevelNow), lua_setfield(L, -2, "fold_level_now"); + //lua_pushinteger(L, n->foldLevelPrev); + //lua_setfield(L, -2, "fold_level_prev"); + lua_pushinteger(L, n->margin), lua_setfield(L, -2, "margin"); + lua_pushinteger(L, n->x), lua_setfield(L, -2, "x"); + lua_pushinteger(L, n->y), lua_setfield(L, -2, "y"); + lL_event(L, "SCN", LUA_TTABLE, luaL_ref(L, LUA_REGISTRYINDEX), -1); } /** - * Requests and pops up a context menu for the Scintilla view. + * Shows the context menu for a Scintilla view based on a mouse event. + * @param L The Lua state. * @param event The mouse button event. */ -void l_gui_popup_context_menu(GdkEventButton *event) { - lua_getglobal(lua, "gui"); - if (lua_istable(lua, -1)) { - lua_getfield(lua, -1, "context_menu"); - if (lua_isuserdata(lua, -1)) { - GtkWidget *menu = l_togtkwidget(lua, -1); +static void lL_showcontextmenu(lua_State *L, GdkEventButton *event) { + lua_getglobal(L, "gui"); + if (lua_istable(L, -1)) { + lua_getfield(L, -1, "context_menu"); + if (lua_isuserdata(L, -1)) { + GtkWidget *menu = (GtkWidget *)lua_touserdata(L, -1); gtk_widget_show_all(menu); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, event ? event->button : 0, gdk_event_get_time((GdkEvent *)event)); - } else if (!lua_isnil(lua, -1)) + } else if (!lua_isnil(L, -1)) warn("gui.context_menu: gtkmenu expected"); - lua_pop(lua, 1); // gui.context_menu - } else lua_pop(lua, 1); + lua_pop(L, 1); // gui.context_menu + } else lua_pop(L, 1); // non-table } /******************************************************************************/ @@ -1530,518 +1353,476 @@ void l_gui_popup_context_menu(GdkEventButton *event) { /******************************************************************************/ /** - * Calls Scintilla with appropriate parameters and returs appropriate values. - * @param lua The Lua State. - * @param view The Scintilla view to call. - * @param msg The integer message index to call Scintilla with. - * @param p1_type The Lua type of p1, the Scintilla w parameter. - * @param p2_type The Lua type of p2, the Scintilla l parameter. - * @param rt_type The Lua type of the Scintilla return parameter. - * @param arg The index on the Lua stack where arguments to Scintilla begin. + * Calls a function as a Scintilla function. + * Does not remove any arguments from the stack, but does push results. + * @param L The Lua state. + * @param msg The Scintilla message. + * @param wtype The type of Scintilla wParam. + * @param ltype The type of Scintilla lParam. + * @param rtype The type of the Scintilla return. + * @param arg The stack index of the first Scintilla parameter. Subsequent + * elements will also be passed to Scintilla as needed. + * @return number of results pushed onto the stack. + * @see lL_checkscintillaparam */ -static int l_call_scintilla(lua_State *lua, GtkWidget *view, int msg, - int p1_type, int p2_type, int rt_type, int arg) { - long params[2] = {0, 0}; - int params_needed = 2, len = 0, string_return = FALSE; +static int l_callscintilla(lua_State *L, int msg, int wtype, int ltype, + int rtype, int arg) { + uptr_t wparam = 0; + sptr_t lparam = 0, len = 0; + int params_needed = 2, string_return = FALSE; char *return_string = 0; - // SCI_PRIVATELEXERCALL iface has p1_type int, p2_type int. Change p2_type - // appropriately. See LPeg lexer API for more info. + // Even though the SCI_PRIVATELEXERCALL interface has ltype int, the LPeg + // lexer API uses different types depending on wparam. Modify ltype + // appropriately. See the LPeg lexer API for more information. if (msg == SCI_PRIVATELEXERCALL) { - p2_type = tSTRINGRESULT; - int c = luaL_checklong(lua, arg); + ltype = tSTRINGRESULT; + int c = luaL_checklong(L, arg); if (c == SCI_GETDIRECTFUNCTION || c == SCI_SETDOCPOINTER) - p2_type = tINT; + ltype = tINT; else if (c == SCI_SETLEXERLANGUAGE) - p2_type = tSTRING; + ltype = tSTRING; } - // Set the w and l parameters appropriately for Scintilla. - if (p1_type == tLENGTH && p2_type == tSTRING) { - params[0] = (long)lua_strlen(lua, arg); - params[1] = (long)luaL_checkstring(lua, arg); + // Set wParam and lParam appropriately for Scintilla based on wtype and ltype. + if (wtype == tLENGTH && ltype == tSTRING) { + wparam = (uptr_t)lua_strlen(L, arg); + lparam = (sptr_t)luaL_checkstring(L, arg); params_needed = 0; - } else if (p2_type == tSTRINGRESULT) { + } else if (ltype == tSTRINGRESULT) { string_return = TRUE; - params_needed = (p1_type == tLENGTH) ? 0 : 1; + params_needed = (wtype == tLENGTH) ? 0 : 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 - len = SS(view, msg, params[0], 0); - if (p1_type == tLENGTH) params[0] = len; - return_string = malloc(len + 1); - return_string[len] = '\0'; + if (params_needed > 0) wparam = lL_checkscintillaparam(L, &arg, wtype); + if (params_needed > 1) lparam = lL_checkscintillaparam(L, &arg, ltype); + if (string_return) { // create a buffer for the return string + len = SS(focused_view, msg, wparam, 0); + if (wtype == tLENGTH) wparam = len; + return_string = malloc(len + 1), return_string[len] = '\0'; if (msg == SCI_GETTEXT || msg == SCI_GETSELTEXT || msg == SCI_GETCURLINE) len--; // Scintilla appends '\0' for these messages; compensate - params[1] = (long)return_string; + lparam = (sptr_t)return_string; } // Send the message to Scintilla and return the appropriate values. - sptr_t result = SS(view, msg, params[0], params[1]); - arg = lua_gettop(lua); - if (string_return) lua_pushlstring(lua, return_string, len); - if (rt_type == tBOOL) lua_pushboolean(lua, result); - if (rt_type > tVOID && rt_type < tBOOL) lua_pushinteger(lua, result); + sptr_t result = SS(focused_view, msg, wparam, lparam); + arg = lua_gettop(L); + if (string_return) lua_pushlstring(L, return_string, len); + if (rtype == tBOOL) lua_pushboolean(L, result); + if (rtype > tVOID && rtype < tBOOL) lua_pushinteger(L, result); g_free(return_string); - return lua_gettop(lua) - arg; -} - -/** - * Calls a Scintilla buffer function with upvalues from a closure. - * @param lua The Lua State. - * @see l_buffer_mt_index - */ -static int l_call_buffer_function(lua_State *lua) { - GtkWidget *view = focused_view; - int buffer_func_table_idx = lua_upvalueindex(1); - 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); - int arg = 1; - if (lua_type(lua, 1) == LUA_TTABLE) { - l_check_focused_buffer(lua, 1); - arg = 2; + return lua_gettop(L) - arg; +} + +static int lbuf_closure(lua_State *L) { + // If optional buffer argument is given, check it. + if (lua_istable(L, 1)) lL_globaldoccheck(L, 1); + // Interface table is of the form { msg, rtype, wtype, ltype }. + return l_callscintilla(L, l_rawgetiint(L, lua_upvalueindex(1), 1), + l_rawgetiint(L, lua_upvalueindex(1), 3), + l_rawgetiint(L, lua_upvalueindex(1), 4), + l_rawgetiint(L, lua_upvalueindex(1), 2), + lua_istable(L, 1) ? 2 : 1); +} + +static int lbuf_property(lua_State *L) { + int newindex = (lua_gettop(L) == 3); + luaL_getmetatable(L, "ta_buffer"); + lua_getmetatable(L, 1); // metatable can be either ta_buffer or ta_bufferp + int is_buffer = lua_equal(L, -1, -2); + lua_pop(L, 2); // metatable, metatable + + // If the key is a Scintilla function, return a callable closure. + if (is_buffer && !newindex) { + lua_getfield(L, LUA_REGISTRYINDEX, "ta_functions"); + lua_pushvalue(L, 2), lua_gettable(L, -2); + if (lua_istable(L, -1)) return (lua_pushcclosure(L, lbuf_closure, 1), 1); + lua_pop(L, 2); // non-table, ta_functions } - return l_call_scintilla(lua, view, msg, p1_type, p2_type, rt_type, arg); -} -/** - * Metatable index for a buffer table. - * If the key is a Scintilla buffer function, push a closure so it can be called - * as a function. If the key is a non-indexable buffer property, call Scintilla - * to get it. If the key is an indexible buffer property, push a table with a - * metatable to access buffer property indices. - * @param lua The Lua State. - */ -static int l_buffer_mt_index(lua_State *lua) { - const char *key = luaL_checkstring(lua, 2); - - lua_getfield(lua, LUA_REGISTRYINDEX, "functions"); - lua_getfield(lua, -1, key); - lua_remove(lua, -2); // buffer functions - if (lua_istable(lua, -1)) { - // Of the form { msg, rt_type, p1_type, p2_type } - lua_pushcclosure(lua, l_call_buffer_function, 1); - return 1; - } else lua_pop(lua, 1); // non-table - - lua_getfield(lua, LUA_REGISTRYINDEX, "properties"); - lua_getfield(lua, -1, key); - lua_remove(lua, -2); // buffer properties - if (lua_istable(lua, -1)) { - 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(focused_view, SCI_GETDOCPOINTER, 0, 0); - lua_newtable(lua); - lua_pushstring(lua, key); - lua_setfield(lua, -2, "property"); - lua_pushinteger(lua, doc); - lua_setfield(lua, -2, "doc_pointer"); - l_mt(lua, "_bufferp_mt", l_bufferp_mt_index, l_bufferp_mt_newindex); + // If the key is a Scintilla property, determine if it is an indexible one or + // not. If so, return a table with the appropriate metatable; otherwise call + // Scintilla to get or set the property's value. + lua_getfield(L, LUA_REGISTRYINDEX, "ta_properties"); + if (is_buffer) + lua_pushvalue(L, 2); // key is given + else + lua_getfield(L, 1, "property"); // indexible property + lua_gettable(L, -2); + if (lua_istable(L, -1)) { + // Interface table is of the form { get_id, set_id, rtype, wtype }. + if (!is_buffer) + lua_getfield(L, 1, "buffer"), lL_globaldoccheck(L, -1), lua_pop(L, 1); + else + lL_globaldoccheck(L, 1); + if (is_buffer && l_rawgetiint(L, -1, 4) != tVOID) { // indexible property + lua_newtable(L); + lua_pushvalue(L, 2), lua_setfield(L, -2, "property"); + lua_pushvalue(L, 1), lua_setfield(L, -2, "buffer"); + l_setmetatable(L, -1, "ta_bufferp", lbuf_property, lbuf_property); return 1; - } else return l_call_scintilla(lua, focused_view, msg, p1_type, tVOID, - rt_type, 2); - } else lua_pop(lua, 1); // non-table - - lua_rawget(lua, 1); - return 1; -} - -/** - * Helper function for the buffer property metatable. - * Throws an error when trying to write to a read-only property or when trying - * to read a write-only property. - * @param lua The Lua State. - * @param n 1 for getter property, 2 for setter. - * @param prop String property name. - * @param arg The index on the Lua stack where arguments to Scintilla begin. - * For setter properties, it is 3 because the index is not an argument. For - * getter and setter properties, it is 2 because the index is an argument. - */ -static int l_bufferp_mt_(lua_State *lua, int n, const char *prop, int arg) { - lua_getfield(lua, LUA_REGISTRYINDEX, "properties"); - lua_getfield(lua, -1, prop); - lua_remove(lua, -2); // buffer properties - if (lua_istable(lua, -1)) { - 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))) { - int temp = p1_type; - p1_type = p2_type; - p2_type = temp; } - luaL_argcheck(lua, msg != 0, arg, - (n == 1) ? "write-only property" : "read-only property"); - return l_call_scintilla(lua, focused_view, msg, p1_type, p2_type, rt_type, - arg); - } else lua_pop(lua, 1); // non-table - - (lua_gettop(lua) > 2) ? lua_rawset(lua, 1) : lua_rawget(lua, 1); - return 0; -} - -static int l_buffer_mt_newindex(lua_State *lua) { - return l_bufferp_mt_(lua, 2, lua_tostring(lua, 2), 3); -} - -static int l_bufferp_mt_index(lua_State *lua) { - return l_bufferp_mt_(lua, 1, l_rawget_str(lua, 1, "property"), 2); -} + int msg = l_rawgetiint(L, -1, !newindex ? 1 : 2); + int wtype = l_rawgetiint(L, -1, !newindex ? 4 : 3); + int ltype = !newindex ? tVOID : l_rawgetiint(L, -1, 4); + int rtype = !newindex ? l_rawgetiint(L, -1, 3) : tVOID; + if (newindex && (ltype != tVOID || wtype == tSTRING)) { + int temp = wtype; + wtype = ltype, ltype = temp; + } + luaL_argcheck(L, msg != 0, !newindex ? 2 : 3, + !newindex ? "write-only property" : "read-only property"); + return l_callscintilla(L, msg, wtype, ltype, rtype, + (!is_buffer || !newindex) ? 2 : 3); + } else lua_pop(L, 2); // non-table, ta_properties -static int l_bufferp_mt_newindex(lua_State *lua) { - return l_bufferp_mt_(lua, 2, l_rawget_str(lua, 1, "property"), 2); + !newindex ? lua_rawget(L, 1) : lua_rawset(L, 1); + return 1; } -static int l_view_mt_index(lua_State *lua) { - const char *key = lua_tostring(lua, 2); - if (streq(key, "doc_pointer")) - lua_pushinteger(lua, SS(l_checkview(lua, 1), SCI_GETDOCPOINTER, 0, 0)); - else if (streq(key, "size")) { - GtkWidget *view = l_checkview(lua, 1); +static int lview__index(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "buffer") == 0) + l_pushdoc(L, SS(lL_checkview(L, 1), SCI_GETDOCPOINTER, 0, 0)); + else if (strcmp(key, "size") == 0) { + GtkWidget *view = lL_checkview(L, 1); if (GTK_IS_PANED(gtk_widget_get_parent(view))) { int pos = gtk_paned_get_position(GTK_PANED(gtk_widget_get_parent(view))); - lua_pushinteger(lua, pos); - } else lua_pushnil(lua); - } else lua_rawget(lua, 1); + lua_pushinteger(L, pos); + } else lua_pushnil(L); + } else lua_rawget(L, 1); return 1; } -static int l_view_mt_newindex(lua_State *lua) { - const char *key = lua_tostring(lua, 2); - if (streq(key, "size")) { - GtkWidget *pane = gtk_widget_get_parent(l_checkview(lua, 1)); - int size = luaL_checkinteger(lua, 3); +static int lview__newindex(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "buffer") == 0) + luaL_argerror(L, 3, "read-only property"); + else if (strcmp(key, "size") == 0) { + GtkWidget *pane = gtk_widget_get_parent(lL_checkview(L, 1)); + int size = luaL_checkinteger(L, 3); if (size < 0) size = 0; if (GTK_IS_PANED(pane)) gtk_paned_set_position(GTK_PANED(pane), size); - } else lua_rawset(lua, 1); + } else lua_rawset(L, 1); return 0; } -static int l_gui_mt_index(lua_State *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_pushinteger(lua, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); - else if (streq(key, "statusbar_text")) - lua_pushstring(lua, statusbar_text); - else if (streq(key, "clipboard_text")) { +static int lgui__index(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "title") == 0) + lua_pushstring(L, gtk_window_get_title(GTK_WINDOW(window))); + else if (strcmp(key, "statusbar_text") == 0) + lua_pushstring(L, statusbar_text); + else if (strcmp(key, "clipboard_text") == 0) { char *text = gtk_clipboard_wait_for_text( gtk_clipboard_get(GDK_SELECTION_CLIPBOARD)); - if (text) { - lua_pushstring(lua, text); - g_free(text); - } else lua_pushstring(lua, ""); - } else if (streq(key, "size")) { - lua_newtable(lua); + lua_pushstring(L, text ? text : ""); + if (text) g_free(text); + } else if (strcmp(key, "size") == 0) { int width, height; gtk_window_get_size(GTK_WINDOW(window), &width, &height); - lua_pushinteger(lua, width); - lua_rawseti(lua, -2, 1); - lua_pushinteger(lua, height); - lua_rawseti(lua, -2, 2); - } else lua_rawget(lua, 1); + lua_newtable(L); + lua_pushinteger(L, width), lua_rawseti(L, -2, 1); + lua_pushinteger(L, height), lua_rawseti(L, -2, 2); + } else lua_rawget(L, 1); return 1; } -static int l_gui_mt_newindex(lua_State *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, "focused_doc_pointer") || streq(key, "clipboard_text")) - luaL_argerror(lua, 3, "read-only property"); - else if (streq(key, "docstatusbar_text")) - set_statusbar_text(lua_tostring(lua, 3), 1); - else if (streq(key, "statusbar_text")) { +static void set_statusbar_text(const char *text, int bar) { + if (!statusbar[0] || !statusbar[1]) return; // unavailable on startup + gtk_statusbar_pop(GTK_STATUSBAR(statusbar[bar]), 0); + gtk_statusbar_push(GTK_STATUSBAR(statusbar[bar]), 0, text); +} + +static int lgui__newindex(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "title") == 0) + gtk_window_set_title(GTK_WINDOW(window), lua_tostring(L, 3)); + else if (strcmp(key, "clipboard_text") == 0) + luaL_argerror(L, 3, "read-only property"); + else if (strcmp(key, "docstatusbar_text") == 0) + set_statusbar_text(lua_tostring(L, 3), 1); + else if (strcmp(key, "statusbar_text") == 0) { g_free(statusbar_text); - statusbar_text = g_strdup(!lua_isnil(lua, 3) ? lua_tostring(lua, 3) : ""); + statusbar_text = g_strdup(luaL_optstring(L, 3, "")); set_statusbar_text(statusbar_text, 0); - } else if (streq(key, "menubar")) { - luaL_argcheck(lua, lua_istable(lua, 3), 3, "table of menus expected"); - GtkWidget *menubar = gtk_menu_bar_new(); - lua_pushnil(lua); - while (lua_next(lua, 3)) { - luaL_argcheck(lua, lua_isuserdata(lua, -1), 3, "table of menus expected"); - GtkWidget *menu_item = l_togtkwidget(lua, -1); - gtk_menu_shell_append(GTK_MENU_SHELL(menubar), menu_item); - lua_pop(lua, 1); // value + } else if (strcmp(key, "menubar") == 0) { + luaL_argcheck(L, lua_istable(L, 3), 3, "table of menus expected"); + GtkWidget *new_menubar = gtk_menu_bar_new(); + lua_pushnil(L); + while (lua_next(L, 3)) { + luaL_argcheck(L, lua_isuserdata(L, -1), 3, "table of menus expected"); + GtkWidget *menu_item = (GtkWidget *)lua_touserdata(L, -1); + gtk_menu_shell_append(GTK_MENU_SHELL(new_menubar), menu_item); + lua_pop(L, 1); // value } - set_menubar(menubar); - } else if (streq(key, "size")) { - luaL_argcheck(lua, lua_istable(lua, 3) && lua_objlen(lua, 3) == 2, 3, + GtkWidget *vbox = gtk_widget_get_parent(menubar); + gtk_container_remove(GTK_CONTAINER(vbox), menubar); + menubar = new_menubar; + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); + gtk_box_reorder_child(GTK_BOX(vbox), menubar, 0); + gtk_widget_show_all(menubar); +#if __OSX__ + ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(menubar)); + gtk_widget_hide(menubar); +#endif + } else if (strcmp(key, "size") == 0) { + luaL_argcheck(L, lua_istable(L, 3) && lua_objlen(L, 3) == 2, 3, "{ width, height } table expected"); - int width = l_rawgeti_int(lua, 3, 1); - int height = l_rawgeti_int(lua, 3, 2); - if (width > 0 && height > 0) - gtk_window_resize(GTK_WINDOW(window), width, height); - } else lua_rawset(lua, 1); + int w = l_rawgetiint(L, 3, 1), h = l_rawgetiint(L, 3, 2); + if (w > 0 && h > 0) gtk_window_resize(GTK_WINDOW(window), w, h); + } else lua_rawset(L, 1); return 0; } #define toggled(w) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)) -static int l_find_mt_index(lua_State *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 if (streq(key, "match_case")) - lua_pushboolean(lua, toggled(match_case_opt)); - else if (streq(key, "whole_word")) - lua_pushboolean(lua, toggled(whole_word_opt)); - else if (streq(key, "lua")) - lua_pushboolean(lua, toggled(lua_opt)); - else if (streq(key, "in_files")) - lua_pushboolean(lua, toggled(in_files_opt)); +static int lfind__index(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "find_entry_text") == 0) + lua_pushstring(L, gtk_entry_get_text(GTK_ENTRY(find_entry))); + else if (strcmp(key, "replace_entry_text") == 0) + lua_pushstring(L, gtk_entry_get_text(GTK_ENTRY(replace_entry))); + else if (strcmp(key, "match_case") == 0) + lua_pushboolean(L, toggled(match_case_opt)); + else if (strcmp(key, "whole_word") == 0) + lua_pushboolean(L, toggled(whole_word_opt)); + else if (strcmp(key, "lua") == 0) + lua_pushboolean(L, toggled(lua_opt)); + else if (strcmp(key, "in_files") == 0) + lua_pushboolean(L, toggled(in_files_opt)); else - lua_rawget(lua, 1); + lua_rawget(L, 1); return 1; } #define toggle(w, b) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), b) -static int l_find_mt_newindex(lua_State *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 if (streq(key, "match_case")) - toggle(match_case_opt, lua_toboolean(lua, -1) ? TRUE : FALSE); - else if (streq(key, "whole_word")) - toggle(whole_word_opt, lua_toboolean(lua, -1) ? TRUE : FALSE); - else if (streq(key, "lua")) - toggle(lua_opt, lua_toboolean(lua, -1) ? TRUE : FALSE); - else if (streq(key, "in_files")) - toggle(in_files_opt, lua_toboolean(lua, -1) ? TRUE : FALSE); - else if (streq(key, "find_label_text")) - gtk_label_set_text_with_mnemonic(GTK_LABEL(flabel), lua_tostring(lua, 3)); - else if (streq(key, "replace_label_text")) - gtk_label_set_text_with_mnemonic(GTK_LABEL(rlabel), lua_tostring(lua, 3)); - else if (streq(key, "find_next_button_text")) - gtk_button_set_label(GTK_BUTTON(fnext_button), lua_tostring(lua, 3)); - else if (streq(key, "find_prev_button_text")) - gtk_button_set_label(GTK_BUTTON(fprev_button), lua_tostring(lua, 3)); - else if (streq(key, "replace_button_text")) - gtk_button_set_label(GTK_BUTTON(r_button), lua_tostring(lua, 3)); - else if (streq(key, "replace_all_button_text")) - gtk_button_set_label(GTK_BUTTON(ra_button), lua_tostring(lua, 3)); - else if (streq(key, "match_case_label_text")) - gtk_button_set_label(GTK_BUTTON(match_case_opt), lua_tostring(lua, 3)); - else if (streq(key, "whole_word_label_text")) - gtk_button_set_label(GTK_BUTTON(whole_word_opt), lua_tostring(lua, 3)); - else if (streq(key, "lua_pattern_label_text")) - gtk_button_set_label(GTK_BUTTON(lua_opt), lua_tostring(lua, 3)); - else if (streq(key, "in_files_label_text")) - gtk_button_set_label(GTK_BUTTON(in_files_opt), lua_tostring(lua, 3)); +static int lfind__newindex(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "find_entry_text") == 0) + gtk_entry_set_text(GTK_ENTRY(find_entry), lua_tostring(L, 3)); + else if (strcmp(key, "replace_entry_text") == 0) + gtk_entry_set_text(GTK_ENTRY(replace_entry), lua_tostring(L, 3)); + else if (strcmp(key, "match_case") == 0) + toggle(match_case_opt, lua_toboolean(L, -1)); + else if (strcmp(key, "whole_word") == 0) + toggle(whole_word_opt, lua_toboolean(L, -1)); + else if (strcmp(key, "lua") == 0) + toggle(lua_opt, lua_toboolean(L, -1)); + else if (strcmp(key, "in_files") == 0) + toggle(in_files_opt, lua_toboolean(L, -1)); + else if (strcmp(key, "find_label_text") == 0) + gtk_label_set_text_with_mnemonic(GTK_LABEL(flabel), lua_tostring(L, 3)); + else if (strcmp(key, "replace_label_text") == 0) + gtk_label_set_text_with_mnemonic(GTK_LABEL(rlabel), lua_tostring(L, 3)); + else if (strcmp(key, "find_next_button_text") == 0) + gtk_button_set_label(GTK_BUTTON(fnext_button), lua_tostring(L, 3)); + else if (strcmp(key, "find_prev_button_text") == 0) + gtk_button_set_label(GTK_BUTTON(fprev_button), lua_tostring(L, 3)); + else if (strcmp(key, "replace_button_text") == 0) + gtk_button_set_label(GTK_BUTTON(r_button), lua_tostring(L, 3)); + else if (strcmp(key, "replace_all_button_text") == 0) + gtk_button_set_label(GTK_BUTTON(ra_button), lua_tostring(L, 3)); + else if (strcmp(key, "match_case_label_text") == 0) + gtk_button_set_label(GTK_BUTTON(match_case_opt), lua_tostring(L, 3)); + else if (strcmp(key, "whole_word_label_text") == 0) + gtk_button_set_label(GTK_BUTTON(whole_word_opt), lua_tostring(L, 3)); + else if (strcmp(key, "lua_pattern_label_text") == 0) + gtk_button_set_label(GTK_BUTTON(lua_opt), lua_tostring(L, 3)); + else if (strcmp(key, "in_files_label_text") == 0) + gtk_button_set_label(GTK_BUTTON(in_files_opt), lua_tostring(L, 3)); else - lua_rawset(lua, 1); + lua_rawset(L, 1); return 0; } -static int l_ce_mt_index(lua_State *lua) { - const char *key = lua_tostring(lua, 2); - if (streq(key, "entry_text")) - lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(command_entry))); +static int lce__index(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "entry_text") == 0) + lua_pushstring(L, gtk_entry_get_text(GTK_ENTRY(command_entry))); else - lua_rawget(lua, 1); + lua_rawget(L, 1); return 1; } -static int l_ce_mt_newindex(lua_State *lua) { - const char *key = lua_tostring(lua, 2); - if (streq(key, "entry_text")) - gtk_entry_set_text(GTK_ENTRY(command_entry), lua_tostring(lua, 3)); +static int lce__newindex(lua_State *L) { + const char *key = lua_tostring(L, 2); + if (strcmp(key, "entry_text") == 0) + gtk_entry_set_text(GTK_ENTRY(command_entry), lua_tostring(L, 3)); else - lua_rawset(lua, 1); + lua_rawset(L, 1); return 0; } /******************************************************************************/ -/****************** Lua CFunctions *******************/ +/****************** Lua CFunctions *******************/ /****************** (For documentation, consult the LuaDoc) *******************/ /******************************************************************************/ -static int l_cf_buffer_delete(lua_State *lua) { - l_check_focused_buffer(lua, 1); - sptr_t doc = l_checkdocpointer(lua, 1); - lua_getfield(lua, LUA_REGISTRYINDEX, "buffers"); - if (lua_objlen(lua, -1) > 1) - l_goto_buffer(focused_view, -1, FALSE); - else - new_buffer(focused_view, TRUE, TRUE); - remove_buffer(doc); - l_emit_event("buffer_deleted", -1); - l_emit_event("buffer_after_switch", -1); +static int lbuffer_check_global(lua_State *L) { + lL_globaldoccheck(L, 1); return 0; } -static int l_cf_buffer_new(lua_State *lua) { - new_buffer(focused_view, TRUE, TRUE); - lua_getfield(lua, LUA_REGISTRYINDEX, "buffers"); - lua_rawgeti(lua, -1, lua_objlen(lua, -1)); +static int lbuffer_delete(lua_State *L) { + lL_globaldoccheck(L, 1); + sptr_t doc = SS(focused_view, SCI_GETDOCPOINTER, 0, 0); + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + if (lua_objlen(L, -1) == 1) new_buffer(0); + lL_gotodoc(L, focused_view, -1, TRUE); + delete_buffer(doc); + lL_event(L, "buffer_deleted", -1), + lL_event(L, "buffer_after_switch", -1); + return 0; +} + +static int lbuffer_new(lua_State *L) { + new_buffer(0); + lua_getfield(L, LUA_REGISTRYINDEX, "ta_buffers"); + lua_rawgeti(L, -1, lua_objlen(L, -1)); return 1; } -static int l_cf_buffer_text_range(lua_State *lua) { - l_check_focused_buffer(lua, 1); +static int lbuffer_text_range(lua_State *L) { + lL_globaldoccheck(L, 1); struct Sci_TextRange tr; - tr.chrg.cpMin = luaL_checkinteger(lua, 2); - tr.chrg.cpMax = luaL_checkinteger(lua, 3); - luaL_argcheck(lua, tr.chrg.cpMin <= tr.chrg.cpMax, 3, "start > end"); - int length = tr.chrg.cpMax - tr.chrg.cpMin; - char *text = malloc(length + 1); - tr.lpstrText = text; + tr.chrg.cpMin = luaL_checkinteger(L, 2); + tr.chrg.cpMax = luaL_checkinteger(L, 3); + luaL_argcheck(L, tr.chrg.cpMin <= tr.chrg.cpMax, 3, "start > end"); + tr.lpstrText = malloc(tr.chrg.cpMax - tr.chrg.cpMin + 1); SS(focused_view, SCI_GETTEXTRANGE, 0, (long)(&tr)); - lua_pushlstring(lua, text, length); - g_free(text); + lua_pushlstring(L, tr.lpstrText, tr.chrg.cpMax - tr.chrg.cpMin); + g_free(tr.lpstrText); return 1; } -static int l_cf_view_focus(lua_State *lua) { - GtkWidget *view = l_checkview(lua, 1); - // view might be an old reference; GTK_IS_WIDGET checks for a valid widget - if (GTK_IS_WIDGET(view)) gtk_widget_grab_focus(view); - return 0; -} - -static int l_cf_view_split(lua_State *lua) { - GtkWidget *view = l_checkview(lua, 1); - int vertical = TRUE; - if (lua_gettop(lua) > 1) vertical = lua_toboolean(lua, 2) == 1; - split_view(view, vertical); - lua_pushvalue(lua, 1); // old view - lua_getglobal(lua, "view"); // new view +static int lview_split(lua_State *L) { + split_view(lL_checkview(L, 1), lua_toboolean(L, 2)); + lua_pushvalue(L, 1); // old view + lua_getglobal(L, "view"); // new view return 2; } -static int l_cf_view_unsplit(lua_State *lua) { - GtkWidget *view = l_checkview(lua, 1); - lua_pushboolean(lua, unsplit_view(view)); +static int lview_unsplit(lua_State *L) { + lua_pushboolean(L, unsplit_view(lL_checkview(L, 1))); return 1; } #define child1(p) gtk_paned_get_child1(GTK_PANED(p)) #define child2(p) gtk_paned_get_child2(GTK_PANED(p)) -#define view_dpi(view) l_get_docpointer_index(SS(view, SCI_GETDOCPOINTER, 0, 0)) -void l_create_entry(lua_State *lua, GtkWidget *c1, GtkWidget *c2, - int vertical) { - lua_newtable(lua); +static void l_pushsplittable(lua_State *L, GtkWidget *c1, GtkWidget *c2) { + lua_newtable(L); if (GTK_IS_PANED(c1)) - l_create_entry(lua, child1(c1), child2(c1), GTK_IS_HPANED(c1) == 1); + l_pushsplittable(L, child1(c1), child2(c1)); else - lua_pushinteger(lua, view_dpi(c1)); - lua_rawseti(lua, -2, 1); + l_pushview(L, c1); + lua_rawseti(L, -2, 1); if (GTK_IS_PANED(c2)) - l_create_entry(lua, child1(c2), child2(c2), GTK_IS_HPANED(c2) == 1); + l_pushsplittable(L, child1(c2), child2(c2)); else - lua_pushinteger(lua, view_dpi(c2)); - lua_rawseti(lua, -2, 2); - lua_pushboolean(lua, vertical); - lua_setfield(lua, -2, "vertical"); + l_pushview(L, c2); + lua_rawseti(L, -2, 2); + lua_pushboolean(L, GTK_IS_HPANED(gtk_widget_get_parent(c1))); + lua_setfield(L, -2, "vertical"); int size = gtk_paned_get_position(GTK_PANED(gtk_widget_get_parent(c1))); - lua_pushinteger(lua, size); - lua_setfield(lua, -2, "size"); + lua_pushinteger(L, size), lua_setfield(L, -2, "size"); } -static int l_cf_gui_get_split_table(lua_State *lua) { - lua_getfield(lua, LUA_REGISTRYINDEX, "views"); - if (lua_objlen(lua, -1) > 1) { - GtkWidget *pane = gtk_widget_get_parent(focused_view); +static int lgui_get_split_table(lua_State *L) { + GtkWidget *pane = gtk_widget_get_parent(focused_view); + if (GTK_IS_PANED(pane)) { while (GTK_IS_PANED(gtk_widget_get_parent(pane))) pane = gtk_widget_get_parent(pane); - l_create_entry(lua, child1(pane), child2(pane), GTK_IS_HPANED(pane) == 1); - } else lua_pushinteger(lua, view_dpi(focused_view)); + l_pushsplittable(L, child1(pane), child2(pane)); + } else l_pushview(L, focused_view); return 1; } -static int l_cf_gui_goto_(lua_State *lua, GtkWidget *view, int buffer) { - int n = luaL_checkinteger(lua, 1); - int abs = (lua_gettop(lua) > 1) ? lua_toboolean(lua, 2) == 1 : TRUE; - buffer ? l_goto_buffer(view, n, abs) : l_goto_view(view, n, abs); - return 0; -} - // 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. -static int l_cf_view_goto_buffer(lua_State *lua) { - GtkWidget *view = l_checkview(lua, 1); - int switch_focus = view != focused_view; - GtkWidget *orig_focused_view = focused_view; +static int lview_goto_buffer(lua_State *L) { + GtkWidget *view = lL_checkview(L, 1), *prev_view = focused_view; + int n = luaL_checkinteger(L, 2), relative = lua_toboolean(L, 3); + int switch_focus = (view != focused_view); if (switch_focus) SS(view, SCI_SETFOCUS, TRUE, 0); - lua_remove(lua, 1); // view table - l_emit_event("buffer_before_switch", -1); - l_cf_gui_goto_(lua, view, TRUE); - l_emit_event("buffer_after_switch", -1); - if (switch_focus) { - SS(view, SCI_SETFOCUS, FALSE, 0); - gtk_widget_grab_focus(orig_focused_view); - } + lL_event(L, "buffer_before_switch", -1); + lL_gotodoc(L, view, n, relative); + lL_event(L, "buffer_after_switch", -1); + if (switch_focus) + SS(view, SCI_SETFOCUS, FALSE, 0), gtk_widget_grab_focus(prev_view); return 0; } -static int l_cf_gui_dialog(lua_State *lua) { - GCDialogType type = gcocoadialog_type(luaL_checkstring(lua, 1)); - int i, j, k, n = lua_gettop(lua) - 1, argc = n; +static int lgui_dialog(lua_State *L) { + GCDialogType type = gcocoadialog_type(luaL_checkstring(L, 1)); + int i, j, k, n = lua_gettop(L) - 1, argc = n; for (i = 2; i < n + 2; i++) - if (lua_type(lua, i) == LUA_TTABLE) argc += lua_objlen(lua, i) - 1; + if (lua_istable(L, i)) argc += lua_objlen(L, i) - 1; const char **argv = malloc((argc + 1) * sizeof(const char *)); for (i = 0, j = 2; j < n + 2; j++) - if (lua_type(lua, j) == LUA_TTABLE) { - int len = lua_objlen(lua, j); + if (lua_istable(L, j)) { + int len = lua_objlen(L, j); for (k = 1; k <= len; k++) { - lua_rawgeti(lua, j, k); - argv[i++] = luaL_checkstring(lua, -1); - lua_pop(lua, 1); + lua_rawgeti(L, j, k); + argv[i++] = luaL_checkstring(L, -1); + lua_pop(L, 1); } - } else argv[i++] = luaL_checkstring(lua, j); + } else argv[i++] = luaL_checkstring(L, j); argv[argc] = 0; char *out = gcocoadialog(type, argc, argv); - lua_pushstring(lua, out); - free(out); - free(argv); + lua_pushstring(L, out); + free(out), free(argv); return 1; } -static int l_cf_gui_goto_view(lua_State *lua) { - return l_cf_gui_goto_(lua, focused_view, FALSE); +static int lgui_goto_view(lua_State *L) { + int n = luaL_checkinteger(L, 1), relative = lua_toboolean(L, 2); + if (relative && n == 0) return 0; + lua_getfield(L, LUA_REGISTRYINDEX, "ta_views"); + if (relative) { + l_pushview(L, focused_view), lua_gettable(L, -2); + n = lua_tointeger(L, -1) + n; + if (n > lua_objlen(L, -2)) + n = 1; + else if (n < 1) + n = lua_objlen(L, -2); + lua_rawgeti(L, -2, n); + } else { + luaL_argcheck(L, n > 0 && n <= lua_objlen(L, -1), 1, + "no View exists at that index"); + lua_rawgeti(L, -1, n); + } + GtkWidget *view = l_toview(L, -1); + gtk_widget_grab_focus(view); + // gui.dialog() interferes with focus so gtk_widget_grab_focus() does not + // always work. If this is the case, ensure goto_view() is called. + if (!gtk_widget_has_focus(view)) goto_view(view); + return 0; } -static void t_menu_activate(GtkWidget *menu, gpointer id) { - l_emit_event("menu_clicked", LUA_TNUMBER, GPOINTER_TO_INT(id), -1); +static void m_clicked(GtkWidget *menu, gpointer id) { + lL_event(lua, "menu_clicked", LUA_TNUMBER, GPOINTER_TO_INT(id), -1); } -static int l_cf_gui_gtkmenu(lua_State *lua) { - luaL_checktype(lua, 1, LUA_TTABLE); - GtkWidget *menu = l_create_gtkmenu(lua, G_CALLBACK(t_menu_activate), FALSE); - lua_pushlightuserdata(lua, (GtkWidget *)menu); +static int lgui_gtkmenu(lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + l_pushgtkmenu(L, -1, G_CALLBACK(m_clicked), FALSE); return 1; } -static int l_cf_string_iconv(lua_State *lua) { +static int lstring_iconv(lua_State *L) { size_t text_len = 0, conv_len = 0; - const char *text = luaL_checklstring(lua, 1, &text_len); - const char *to = luaL_checkstring(lua, 2); - const char *from = luaL_checkstring(lua, 3); + const char *text = luaL_checklstring(L, 1, &text_len); + const char *to = luaL_checkstring(L, 2); + const char *from = luaL_checkstring(L, 3); char *converted = g_convert(text, text_len, to, from, NULL, &conv_len, NULL); - if (converted) { - lua_pushlstring(lua, converted, conv_len); - g_free(converted); - } else luaL_error(lua, "Conversion failed"); + if (!converted) luaL_error(L, "Conversion failed"); + lua_pushlstring(L, converted, conv_len); + g_free(converted); return 1; } -static int l_cf_quit(lua_State *lua) { +static int lquit(lua_State *L) { GdkEventAny event; event.type = GDK_DELETE; event.window = gtk_widget_get_window(window); @@ -2050,84 +1831,103 @@ static int l_cf_quit(lua_State *lua) { return 0; } -static int l_cf_reset(lua_State *lua) { - l_emit_event("reset_before", -1); - l_init(0, NULL, TRUE); - lua_pushboolean(lua, TRUE); - lua_setglobal(lua, "RESETTING"); - l_set_view_global(focused_view); - l_set_buffer_global(focused_view); - l_load_script("init.lua"); - lua_pushnil(lua); - lua_setglobal(lua, "RESETTING"); - l_emit_event("reset_after", -1); +static int lreset(lua_State *L) { + lL_event(L, "reset_before", -1); + lL_init(L, 0, NULL, TRUE); + lua_pushboolean(L, TRUE), lua_setglobal(L, "RESETTING"); + l_setglobalview(L, focused_view); + l_setglobaldoc(L, SS(focused_view, SCI_GETDOCPOINTER, 0, 0)); + lL_dofile(L, "init.lua"); + lua_pushnil(L), lua_setglobal(L, "RESETTING"); + lL_event(L, "reset_after", -1); return 0; } -static gbool emit_timeout(gpointer data) { +static gboolean emit_timeout(gpointer data) { int *refs = (int *)data; lua_rawgeti(lua, LUA_REGISTRYINDEX, refs[0]); // function int nargs = 0, repeat = TRUE; while (refs[++nargs]) lua_rawgeti(lua, LUA_REGISTRYINDEX, refs[nargs]); - l_call_function(nargs - 1, 1, TRUE); - if (lua_toboolean(lua, -1) == 0 || lua_isnil(lua, -1)) { + int ok = (lua_pcall(lua, nargs - 1, 1, 0) == 0); + if (!ok || !lua_toboolean(lua, -1)) { while (--nargs >= 0) luaL_unref(lua, LUA_REGISTRYINDEX, refs[nargs]); repeat = FALSE; + if (!ok) lL_event(lua, "error", LUA_TSTRING, lua_tostring(lua, -1), -1); } - lua_pop(lua, 1); // boolean or nil + lua_pop(lua, 1); // result return repeat; } -static int l_cf_timeout(lua_State *lua) { - int timeout = luaL_checkinteger(lua, 1); - luaL_argcheck(lua, timeout > 0, 1, "timeout must be > 0"); - luaL_argcheck(lua, lua_isfunction(lua, 2), 2, "function expected"); - int n = lua_gettop(lua); +static int ltimeout(lua_State *L) { + int timeout = luaL_checkinteger(L, 1); + luaL_argcheck(L, timeout > 0, 1, "timeout must be > 0"); + luaL_argcheck(L, lua_isfunction(L, 2), 2, "function expected"); + int n = lua_gettop(L); int *refs = (int *)calloc(n, sizeof(int)); - lua_pushvalue(lua, 2); - refs[0] = luaL_ref(lua, LUA_REGISTRYINDEX); + lua_pushvalue(L, 2); + refs[0] = luaL_ref(L, LUA_REGISTRYINDEX); for (int i = 3; i <= n; i++) { - lua_pushvalue(lua, i); - refs[i - 2] = luaL_ref(lua, LUA_REGISTRYINDEX); + lua_pushvalue(L, i); + refs[i - 2] = luaL_ref(L, LUA_REGISTRYINDEX); } g_timeout_add_seconds(timeout, emit_timeout, (gpointer)refs); return 0; } -static int l_cf_find_focus(lua_State *lua) { - find_toggle_focus(); +static int lfind_focus(lua_State *L) { + if (!gtk_widget_has_focus(findbox)) { + gtk_widget_show(findbox); + gtk_widget_grab_focus(find_entry); + gtk_widget_grab_default(fnext_button); + } else { + gtk_widget_grab_focus(focused_view); + gtk_widget_hide(findbox); + } return 0; } -#define emit(o, s) { \ - g_signal_emit_by_name(G_OBJECT(o), s); \ - return 0; \ +static int lfind_next(lua_State *L) { + return (g_signal_emit_by_name(G_OBJECT(fnext_button), "clicked"), 0); } -static int l_cf_find_next(lua_State *lua) { emit(fnext_button, "clicked") } -static int l_cf_find_prev(lua_State *lua) { emit(fprev_button, "clicked") } -static int l_cf_find_replace(lua_State *lua) { emit(r_button, "clicked") } -static int l_cf_find_replace_all(lua_State *lua) { emit(ra_button, "clicked") } -static int l_cf_ce_focus(lua_State *lua) { - ce_toggle_focus(); +static int lfind_prev(lua_State *L) { + return (g_signal_emit_by_name(G_OBJECT(fprev_button), "clicked"), 0); +} + +static int lfind_replace(lua_State *L) { + return (g_signal_emit_by_name(G_OBJECT(r_button), "clicked"), 0); +} + +static int lfind_replace_all(lua_State *L) { + return (g_signal_emit_by_name(G_OBJECT(ra_button), "clicked"), 0); +} + +static int lce_focus(lua_State *L) { + if (!gtk_widget_has_focus(command_entry)) { + gtk_widget_show(command_entry); + gtk_widget_grab_focus(command_entry); + } else { + gtk_widget_hide(command_entry); + gtk_widget_grab_focus(focused_view); + } return 0; } -static int l_cf_ce_show_completions(lua_State *lua) { - luaL_checktype(lua, 1, LUA_TTABLE); +static int lce_show_completions(lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); GtkEntryCompletion *completion = gtk_entry_get_completion( GTK_ENTRY(command_entry)); GtkListStore *store = GTK_LIST_STORE( gtk_entry_completion_get_model(completion)); gtk_list_store_clear(store); - lua_pushnil(lua); - while (lua_next(lua, 1)) { - if (lua_type(lua, -1) == LUA_TSTRING) { + lua_pushnil(L); + while (lua_next(L, 1)) { + if (lua_type(L, -1) == LUA_TSTRING) { GtkTreeIter iter; gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, 0, lua_tostring(lua, -1), -1); + gtk_list_store_set(store, &iter, 0, lua_tostring(L, -1), -1); } else warn("command_entry.show_completions: non-string value ignored"); - lua_pop(lua, 1); // value + lua_pop(L, 1); // value } gtk_entry_completion_complete(completion); return 0; |