aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile4
-rw-r--r--src/lua_interface.c524
-rw-r--r--src/textadept.c138
-rw-r--r--src/textadept.h19
4 files changed, 289 insertions, 396 deletions
diff --git a/src/Makefile b/src/Makefile
index 06857307..52fb26fe 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -4,9 +4,9 @@
INCLUDEDIRS=-Iscintilla-st/include -Ilua/include
ifdef DEBUG
-CXXFLAGS=-DDEBUG -g -DGTK -DSCI_LEXER -W -Wall
+CXXFLAGS=-DDEBUG -g -DGTK -DSCI_LEXER -W -Wall -Wno-sign-compare
else
-CXXFLAGS=-DNDEBUG -O -DGTK -DSCI_LEXER -W -Wall
+CXXFLAGS=-DNDEBUG -O -DGTK -DSCI_LEXER -W -Wall -Wno-sign-compare
endif
GTKFLAGS=$(shell pkg-config --cflags gtk+-2.0)
GTKLIBS=$(shell pkg-config --libs gtk+-2.0 gthread-2.0)
diff --git a/src/lua_interface.c b/src/lua_interface.c
index 9a126cbf..b8101e20 100644
--- a/src/lua_interface.c
+++ b/src/lua_interface.c
@@ -58,12 +58,12 @@ static int l_buffer_mt_index(lua_State *lua),
l_ce_mt_index(lua_State *lua),
l_ce_mt_newindex(lua_State *lua);
-static int l_cf_ta_buffer_new(lua_State *lua),
- l_cf_buffer_delete(lua_State *lua),
+static int l_cf_buffer_delete(lua_State *lua),
l_cf_buffer_text_range(lua_State *lua),
l_cf_view_focus(lua_State *lua),
l_cf_view_split(lua_State *lua),
l_cf_view_unsplit(lua_State *lua),
+ l_cf_ta_buffer_new(lua_State *lua),
l_cf_ta_get_split_table(lua_State *lua),
l_cf_ta_goto_window(lua_State *lua),
l_cf_view_goto_buffer(lua_State *lua),
@@ -71,16 +71,19 @@ static int l_cf_ta_buffer_new(lua_State *lua),
l_cf_ta_iconv(lua_State *lua),
l_cf_ta_reset(lua_State *lua),
l_cf_ta_quit(lua_State *lua),
- l_cf_pm_focus(lua_State *lua),
- l_cf_pm_clear(lua_State *lua),
l_cf_pm_activate(lua_State *lua),
l_cf_pm_add_browser(lua_State *lua),
+ l_cf_pm_clear(lua_State *lua),
+ l_cf_pm_fill(lua_State *lua),
+ l_cf_pm_focus(lua_State *lua),
+ l_cf_pm_show_context_menu(lua_State *lua),
l_cf_find_focus(lua_State *lua),
- l_cf_call_find_next(lua_State *lua),
- l_cf_call_find_prev(lua_State *lua),
- l_cf_call_replace(lua_State *lua),
- l_cf_call_replace_all(lua_State *lua),
- l_cf_ce_focus(lua_State *lua);
+ l_cf_find_next(lua_State *lua),
+ l_cf_find_prev(lua_State *lua),
+ l_cf_find_replace(lua_State *lua),
+ l_cf_find_replace_all(lua_State *lua),
+ l_cf_ce_focus(lua_State *lua),
+ l_cf_ce_show_completions(lua_State *lua);
/**
* Inits or re-inits the Lua State.
@@ -114,31 +117,34 @@ bool l_init(int argc, char **argv, bool reinit) {
lua_newtable(lua);
lua_newtable(lua);
- l_cfunc(lua, l_cf_pm_focus, "focus");
- l_cfunc(lua, l_cf_pm_clear, "clear");
l_cfunc(lua, l_cf_pm_activate, "activate");
l_cfunc(lua, l_cf_pm_add_browser, "add_browser");
+ l_cfunc(lua, l_cf_pm_clear, "clear");
+ l_cfunc(lua, l_cf_pm_fill, "fill");
+ l_cfunc(lua, l_cf_pm_focus, "focus");
+ l_cfunc(lua, l_cf_pm_show_context_menu, "show_context_menu");
l_mt(lua, "_pm_mt", l_pm_mt_index, l_pm_mt_newindex);
lua_setfield(lua, -2, "pm");
lua_newtable(lua);
+ l_cfunc(lua, l_cf_find_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_call_find_next, "call_find_next");
- l_cfunc(lua, l_cf_call_find_prev, "call_find_prev");
- l_cfunc(lua, l_cf_call_replace, "call_replace");
- l_cfunc(lua, l_cf_call_replace_all, "call_replace_all");
+ 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_ta_buffer_new, "new_buffer");
- l_cfunc(lua, l_cf_ta_goto_window, "goto_view");
l_cfunc(lua, l_cf_ta_get_split_table, "get_split_table");
+ l_cfunc(lua, l_cf_ta_goto_window, "goto_view");
l_cfunc(lua, l_cf_ta_gtkmenu, "gtkmenu");
l_cfunc(lua, l_cf_ta_iconv, "iconv");
- l_cfunc(lua, l_cf_ta_reset, "reset");
+ l_cfunc(lua, l_cf_ta_buffer_new, "new_buffer");
l_cfunc(lua, l_cf_ta_quit, "quit");
+ l_cfunc(lua, l_cf_ta_reset, "reset");
l_mt(lua, "_textadept_mt", l_ta_mt_index, l_ta_mt_newindex);
lua_setglobal(lua, "textadept");
@@ -272,9 +278,9 @@ void l_goto_scintilla_window(GtkWidget *editor, int n, bool absolute) {
lua_rawgeti(lua, -1, n);
}
editor = l_checkview(lua, -1);
- if (!closing) l_handle_event("view_before_switch");
+ if (!closing) l_handle_event("view_before_switch", -1);
gtk_widget_grab_focus(editor);
- if (!closing) l_handle_event("view_after_switch");
+ if (!closing) l_handle_event("view_after_switch", -1);
lua_pop(lua, 2); // view table and views
}
@@ -374,16 +380,6 @@ unsigned int l_get_docpointer_index(sptr_t doc) {
return idx;
}
-#define l_set_bufferp(k, v) { \
- lua_pushstring(lua, k); \
- lua_pushinteger(lua, v); \
- lua_rawset(lua, -3); \
-}
-#define l_get_bufferp(k, i) { \
- lua_pushstring(lua, k); \
- lua_rawget(lua, (i < 0) ? i - 1 : i); \
-}
-
/**
* Changes a Scintilla window's document to one in the global 'buffers' table.
* Before doing so, it saves the scroll and caret positions in the current
@@ -414,10 +410,10 @@ void l_goto_scintilla_buffer(GtkWidget *editor, int n, bool absolute) {
lua_rawgeti(lua, -1, n);
}
sptr_t doc = l_checkdocpointer(lua, -1);
- if (!closing) l_handle_event("buffer_before_switch");
+ if (!closing) l_handle_event("buffer_before_switch", -1);
SS(sci, SCI_SETDOCPOINTER, 0, doc);
l_set_buffer_global(sci);
- if (!closing) l_handle_event("buffer_after_switch");
+ if (!closing) l_handle_event("buffer_after_switch", -1);
lua_pop(lua, 2); // buffer table and buffers
}
@@ -509,13 +505,19 @@ bool l_ista2function(const char *table, const char *key) {
* values at the top of the stack. If false, discards the return values.
* Defaults to false.
*/
-bool l_call_function(int nargs, int retn=0, bool keep_return=false) {
+static bool l_call_function(int nargs, int retn=0, bool keep_return=false) {
int ret = lua_pcall(lua, nargs, retn, 0);
if (ret == 0) {
bool result = (retn > 0) ? lua_toboolean(lua, -1) == 1 : true;
if (retn > 0 && !keep_return) lua_pop(lua, retn); // retn
return result;
- } else l_handle_error(NULL);
+ } else {
+ if (focused_editor)
+ l_handle_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;
}
@@ -637,47 +639,48 @@ static void l_check_focused_buffer(lua_State *lua, int narg) {
// Notification/event handlers
/**
- * Handles a Lua error.
- * The main error message is at the top of the Lua stack.
- * @param extramsg An additional error message to display.
- */
-void l_handle_error(const char *extramsg) {
- if (focused_editor && l_ista2function("events", "error")) {
- l_insert(lua, -1); // shift error message down
- if (extramsg) lua_pushstring(lua, extramsg);
- l_call_function(extramsg ? 2 : 1);
- } else {
- printf("Lua Error: %s\n", lua_tostring(lua, -1));
- if (extramsg) printf("%s\n", extramsg);
- }
- lua_settop(lua, 0);
-}
-
-/**
* Handles a Textadept event.
* @param s String event name.
- * @param arg Optional string argument.
- */
-bool l_handle_event(const char *s, const char *arg) {
- if (!l_ista2function("events", s)) return false;
- if (arg) lua_pushstring(lua, arg);
- return l_call_function(arg ? 1 : 0, 1);
-}
-
-/**
- * Handles a Textadept keypress.
- * @param keyval The key value of the key pressed.
- * @param shift Flag indicating whether or not the shift modifier was held.
- * @param control Flag indicating whether or not the control modifier was held.
- * @param alt Flag indicating whether or not the alt modifier was held.
+ * @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.
*/
-bool l_handle_keypress(int keyval, bool shift, bool control, bool alt) {
- if (!l_ista2function("events", "keypress")) return false;
- lua_pushinteger(lua, keyval);
- lua_pushboolean(lua, shift);
- lua_pushboolean(lua, control);
- lua_pushboolean(lua, alt);
- return l_call_function(4, 1);
+bool l_handle_event(const char *s, ...) {
+ if (!l_ista2function("events", "handle")) return false;
+ lua_pushstring(lua, s);
+ int n = 1;
+ va_list ap;
+ va_start(ap, s);
+ int type = va_arg(ap, int);
+ while (type != -1) {
+ void *arg = va_arg(ap, void*);
+ switch(type) {
+ case LUA_TNIL:
+ lua_pushnil(lua);
+ break;
+ case LUA_TBOOLEAN:
+ lua_pushboolean(lua, reinterpret_cast<long>(arg));
+ break;
+ case LUA_TNUMBER:
+ lua_pushinteger(lua, reinterpret_cast<long>(arg));
+ break;
+ case LUA_TSTRING:
+ lua_pushstring(lua, reinterpret_cast<char*>(arg));
+ break;
+ case LUA_TLIGHTUSERDATA:
+ case LUA_TTABLE: {
+ long ref = reinterpret_cast<long>(arg);
+ lua_rawgeti(lua, LUA_REGISTRYINDEX, ref);
+ luaL_unref(lua, LUA_REGISTRYINDEX, ref);
+ break;
+ } default:
+ warn("events.handle: ignored invalid argument type");
+ }
+ n++;
+ type = va_arg(ap, int);
+ }
+ va_end(ap);
+ return l_call_function(n, 1);
}
#define l_pushscninteger(i, n) { \
@@ -740,206 +743,52 @@ void l_ta_popup_context_menu(GdkEventButton *event) {
// Project Manager
/**
- * Creates and pushes a Lua table of parent nodes for the given Project Manager
- * treeview path.
+ * Creates a Lua table of parent nodes for the given Project Manager treeview
+ * path and returns a reference to it.
* The first table item is the PM Entry text, the next items are parents of the
* given node in descending order, and the last item is the given node itself.
+ * The reference can be retrieved using lua_rawgeti.
* @param store The GtkTreeStore of the PM view.
* @param path The GtkTreePath of the node. If NULL, only the PM Entry text is
* contained in the resulting table.
+ * @return int reference to the created table in LUA_REGISTRYINDEX.
*/
-void l_pushpathtable(GtkTreeStore *store, GtkTreePath *path) {
+int l_pm_pathtableref(GtkTreeStore *store, GtkTreePath *path) {
lua_newtable(lua);
lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(pm_entry)));
lua_rawseti(lua, -2, 1);
- if (!path) return;
- GtkTreeIter iter;
- while (gtk_tree_path_get_depth(path) > 0) {
- char *item = 0;
- gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path);
- gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 1, &item, -1);
- lua_pushstring(lua, item);
- lua_rawseti(lua, -2, gtk_tree_path_get_depth(path) + 1);
- g_free(item);
- gtk_tree_path_up(path);
- }
-}
-
-/**
- * Requests and adds contents to the Project Manager view.
- * @param store The GtkTreeStore of the PM view.
- * @param initial_iter An initial GtkTreeIter. If NULL, contents will be added
- * to the treeview root. Otherwise they will be added to this parent node.
- */
-void l_pm_view_fill(GtkTreeStore *store, GtkTreeIter *initial_iter) {
- if (!l_ista2function("pm", "get_contents_for")) return;
- if (initial_iter) {
- GtkTreePath *path =
- gtk_tree_model_get_path(GTK_TREE_MODEL(store), initial_iter);
- l_pushpathtable(store, path);
- gtk_tree_path_free(path);
- } else l_pushpathtable(store, NULL);
- lua_pushboolean(lua, initial_iter != NULL);
- l_call_function(2, 1, true);
- if (!lua_istable(lua, -1)) {
- if (!lua_isnil(lua, -1)) warn("pm.get_contents_for: table expected");
- lua_pop(lua, 1); // non-table return
- return;
- }
-
- if (!initial_iter) gtk_tree_store_clear(store);
- lua_pushnil(lua);
- while (lua_next(lua, -2)) {
- if (lua_istable(lua, -1) && lua_type(lua, -2) == LUA_TSTRING) {
- GtkTreeIter iter, child;
- gtk_tree_store_append(store, &iter, initial_iter);
- gtk_tree_store_set(store, &iter, 1, lua_tostring(lua, -2), -1);
- lua_getfield(lua, -1, "parent");
- if (lua_toboolean(lua, -1)) {
- gtk_tree_store_append(store, &child, &iter);
- gtk_tree_store_set(store, &child, 1, "\0dummy", -1);
- }
- lua_pop(lua, 1); // parent
- lua_getfield(lua, -1, "pixbuf");
- if (lua_isstring(lua, -1))
- gtk_tree_store_set(store, &iter, 0, lua_tostring(lua, -1), -1);
- else if (!lua_isnil(lua, -1))
- warn("pm.fill: non-string pixbuf key ignored");
- lua_pop(lua, 1); // pixbuf
- lua_getfield(lua, -1, "text");
- gtk_tree_store_set(store, &iter, 2, lua_isstring(lua, -1) ?
- lua_tostring(lua, -1) : lua_tostring(lua, -3), -1);
- lua_pop(lua, 1); // display text
- } else warn("pm.fill: string id key must have table value");
- lua_pop(lua, 1); // value
+ if (path) {
+ GtkTreeIter iter;
+ while (gtk_tree_path_get_depth(path) > 0) {
+ char *item = 0;
+ gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path);
+ gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 1, &item, -1);
+ lua_pushstring(lua, item);
+ lua_rawseti(lua, -2, gtk_tree_path_get_depth(path) + 1);
+ g_free(item);
+ gtk_tree_path_up(path);
+ }
}
- lua_pop(lua, 1); // returned table
-
- l_handle_event("pm_view_filled");
+ return luaL_ref(lua, LUA_REGISTRYINDEX);
}
/**
- * Requests and pops up a context menu for a selected Project Manager item.
- * @param store The GtkTreeStore of the PM view.
- * @param path The GtkTreePath of the item.
+ * Requests a popup context menu for a selected Project Manager item.
* @param event The mouse button event.
- * @param callback The GCallback associated with each menu item.
- */
-void l_pm_popup_context_menu(GtkTreeStore *store, GtkTreePath *path,
- GdkEventButton *event, GCallback callback) {
- if (!l_ista2function("pm", "get_context_menu")) return;
- l_pushpathtable(store, path);
- l_call_function(1, 1, true);
- if (lua_istable(lua, -1)) {
- GtkWidget *menu = l_create_gtkmenu(lua, callback, false);
- gtk_widget_show_all(menu);
- gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
- event ? event->button : 0,
- gdk_event_get_time(reinterpret_cast<GdkEvent*>(event)));
- } else warn("pm.get_context_menu: table expected");
- lua_pop(lua, 1); // returned value
-}
-
-/**
- * Performs an action for the selected Project Manager item.
- * @param store The GtkTreeStore of the PM view.
- * @param path The GtkTreePath of the item.
- */
-void l_pm_perform_action(GtkTreeStore *store, GtkTreePath *path) {
- if (!l_ista2function("pm", "perform_action")) return;
- l_pushpathtable(store, path);
- l_call_function(1);
-}
-
-/**
- * Performs a selected menu action from a Project Manager item's context menu.
- * @param store The GtkTreeStore of the PM view.
- * @param path The GtkTreePath of the item.
- * @param id The numeric ID for the menu item.
- */
-void l_pm_perform_menu_action(GtkTreeStore *store, GtkTreePath *path, int id) {
- if (!l_ista2function("pm", "perform_menu_action")) return;
- lua_pushnumber(lua, id);
- l_pushpathtable(store, path);
- l_call_function(2);
-}
-
-// Find/Replace
-
-/**
- * Finds text in the current document.
- * @param ftext The text to find.
- * @param next Flag indicating whether or not to find next. If false, finds
- * previous matches.
- */
-void l_find(const char *ftext, bool next) {
- if (!l_ista2function("find", "find")) return;
- lua_pushstring(lua, ftext);
- lua_pushboolean(lua, next);
- l_call_function(2);
-}
-
-/**
- * Replaces text in the current document.
- * @param rtext The text to replace the found text with.
- */
-void l_find_replace(const char *rtext) {
- if (!l_ista2function("find", "replace")) return;
- lua_pushstring(lua, rtext);
- l_call_function(1);
-}
-
-/**
- * Replaces all found text in the current document.
- * @param ftext The text to find.
- * @param rtext The text to replace the found text with.
- */
-void l_find_replace_all(const char *ftext, const char *rtext) {
- if (!l_ista2function("find", "replace_all")) return;
- lua_pushstring(lua, ftext);
- lua_pushstring(lua, rtext);
- l_call_function(2);
-}
-
-// Command Entry
-
-/**
- * Executes a given command string as Lua code.
- * @param command Lua code to execute.
- */
-void l_ce_command(const char *command) {
- int top = lua_gettop(lua);
- if (luaL_dostring(lua, command) == 0) {
- l_handle_event("update_ui");
- lua_settop(lua, top);
- } else l_handle_error("Error executing command");
-}
-
-/**
- * Requests and adds completions for the Command Entry Completion.
- * @param store The GtkListStore to populate.
*/
-void l_cec_fill(GtkListStore *store) {
- if (!l_ista2function("command_entry", "get_completions_for")) return;
- lua_pushstring(lua, gtk_entry_get_text(GTK_ENTRY(command_entry)));
- l_call_function(1, 1, true);
- if (!lua_istable(lua, -1)) {
- if (!lua_isnil(lua, -1)) warn("ce.get_completions_for: table expected");
- lua_pop(lua, 1); // non-table return
- return;
- }
-
- gtk_list_store_clear(store);
- lua_pushnil(lua);
- while (lua_next(lua, -2)) {
- if (lua_type(lua, -1) == LUA_TSTRING) {
- GtkTreeIter iter;
- gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, lua_tostring(lua, -1), -1);
- } else warn("ce.get_completions_for: non-string value ignored");
- lua_pop(lua, 1); // value
- }
- lua_pop(lua, 1); // returned table
+void l_pm_popup_context_menu(GdkEventButton *event) {
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pm_view));
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(pm_view));
+ if (gtk_tree_selection_get_selected(sel, NULL, &iter))
+ path = gtk_tree_model_get_path(model, &iter);
+ lua_pushlightuserdata(lua, const_cast<GdkEventButton*>(event));
+ int ref = luaL_ref(lua, LUA_REGISTRYINDEX);
+ l_handle_event("pm_context_menu_request", LUA_TTABLE,
+ l_pm_pathtableref(GTK_TREE_STORE(model), path),
+ LUA_TLIGHTUSERDATA, ref, -1);
+ if (path) gtk_tree_path_free(path);
}
// Lua functions (stack maintenence is unnecessary)
@@ -1308,13 +1157,6 @@ static int l_ce_mt_newindex(lua_State *lua) {
// Lua CFunctions. For documentation, consult the LuaDoc.
-static int l_cf_ta_buffer_new(lua_State *lua) {
- new_scintilla_buffer(SCINTILLA(focused_editor), true, true);
- lua_getfield(lua, LUA_REGISTRYINDEX, "buffers");
- lua_rawgeti(lua, -1, lua_objlen(lua, -1));
- return 1;
-}
-
static int l_cf_buffer_delete(lua_State *lua) {
l_check_focused_buffer(lua, 1);
sptr_t doc = l_checkdocpointer(lua, 1);
@@ -1324,10 +1166,17 @@ static int l_cf_buffer_delete(lua_State *lua) {
else
new_scintilla_buffer(SCINTILLA(focused_editor), true, true);
remove_scintilla_buffer(doc);
- l_handle_event("buffer_deleted");
+ l_handle_event("buffer_deleted", -1);
return 0;
}
+static int l_cf_ta_buffer_new(lua_State *lua) {
+ new_scintilla_buffer(SCINTILLA(focused_editor), true, true);
+ lua_getfield(lua, LUA_REGISTRYINDEX, "buffers");
+ lua_rawgeti(lua, -1, lua_objlen(lua, -1));
+ return 1;
+}
+
static int l_cf_buffer_text_range(lua_State *lua) {
l_check_focused_buffer(lua, 1);
#ifndef MAC
@@ -1414,10 +1263,6 @@ static int l_cf_ta_goto_(lua_State *lua, GtkWidget *editor, bool buffer) {
return 0;
}
-static int l_cf_ta_goto_window(lua_State *lua) {
- return l_cf_ta_goto_(lua, focused_editor, false);
-}
-
// If the indexed view is not currently focused, temporarily focus it so calls
// to handlers will not throw 'indexed buffer is not the focused one' error.
static int l_cf_view_goto_buffer(lua_State *lua) {
@@ -1434,12 +1279,12 @@ static int l_cf_view_goto_buffer(lua_State *lua) {
return 0;
}
+static int l_cf_ta_goto_window(lua_State *lua) {
+ return l_cf_ta_goto_(lua, focused_editor, false);
+}
+
static void t_menu_activate(GtkWidget *, gpointer id) {
- int menu_id = GPOINTER_TO_INT(id);
- char *menu_id_str = static_cast<char*>(malloc(sizeof(char) * 12));
- sprintf(menu_id_str, "%i", menu_id);
- l_handle_event("menu_clicked", menu_id_str);
- g_free(menu_id_str);
+ l_handle_event("menu_clicked", LUA_TNUMBER, GPOINTER_TO_INT(id), -1);
}
static int l_cf_ta_gtkmenu(lua_State *lua) {
@@ -1462,8 +1307,17 @@ static int l_cf_ta_iconv(lua_State *lua) {
return 1;
}
+static int l_cf_ta_quit(lua_State *) {
+ GdkEventAny event;
+ event.type = GDK_DELETE;
+ event.window = window->window;
+ event.send_event = TRUE;
+ gdk_event_put(reinterpret_cast<GdkEvent*>(&event));
+ return 0;
+}
+
static int l_cf_ta_reset(lua_State *lua) {
- l_handle_event("resetting");
+ l_handle_event("resetting", -1);
l_init(0, NULL, true);
lua_pushboolean(lua, true);
lua_setglobal(lua, "RESETTING");
@@ -1475,17 +1329,14 @@ static int l_cf_ta_reset(lua_State *lua) {
return 0;
}
-static int l_cf_ta_quit(lua_State *) {
- GdkEventAny event;
- event.type = GDK_DELETE;
- event.window = window->window;
- event.send_event = TRUE;
- gdk_event_put(reinterpret_cast<GdkEvent*>(&event));
+static int l_cf_pm_activate(lua_State *) {
+ g_signal_emit_by_name(G_OBJECT(pm_entry), "activate");
return 0;
}
-static int l_cf_pm_focus(lua_State *) {
- pm_toggle_focus();
+static int l_cf_pm_add_browser(lua_State *lua) {
+ GtkWidget *pm_combo = gtk_widget_get_parent(pm_entry);
+ gtk_combo_box_append_text(GTK_COMBO_BOX(pm_combo), lua_tostring(lua, -1));
return 0;
}
@@ -1495,14 +1346,83 @@ static int l_cf_pm_clear(lua_State *) {
return 0;
}
-static int l_cf_pm_activate(lua_State *) {
- g_signal_emit_by_name(G_OBJECT(pm_entry), "activate");
+static int l_cf_pm_fill(lua_State *lua) {
+ luaL_checktype(lua, 1, LUA_TTABLE);
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(pm_view));
+ GtkTreeStore *store = GTK_TREE_STORE(model);
+ GtkTreeIter initial_iter, *initial_iter_p = NULL;
+ if (lua_gettop(lua) > 1 && lua_type(lua, 2) == LUA_TSTRING &&
+ gtk_tree_model_get_iter_from_string(model, &initial_iter,
+ lua_tostring(lua, 2)))
+ initial_iter_p = &initial_iter;
+ if (!initial_iter_p) gtk_tree_store_clear(store);
+ GtkTreeIter iter, child;
+ lua_pushnil(lua);
+ while (lua_next(lua, 1)) {
+ if (lua_istable(lua, -1) && lua_type(lua, -2) == LUA_TSTRING) {
+ gtk_tree_store_append(store, &iter, initial_iter_p);
+ gtk_tree_store_set(store, &iter, 1, lua_tostring(lua, -2), -1);
+ lua_getfield(lua, -1, "parent");
+ if (lua_toboolean(lua, -1)) {
+ gtk_tree_store_append(store, &child, &iter);
+ gtk_tree_store_set(store, &child, 1, "\0dummy", -1);
+ }
+ lua_pop(lua, 1); // parent
+ lua_getfield(lua, -1, "pixbuf");
+ if (lua_isstring(lua, -1))
+ gtk_tree_store_set(store, &iter, 0, lua_tostring(lua, -1), -1);
+ else if (!lua_isnil(lua, -1))
+ warn("pm.fill: non-string pixbuf key ignored");
+ lua_pop(lua, 1); // pixbuf
+ lua_getfield(lua, -1, "text");
+ gtk_tree_store_set(store, &iter, 2, lua_isstring(lua, -1) ?
+ lua_tostring(lua, -1) : lua_tostring(lua, -3), -1);
+ lua_pop(lua, 1); // display text
+ } else warn("pm.fill: string id key must have table value");
+ lua_pop(lua, 1); // value
+ }
+ if (initial_iter_p) {
+ char *item;
+ gtk_tree_model_iter_nth_child(model, &child, initial_iter_p, 0);
+ gtk_tree_model_get(model, &child, 1, &item, -1);
+ if (strcmp(reinterpret_cast<const char*>(item), "\0dummy") == 0)
+ gtk_tree_store_remove(store, &child);
+ g_free(item);
+ }
return 0;
}
-static int l_cf_pm_add_browser(lua_State *lua) {
- GtkWidget *pm_combo = gtk_widget_get_parent(pm_entry);
- gtk_combo_box_append_text(GTK_COMBO_BOX(pm_combo), lua_tostring(lua, -1));
+static void pm_menu_activate(GtkWidget *, gpointer id) {
+ GtkTreePath *path = NULL;
+ GtkTreeIter iter;
+ GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pm_view));
+ GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(pm_view));
+ if (gtk_tree_selection_get_selected(sel, NULL, &iter))
+ path = gtk_tree_model_get_path(model, &iter);
+ l_handle_event("pm_menu_clicked", LUA_TNUMBER, GPOINTER_TO_INT(id),
+ LUA_TTABLE, l_pm_pathtableref(GTK_TREE_STORE(model), path),
+ -1);
+ if (path) gtk_tree_path_free(path);
+}
+
+static int l_cf_pm_show_context_menu(lua_State *lua) {
+ GdkEventButton *event = NULL;
+ if (lua_gettop(lua) > 1) {
+ if (lua_isuserdata(lua, 2))
+ event = reinterpret_cast<GdkEventButton*>(lua_touserdata(lua, 2));
+ lua_pop(lua, 1); // userdata
+ }
+ luaL_checktype(lua, 1, LUA_TTABLE);
+ GtkWidget *menu = l_create_gtkmenu(lua, G_CALLBACK(pm_menu_activate), false);
+ gtk_widget_show_all(menu);
+ gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
+ event ? event->button : 0,
+ gdk_event_get_time(reinterpret_cast<GdkEvent*>(event)));
+ return 0;
+}
+
+static int l_cf_pm_focus(lua_State *) {
+ pm_toggle_focus();
return 0;
}
@@ -1511,22 +1431,22 @@ static int l_cf_find_focus(lua_State *) {
return 0;
}
-static int l_cf_call_find_next(lua_State *) {
+static int l_cf_find_next(lua_State *) {
g_signal_emit_by_name(G_OBJECT(fnext_button), "clicked");
return 0;
}
-static int l_cf_call_find_prev(lua_State *) {
+static int l_cf_find_prev(lua_State *) {
g_signal_emit_by_name(G_OBJECT(fprev_button), "clicked");
return 0;
}
-static int l_cf_call_replace(lua_State *) {
+static int l_cf_find_replace(lua_State *) {
g_signal_emit_by_name(G_OBJECT(r_button), "clicked");
return 0;
}
-static int l_cf_call_replace_all(lua_State *) {
+static int l_cf_find_replace_all(lua_State *) {
g_signal_emit_by_name(G_OBJECT(ra_button), "clicked");
return 0;
}
@@ -1535,3 +1455,23 @@ static int l_cf_ce_focus(lua_State *) {
ce_toggle_focus();
return 0;
}
+
+static int l_cf_ce_show_completions(lua_State *lua) {
+ luaL_checktype(lua, 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) {
+ GtkTreeIter iter;
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, lua_tostring(lua, -1), -1);
+ } else warn("command_entry.show_completions: non-string value ignored");
+ lua_pop(lua, 1); // value
+ }
+ gtk_entry_completion_complete(completion);
+ return 0;
+}
diff --git a/src/textadept.c b/src/textadept.c
index c0615351..cec9cb3c 100644
--- a/src/textadept.c
+++ b/src/textadept.c
@@ -42,7 +42,7 @@ static int pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a,
static void pm_entry_activated(GtkWidget *, gpointer);
static void pm_entry_changed(GtkComboBoxEntry *, gpointer);
static gbool pm_keypress(GtkWidget *, GdkEventKey *event, gpointer);
-static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter, GtkTreePath *,
+static void pm_row_expanded(GtkTreeView *, GtkTreeIter *, GtkTreePath *path,
gpointer);
static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter, GtkTreePath *,
gpointer);
@@ -50,7 +50,6 @@ static void pm_row_activated(GtkTreeView *view, GtkTreePath *path,
GtkTreeViewColumn *, gpointer);
static gbool pm_buttonpress(GtkTreeView *, GdkEventButton *event, gpointer);
static gbool pm_popup_menu(GtkWidget *, gpointer);
-static void pm_menu_activate(GtkWidget *, gpointer id);
// Find/Replace
GtkWidget *findbox, *find_entry, *replace_entry, *fnext_button, *fprev_button,
@@ -60,6 +59,7 @@ GtkWidget *find_create_ui();
GtkListStore *find_store, *repl_store;
static void find_button_clicked(GtkWidget *button, gpointer);
+static gbool find_entry_keypress(GtkWidget *, GdkEventKey *event, gpointer);
// Command Entry
GtkWidget *command_entry;
@@ -245,7 +245,7 @@ GtkWidget *new_scintilla_window(sptr_t buffer_id) {
new_scintilla_buffer(SCINTILLA(editor), false, false);
} else new_scintilla_buffer(SCINTILLA(editor), false, true);
l_set_view_global(editor);
- l_handle_event("view_new");
+ l_handle_event("view_new", -1);
return editor;
}
@@ -287,8 +287,8 @@ void new_scintilla_buffer(ScintillaObject *sci, bool create, bool addref) {
SS(sci, SCI_ADDREFDOCUMENT, 0, doc);
}
l_set_buffer_global(sci);
- l_handle_event("buffer_new");
- l_handle_event("update_ui"); // update document status
+ l_handle_event("buffer_new", -1);
+ l_handle_event("update_ui", -1); // update document status
}
/**
@@ -422,11 +422,11 @@ void set_statusbar_text(const char *text, bool docbar) {
* @see s_command
*/
static void switch_to_view(GtkWidget *editor) {
- l_handle_event("view_before_switch");
+ l_handle_event("view_before_switch", -1);
focused_editor = editor;
l_set_view_global(editor);
l_set_buffer_global(SCINTILLA(editor));
- l_handle_event("view_after_switch");
+ l_handle_event("view_after_switch", -1);
}
/**
@@ -451,17 +451,18 @@ static void s_command(GtkWidget *editor, gint wParam, gpointer, gpointer) {
/**
* Signal for a Scintilla keypress.
* Collects the modifier states as flags and calls Lua to handle the keypress.
- * @see l_handle_keypress
*/
static gbool s_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
- bool shift = event->state & GDK_SHIFT_MASK;
- bool control = event->state & GDK_CONTROL_MASK;
+ return l_handle_event("keypress",
+ LUA_TNUMBER, event->keyval,
+ LUA_TBOOLEAN, event->state & GDK_SHIFT_MASK,
+ LUA_TBOOLEAN, event->state & GDK_CONTROL_MASK,
#ifndef MAC
- bool alt = event->state & GDK_MOD1_MASK;
+ LUA_TBOOLEAN, event->state & GDK_MOD1_MASK,
#else
- bool alt = event->state & GDK_META_MASK;
+ LUA_TBOOLEAN, event->state & GDK_META_MASK,
#endif
- return l_handle_keypress(event->keyval, shift, control, alt) ? TRUE : FALSE;
+ -1) ? TRUE : FALSE;
}
/**
@@ -505,7 +506,7 @@ static gbool w_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
* @see l_close
*/
static gbool w_exit(GtkWidget *, GdkEventAny *, gpointer) {
- if (!l_handle_event("quit")) return TRUE;
+ if (!l_handle_event("quit", -1)) return TRUE;
l_close();
scintilla_release_resources();
gtk_main_quit();
@@ -516,7 +517,6 @@ static gbool w_exit(GtkWidget *, GdkEventAny *, gpointer) {
/**
* Signal for an Open Document AppleEvent.
* Generates a 'appleevent_odoc' event for each document sent.
- * @see l_handle_event
*/
static OSErr w_ae_open(const AppleEvent *event, AppleEvent *, long) {
AEDescList file_list;
@@ -529,7 +529,7 @@ static OSErr w_ae_open(const AppleEvent *event, AppleEvent *, long) {
NULL);
CFURLRef url = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsref);
if (url) {
- l_handle_event("appleevent_odoc", CFURL_TO_STR(url));
+ l_handle_event("appleevent_odoc", LUA_TSTRING, CFURL_TO_STR(url), -1);
CFRelease(url);
}
}
@@ -657,19 +657,19 @@ static int pm_sort_iter_compare_func(GtkTreeModel *model, GtkTreeIter *a,
/**
* Signal for the activation of the Project Manager entry.
* Requests contents for the Project Manager.
- * @see l_pm_view_fill
*/
static void pm_entry_activated(GtkWidget *, gpointer) {
- l_pm_view_fill(pm_store, NULL);
+ l_handle_event("pm_contents_request", LUA_TTABLE,
+ l_pm_pathtableref(pm_store, NULL), LUA_TNIL, 0, -1);
}
/**
* Signal for a change of the text in the Project Manager entry.
* Requests contents for the Project Manager.
- * @see l_pm_view_fill
*/
static void pm_entry_changed(GtkComboBoxEntry *, gpointer) {
- l_pm_view_fill(pm_store, NULL);
+ l_handle_event("pm_contents_request", LUA_TTABLE,
+ l_pm_pathtableref(pm_store, NULL), LUA_TNIL, 0, -1);
}
/**
@@ -689,18 +689,13 @@ static gbool pm_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
* Requests contents for a Project Manager parent node being opened.
* Since a parent is given a dummy child by default in order to indicate that
* it is a parent, that dummy child is removed.
- * @see l_pm_view_fill
*/
-static void pm_row_expanded(GtkTreeView *, GtkTreeIter *iter, GtkTreePath *,
+static void pm_row_expanded(GtkTreeView *, GtkTreeIter *, GtkTreePath *path,
gpointer) {
- l_pm_view_fill(pm_store, iter);
- GtkTreeIter child;
- char *item;
- gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(pm_store), &child, iter, 0);
- gtk_tree_model_get(GTK_TREE_MODEL(pm_store), &child, 1, &item, -1);
- if (strcmp(reinterpret_cast<const char*>(item), "\0dummy") == 0)
- gtk_tree_store_remove(pm_store, &child);
- g_free(item);
+ char *path_str = gtk_tree_path_to_string(path);
+ l_handle_event("pm_contents_request", LUA_TTABLE,
+ l_pm_pathtableref(pm_store, path), LUA_TSTRING, path_str, -1);
+ g_free(path_str);
}
/**
@@ -724,27 +719,13 @@ static void pm_row_collapsed(GtkTreeView *, GtkTreeIter *iter, GtkTreePath *,
* Performs the appropriate action on a selected Project Manager node.
* If the node is a collapsed parent, it is expanded; otherwise the parent is
* collapsed. If the node is not a parent at all, a Lua action is performed.
- * @see l_pm_perform_action
*/
static void pm_row_activated(GtkTreeView *view, GtkTreePath *path,
GtkTreeViewColumn *, gpointer) {
if (!gtk_tree_view_expand_row(view, path, FALSE))
if (!gtk_tree_view_collapse_row(view, path))
- l_pm_perform_action(pm_store, path);
-}
-
-/**
- * Helper function to return the path of the selected Project Manager view item
- * (if any).
- * The returned GtkTreePath must be freed if it is not NULL.
- */
-static GtkTreePath *pm_view_get_selection_path() {
- GtkTreeIter iter;
- GtkTreePath *path = 0;
- GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(pm_view));
- if (gtk_tree_selection_get_selected(sel, NULL, &iter))
- path = gtk_tree_model_get_path(GTK_TREE_MODEL(pm_store), &iter);
- return path;
+ l_handle_event("pm_item_selected", LUA_TTABLE,
+ l_pm_pathtableref(pm_store, path), -1);
}
/**
@@ -754,9 +735,7 @@ static GtkTreePath *pm_view_get_selection_path() {
*/
static gbool pm_buttonpress(GtkTreeView *, GdkEventButton *event, gpointer) {
if (event->type != GDK_BUTTON_PRESS || event->button != 3) return FALSE;
- GtkTreePath *path = pm_view_get_selection_path();
- l_pm_popup_context_menu(pm_store, path, event, G_CALLBACK(pm_menu_activate));
- if (path) gtk_tree_path_free(path);
+ l_pm_popup_context_menu(event);
return TRUE;
}
@@ -766,24 +745,10 @@ static gbool pm_buttonpress(GtkTreeView *, GdkEventButton *event, gpointer) {
* @see l_pm_popup_context_menu
*/
static gbool pm_popup_menu(GtkWidget *, gpointer) {
- GtkTreePath *path = pm_view_get_selection_path();
- l_pm_popup_context_menu(pm_store, path, NULL, G_CALLBACK(pm_menu_activate));
- if (path) gtk_tree_path_free(path);
+ l_pm_popup_context_menu(NULL);
return TRUE;
}
-/**
- * Signal for a selected Project Manager menu item.
- * Performs a Lua action for a selected menu item.
- * @param id The numeric ID for the menu item.
- * @see l_pm_perform_menu_action
- */
-static void pm_menu_activate(GtkWidget *, gpointer id) {
- GtkTreePath *path = pm_view_get_selection_path();
- l_pm_perform_menu_action(pm_store, path, GPOINTER_TO_INT(id));
- if (path) gtk_tree_path_free(path);
-}
-
// Find/Replace
#define attach(w, x1, x2, y1, y2, xo, yo, xp, yp) \
@@ -840,6 +805,7 @@ 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(find_entry, "key-press-event", find_entry_keypress);
signal(fnext_button, "clicked", find_button_clicked);
signal(fprev_button, "clicked", find_button_clicked);
signal(r_button, "clicked", find_button_clicked);
@@ -906,20 +872,30 @@ static void find_button_clicked(GtkWidget *button, gpointer) {
if (strlen(find_text) == 0) return;
if (button == fnext_button || button == fprev_button) {
find_add_to_history(find_text, find_store);
- l_find(find_text, button == fnext_button);
+ l_handle_event("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_find_replace(repl_text);
- l_find(find_text, true);
- } else l_find_replace_all(find_text, repl_text);
+ l_handle_event("replace", LUA_TSTRING, repl_text, -1);
+ l_handle_event("find", LUA_TSTRING, find_text, LUA_TBOOLEAN, 1, -1);
+ } else
+ l_handle_event("replace_all", LUA_TSTRING, find_text, LUA_TSTRING,
+ repl_text, -1);
}
}
+/**
+ * Signal for a Find entry keypress.
+ */
+static gbool find_entry_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
+ return l_handle_event("find_keypress", LUA_TNUMBER, event->keyval, -1);
+}
+
// Command Entry
/**
- * Toggles focus between a Scintilla window and the Lua command entry.
+ * Toggles focus between a Scintilla window and the Command Entry.
* When the entry is visible, the statusbars are temporarily hidden.
*/
void ce_toggle_focus() {
@@ -977,30 +953,18 @@ static gbool cec_match_selected(GtkEntryCompletion *, GtkTreeModel *model,
// Signals
/**
- * Signal for the 'enter' key being pressed in the Lua command entry.
- * Evaluates the input text as Lua code.
+ * Signal for the 'enter' key being pressed in the Command Entry.
*/
static void c_activated(GtkWidget *widget, gpointer) {
- l_ce_command(gtk_entry_get_text(GTK_ENTRY(widget)));
+ l_handle_event("command_entry_command", LUA_TSTRING,
+ gtk_entry_get_text(GTK_ENTRY(widget)), -1);
ce_toggle_focus();
}
/**
- * Signal for a keypress inside the Lua command entry.
- * Currently handled keypresses:
- * - Escape - Hide the completion buffer if it is open.
- * - Tab - Display possible completions.
+ * Signal for a keypress inside the Command Entry.
*/
static gbool c_keypress(GtkWidget *, GdkEventKey *event, gpointer) {
- if (event->state == 0)
- switch(event->keyval) {
- case 0xff1b:
- ce_toggle_focus();
- return TRUE;
- case 0xff09:
- l_cec_fill(cec_store);
- gtk_entry_completion_complete(command_entry_completion);
- return TRUE;
- }
- return FALSE;
+ return l_handle_event("command_entry_keypress", LUA_TNUMBER, event->keyval,
+ -1);
}
diff --git a/src/textadept.h b/src/textadept.h
index 666c361d..73064fdf 100644
--- a/src/textadept.h
+++ b/src/textadept.h
@@ -3,6 +3,7 @@
#ifndef TEXTADEPT_H
#define TEXTADEPT_H
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -62,23 +63,11 @@ void l_remove_scintilla_buffer(sptr_t doc);
void l_goto_scintilla_buffer(GtkWidget *editor, int n, bool absolute);
void l_set_buffer_global(ScintillaObject *sci);
-void l_handle_error(const char *errmsg);
-bool l_handle_event(const char *e, const char *arg=NULL);
-bool l_handle_keypress(int keyval, bool shift, bool control, bool alt);
+bool l_handle_event(const char *e, ...);
void l_handle_scnnotification(SCNotification *n);
void l_ta_popup_context_menu(GdkEventButton *event);
-void l_pm_view_fill(GtkTreeStore *store, GtkTreeIter *initial_iter);
-void l_pm_perform_action(GtkTreeStore *store, GtkTreePath *path);
-void l_pm_popup_context_menu(GtkTreeStore *store, GtkTreePath *path,
- GdkEventButton *event, GCallback callback);
-void l_pm_perform_menu_action(GtkTreeStore *store, GtkTreePath *path, int id);
-
-void l_find(const char *ftext, bool next);
-void l_find_replace(const char *rtext);
-void l_find_replace_all(const char *ftext, const char *rtext);
-
-void l_ce_command(const char *command);
-void l_cec_fill(GtkListStore *store);
+int l_pm_pathtableref(GtkTreeStore *store, GtkTreePath *path);
+void l_pm_popup_context_menu(GdkEventButton *event);
#endif