aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--core/locale.conf11
-rw-r--r--modules/textadept/find.lua21
-rw-r--r--src/textadept.c247
3 files changed, 205 insertions, 74 deletions
diff --git a/core/locale.conf b/core/locale.conf
index c9700553..a5ce805d 100644
--- a/core/locale.conf
+++ b/core/locale.conf
@@ -94,6 +94,17 @@ An error occured: = An error occured:
%_OK = _OK
%_Cancel = _Cancel
replacement(s) made = replacement(s) made
+% For ncurses:
+Find: = Find:
+Replace: = Replace:
+[Next] = [Next]
+[Prev] = [Prev]
+[Replace] = [Replace]
+[All] = [All]
+Case(F1) = Case(F1)
+Word(F2) = Word(F2)
+Pattern(F3) = Pattern(F3)
+Files(F4) = Files(F4)
% modules/textadept/keys.lua
Lexer = Lexer
diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua
index 399eda46..1a9d2cfb 100644
--- a/modules/textadept/find.lua
+++ b/modules/textadept/find.lua
@@ -50,16 +50,17 @@ local find = gui.find
module('gui.find')]]
local _L = _L
-find.find_label_text = _L['_Find:']
-find.replace_label_text = _L['R_eplace:']
-find.find_next_button_text = _L['Find _Next']
-find.find_prev_button_text = _L['Find _Prev']
-find.replace_button_text = _L['_Replace']
-find.replace_all_button_text = _L['Replace _All']
-find.match_case_label_text = _L['_Match case']
-find.whole_word_label_text = _L['_Whole word']
-find.lua_pattern_label_text = _L['_Lua pattern']
-find.in_files_label_text = _L['_In files']
+find.find_label_text = not NCURSES and _L['_Find:'] or _L['Find:']
+find.replace_label_text = not NCURSES and _L['R_eplace:'] or _L['Replace:']
+find.find_next_button_text = not NCURSES and _L['Find _Next'] or _L['[Next]']
+find.find_prev_button_text = not NCURSES and _L['Find _Prev'] or _L['[Prev]']
+find.replace_button_text = not NCURSES and _L['_Replace'] or _L['[Replace]']
+find.replace_all_button_text = not NCURSES and _L['Replace _All'] or _L['[All]']
+find.match_case_label_text = not NCURSES and _L['_Match case'] or _L['Case(F1)']
+find.whole_word_label_text = not NCURSES and _L['_Whole word'] or _L['Word(F2)']
+find.lua_pattern_label_text = not NCURSES and _L['_Lua pattern'] or
+ _L['Pattern(F3)']
+find.in_files_label_text = not NCURSES and _L['_In files'] or _L['Files(F4)']
local MARK_FIND = _SCINTILLA.next_marker_number()
local MARK_FIND_COLOR = 0x4D9999
diff --git a/src/textadept.c b/src/textadept.c
index 2f18e8aa..924fd254 100644
--- a/src/textadept.c
+++ b/src/textadept.c
@@ -58,16 +58,15 @@ typedef GtkWidget Scintilla;
#define focus_view(v) \
SS(focused_view, SCI_SETFOCUS, 0, 0), SS(v, SCI_SETFOCUS, 1, 0)
#endif
-#define copy(s) strcpy(malloc(strlen(s) + 1), s)
// Window
-char *textadept_home = NULL;
-Scintilla *focused_view;
+static char *textadept_home = NULL;
+static Scintilla *focused_view;
#if GTK
-GtkWidget *window, *menubar, *statusbar[2];
-GtkAccelGroup *accel;
+static GtkWidget *window, *menubar, *statusbar[2];
+static GtkAccelGroup *accel;
#if __OSX__
-GtkOSXApplication *osxapp;
+static GtkOSXApplication *osxapp;
#endif
#endif
static void new_buffer(sptr_t);
@@ -75,50 +74,51 @@ static Scintilla *new_view(sptr_t);
// Find/Replace
#if GTK
-typedef GtkWidget FindBox;
-GtkWidget *find_entry, *replace_entry, *flabel, *rlabel;
+static GtkWidget *findbox, *find_entry, *replace_entry, *flabel, *rlabel;
#define find_text gtk_entry_get_text(GTK_ENTRY(find_entry))
#define repl_text gtk_entry_get_text(GTK_ENTRY(replace_entry))
typedef GtkWidget * FindButton;
-FindButton fnext_button, fprev_button, r_button, ra_button;
-typedef GtkWidget * Option;
-Option match_case, whole_word, lua_pattern, in_files;
+static FindButton fnext_button, fprev_button, r_button, ra_button;
+static GtkWidget *match_case, *whole_word, *lua_pattern, *in_files;
typedef GtkListStore ListStore;
-ListStore *find_store, *repl_store;
+static ListStore *find_store, *repl_store;
#elif NCURSES
-typedef WINDOW FindBox;
-char *find_text = NULL, *repl_text = NULL, *flabel = NULL, *rlabel = NULL;
+static CDKSCREEN *findbox = NULL;
+static CDKENTRY *find_entry, *replace_entry;
+static char *find_text = NULL, *repl_text = NULL, *flabel = NULL,
+ *rlabel = NULL;
typedef int FindButton;
-enum FindButton { fnext_button, fprev_button, r_button, ra_button };
-typedef int Option;
-Option match_case = 0, whole_word = 0, lua_pattern = 0, in_files = 0;
+enum FindButton { fnext_button, r_button, fprev_button, ra_button };
+static int match_case = FALSE, whole_word = FALSE, lua_pattern = FALSE,
+ in_files = FALSE;
+static char *button_labels[4] = { NULL, NULL, NULL, NULL },
+ *option_labels[4] = { NULL, NULL, NULL, NULL };
typedef char * ListStore;
-ListStore find_store[10] = {
+static ListStore find_store[10] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
}, repl_store[10] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
#endif
-FindBox *findbox;
// Command Entry
#if GTK
-GtkWidget *command_entry;
+static GtkWidget *command_entry;
#define command_text gtk_entry_get_text(GTK_ENTRY(command_entry))
-GtkListStore *cc_store;
-GtkEntryCompletion *command_entry_completion;
+static GtkListStore *cc_store;
+static GtkEntryCompletion *command_entry_completion;
#elif NCURSES
-CDKENTRY *command_entry = NULL;
-char *command_text = NULL;
+static CDKENTRY *command_entry = NULL;
+static char *command_text = NULL;
#endif
// Lua
-lua_State *lua;
+static lua_State *lua;
#if NCURSES
-int quit = FALSE;
+static int quit = FALSE;
#endif
-int closing = FALSE;
-char *statusbar_text = NULL;
+static int closing = FALSE;
+static char *statusbar_text = NULL;
static int tVOID = 0, tINT = 1, tLENGTH = 2, /*tPOSITION = 3, tCOLOUR = 4,*/
tBOOL = 5, tKEYMOD = 6, tSTRING = 7, tSTRINGRESULT = 8;
static int lL_init(lua_State *, int, char **, int);
@@ -238,6 +238,18 @@ static int a_command_line(GApplication *app, GApplicationCommandLine *cmdline,
#endif
/**
+ * Frees the given string's current value, if any, and copies the given value to
+ * it.
+ * The given string must be freed when finished.
+ * @param s The address of the string to copy value to.
+ * @param value The new value to copy. It may be freed immediately.
+ */
+static void fcopy(char **s, const char *value) {
+ if (*s) free(*s);
+ *s = strcpy(malloc(strlen(value) + 1), value);
+}
+
+/**
* 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 ListStore to add the text to.
@@ -259,8 +271,8 @@ static void find_add_to_history(const char *text, ListStore *store) {
#elif NCURSES
if (!store[0] || strcmp(text, store[0]) != 0) {
if (store[9]) free(store[9]);
- for (int i = 0; i < 9; i++) store[i + 1] = store[i];
- store[0] = copy(text);
+ for (int i = 9; i > 0; i--) store[i] = store[i - 1];
+ store[0] = NULL, fcopy(&store[0], text);
}
#endif
}
@@ -294,6 +306,62 @@ static int lfind_prev(lua_State *L) {
return (f_clicked(fprev_button, NULL), 0);
}
+#if NCURSES
+/**
+ * Signal for a tab keypress in a CDKEntry to inject a tab in a CDKButtonbox.
+ */
+static int buttonbox_tab(EObjectType cdkType, void *object, void *data,
+ chtype key) {
+ injectCDKButtonbox((CDKBUTTONBOX *)data, key);
+ return TRUE;
+}
+
+/**
+ * Signal for a keypress in the Find/Replace Entry.
+ * For F1-F4 keys, toggle the respective search option.
+ * For Up/Down keys, scroll through find/replace history.
+ */
+static int entry_keypress(EObjectType cdkType, void *object, void *data,
+ chtype key) {
+ switch (key) {
+ case KEY_F(1):
+ match_case = !match_case, option_labels[0] += match_case ? -4 : 4;
+ break;
+ case KEY_F(2):
+ whole_word = !whole_word, option_labels[1] += whole_word ? -4 : 4;
+ break;
+ case KEY_F(3):
+ lua_pattern = !lua_pattern, option_labels[2] += lua_pattern ? -4 : 4;
+ break;
+ case KEY_F(4):
+ in_files = !in_files, option_labels[3] += in_files ? -4 : 4;
+ break;
+ case KEY_UP:
+ case KEY_DOWN: {
+ CDKENTRY *entry = (CDKENTRY *)object;
+ ListStore *store = (entry == find_entry) ? find_store : repl_store;
+ char *value = getCDKEntryValue(entry);
+ int i;
+ for (i = 9; i >= 0; i--)
+ if (store[i] && strcmp(store[i], value) == 0) break;
+ (key == KEY_UP) ? i++ : i--;
+ if (i >= 0 && i <= 9 && store[i])
+ setCDKEntryValue(entry, store[i]), drawCDKEntry(entry, FALSE);
+ break;
+ }
+ }
+ if (key >= KEY_F(1) && key <= KEY_F(4)) {
+ CDKBUTTONBOX **optionbox = (CDKBUTTONBOX **)data;
+ int width = (*optionbox)->boxWidth - 1;
+ destroyCDKButtonbox(*optionbox);
+ *optionbox = newCDKButtonbox(findbox, RIGHT, TOP, 2, width, NULL, 2, 2,
+ option_labels, 4, A_NORMAL, FALSE, FALSE);
+ drawCDKButtonbox(*optionbox, FALSE);
+ }
+ return TRUE;
+}
+#endif
+
/** `find.focus()` Lua function. */
static int lfind_focus(lua_State *L) {
#if GTK
@@ -306,7 +374,45 @@ static int lfind_focus(lua_State *L) {
gtk_widget_hide(findbox);
}
#elif NCURSES
- // TODO: toggle findbox focus.
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+ if (!findbox) {
+ findbox = initCDKScreen(newwin(2, 0, LINES - 3, 0));
+ int b_width = max(strlen(button_labels[0]), strlen(button_labels[1])) +
+ max(strlen(button_labels[2]), strlen(button_labels[3])) + 3;
+ int o_width = max(strlen(option_labels[0]), strlen(option_labels[1])) +
+ max(strlen(option_labels[2]), strlen(option_labels[3])) + 3;
+ int l_width = max(strlen(flabel), strlen(rlabel));
+ int e_width = COLS - o_width - b_width - l_width - 1;
+ find_entry = newCDKEntry(findbox, l_width - strlen(flabel), TOP, NULL,
+ flabel, A_NORMAL, '_', vMIXED, e_width, 0, 64,
+ FALSE, FALSE);
+ replace_entry = newCDKEntry(findbox, l_width - strlen(rlabel), BOTTOM, NULL,
+ rlabel, A_NORMAL, '_', vMIXED, e_width, 0, 64,
+ FALSE, FALSE);
+ CDKBUTTONBOX *buttonbox = newCDKButtonbox(findbox, COLS - o_width - b_width,
+ TOP, 2, b_width, NULL, 2, 2,
+ button_labels, 4, A_REVERSE,
+ FALSE, FALSE);
+ CDKBUTTONBOX *optionbox = newCDKButtonbox(findbox, RIGHT, TOP, 2, o_width,
+ NULL, 2, 2, option_labels, 4,
+ A_NORMAL, FALSE, FALSE);
+ bindCDKObject(vENTRY, find_entry, KEY_TAB, buttonbox_tab, buttonbox);
+ bindCDKObject(vENTRY, find_entry, KEY_BTAB, buttonbox_tab, buttonbox);
+ setCDKEntryPostProcess(find_entry, entry_keypress, &optionbox);
+ bindCDKObject(vENTRY, replace_entry, KEY_TAB, buttonbox_tab, buttonbox);
+ bindCDKObject(vENTRY, replace_entry, KEY_BTAB, buttonbox_tab, buttonbox);
+ setCDKEntryPostProcess(replace_entry, entry_keypress, &optionbox);
+ drawCDKEntry(replace_entry, FALSE);
+ drawCDKButtonbox(buttonbox, FALSE), drawCDKButtonbox(optionbox, FALSE);
+ while (activateCDKEntry(find_entry, NULL)) {
+ fcopy(&find_text, getCDKEntryValue(find_entry));
+ fcopy(&repl_text, getCDKEntryValue(replace_entry));
+ f_clicked(getCDKButtonboxCurrentButton(buttonbox), NULL);
+ }
+ destroyCDKEntry(find_entry), destroyCDKEntry(replace_entry);
+ destroyCDKButtonbox(buttonbox), destroyCDKButtonbox(optionbox);
+ delwin(findbox->window), destroyCDKScreen(findbox), findbox = NULL;
+ }
#endif
return 0;
}
@@ -348,27 +454,29 @@ static int lfind__index(lua_State *L) {
#if GTK
#define toggle(w, b) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), b)
+#define set_label_text(l, t) gtk_label_set_text_with_mnemonic(GTK_LABEL(l), t)
+#define set_button_label(b, l) gtk_button_set_label(GTK_BUTTON(b), l)
#elif NCURSES
#define toggle(w, b) w = b
+#define set_label_text(l, t) fcopy(&l, t)
+#define set_button_label(b, l) fcopy(&button_labels[b], l)
#endif
/** `find.__newindex` Lua metatable. */
static int lfind__newindex(lua_State *L) {
const char *key = lua_tostring(L, 2);
- if (strcmp(key, "find_entry_text") == 0) {
+ if (strcmp(key, "find_entry_text") == 0)
#if GTK
gtk_entry_set_text(GTK_ENTRY(find_entry), lua_tostring(L, 3));
#elif NCURSES
- if (find_text) free(find_text);
- find_text = copy(lua_tostring(L, 3));
+ fcopy(&find_text, lua_tostring(L, 3));
#endif
- } else if (strcmp(key, "replace_entry_text") == 0) {
+ else if (strcmp(key, "replace_entry_text") == 0)
#if GTK
gtk_entry_set_text(GTK_ENTRY(replace_entry), lua_tostring(L, 3));
#elif NCURSES
- if (repl_text) free(repl_text);
- repl_text = copy(lua_tostring(L, 3));
+ fcopy(&repl_text, lua_tostring(L, 3));
#endif
- } else if (strcmp(key, "match_case") == 0)
+ else if (strcmp(key, "match_case") == 0)
toggle(match_case, lua_toboolean(L, -1));
else if (strcmp(key, "whole_word") == 0)
toggle(whole_word, lua_toboolean(L, -1));
@@ -376,19 +484,19 @@ static int lfind__newindex(lua_State *L) {
toggle(lua_pattern, lua_toboolean(L, -1));
else if (strcmp(key, "in_files") == 0)
toggle(in_files, lua_toboolean(L, -1));
-#if GTK
else if (strcmp(key, "find_label_text") == 0)
- gtk_label_set_text_with_mnemonic(GTK_LABEL(flabel), lua_tostring(L, 3));
+ set_label_text(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));
+ set_label_text(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));
+ set_button_label(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));
+ set_button_label(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));
+ set_button_label(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));
+ set_button_label(ra_button, lua_tostring(L, 3));
+#if GTK
else if (strcmp(key, "match_case_label_text") == 0)
gtk_button_set_label(GTK_BUTTON(match_case), lua_tostring(L, 3));
else if (strcmp(key, "whole_word_label_text") == 0)
@@ -397,6 +505,24 @@ static int lfind__newindex(lua_State *L) {
gtk_button_set_label(GTK_BUTTON(lua_pattern), lua_tostring(L, 3));
else if (strcmp(key, "in_files_label_text") == 0)
gtk_button_set_label(GTK_BUTTON(in_files), lua_tostring(L, 3));
+#elif NCURSES
+ // Prepend "</R>" to each option label because pointer arithmetic will be
+ // used to make the "</R>" visible or invisible (thus highlighting or
+ // unhighlighting the label) depending on whether or not the option is
+ // enabled by the user.
+ else if (strcmp(key, "match_case_label_text") == 0) {
+ lua_pushstring(L, "</R>"), lua_pushvalue(L, 3), lua_concat(L, 2);
+ fcopy(&option_labels[0], lua_tostring(L, -1)), option_labels[0] += 4;
+ } else if (strcmp(key, "whole_word_label_text") == 0) {
+ lua_pushstring(L, "</R>"), lua_pushvalue(L, 3), lua_concat(L, 2);
+ fcopy(&option_labels[1], lua_tostring(L, -1)), option_labels[1] += 4;
+ } else if (strcmp(key, "lua_pattern_label_text") == 0) {
+ lua_pushstring(L, "</R>"), lua_pushvalue(L, 3), lua_concat(L, 2);
+ fcopy(&option_labels[2], lua_tostring(L, -1)), option_labels[2] += 4;
+ } else if (strcmp(key, "in_files_label_text") == 0) {
+ lua_pushstring(L, "</R>"), lua_pushvalue(L, 3), lua_concat(L, 2);
+ fcopy(&option_labels[3], lua_tostring(L, -1)), option_labels[3] += 4;
+ }
#endif
else
lua_rawset(L, 1);
@@ -416,12 +542,11 @@ static int lce_focus(lua_State *L) {
#elif NCURSES
if (!command_entry) {
CDKSCREEN *screen = initCDKScreen(newwin(1, 0, LINES - 2, 0));
- command_entry = newCDKEntry(screen, LEFT, TOP, "", "", A_NORMAL, '_',
+ command_entry = newCDKEntry(screen, LEFT, TOP, NULL, NULL, A_NORMAL, '_',
vMIXED, 0, 0, 256, FALSE, FALSE);
setCDKEntryValue(command_entry, command_text);
if (activateCDKEntry(command_entry, NULL)) {
- if (command_text) free(command_text);
- command_text = copy(getCDKEntryValue(command_entry));
+ fcopy(&command_text, getCDKEntryValue(command_entry));
lL_event(lua, "command_entry_command", LUA_TSTRING, command_text, -1);
}
destroyCDKEntry(command_entry), command_entry = NULL;
@@ -450,6 +575,8 @@ static int lce_show_completions(lua_State *L) {
lua_pop(L, 1); // value
}
gtk_entry_completion_complete(completion);
+#elif NCURSES
+ // TODO: cycle through completions.
#endif
return 0;
}
@@ -467,14 +594,14 @@ static int lce__index(lua_State *L) {
/** `command_entry.__newindex` Lua metatable. */
static int lce__newindex(lua_State *L) {
const char *key = lua_tostring(L, 2);
- if (strcmp(key, "entry_text") == 0) {
+ if (strcmp(key, "entry_text") == 0)
#if GTK
gtk_entry_set_text(GTK_ENTRY(command_entry), lua_tostring(L, 3));
#elif NCURSES
- if (command_text) free(command_text);
- command_text = copy(lua_tostring(L, 3));
+ fcopy(&command_text, lua_tostring(L, 3));
#endif
- } else lua_rawset(L, 1);
+ else
+ lua_rawset(L, 1);
return 0;
}
@@ -731,7 +858,7 @@ static int lgui__index(lua_State *L) {
lua_pushstring(L, text ? text : "");
if (text) free(text);
#elif NCURSES
- lua_pushstring(L, ""); // TODO: get Xclipboard text?
+ lua_pushstring(L, ""); // TODO: get Xclipboard text?, CDK GPasteBuffer?
#endif
} else if (strcmp(key, "size") == 0) {
#if GTK
@@ -774,8 +901,7 @@ static int lgui__newindex(lua_State *L) {
else if (strcmp(key, "docstatusbar_text") == 0)
set_statusbar_text(lua_tostring(L, 3), 1);
else if (strcmp(key, "statusbar_text") == 0) {
- if (statusbar_text) free(statusbar_text);
- statusbar_text = copy(luaL_optstring(L, 3, ""));
+ fcopy(&statusbar_text, luaL_optstring(L, 3, ""));
set_statusbar_text(statusbar_text, 0);
} else if (strcmp(key, "menubar") == 0) {
#if GTK
@@ -1845,11 +1971,11 @@ static Scintilla *new_view(sptr_t doc) {
return view;
}
+#if GTK
/**
* Creates the Find box.
*/
-static FindBox *new_findbox() {
-#if GTK
+static GtkWidget *new_findbox() {
#define attach(w, x1, x2, y1, y2, xo, yo, xp, yp) \
gtk_table_attach(GTK_TABLE(findbox), w, x1, x2, y1, y2, xo, yo, xp, yp)
#define EXPAND_FILL (GtkAttachOptions)(GTK_EXPAND | GTK_FILL)
@@ -1914,12 +2040,10 @@ static FindBox *new_findbox() {
gtk_widget_set_can_focus(whole_word, FALSE);
gtk_widget_set_can_focus(lua_pattern, FALSE);
gtk_widget_set_can_focus(in_files, FALSE);
-#endif
return findbox;
}
-#if GTK
/**
* Signal for the 'enter' key being pressed in the Command Entry.
*/
@@ -2160,11 +2284,6 @@ int main(int argc, char **argv) {
break;
default: continue;
}
-// if (c == SCK_ESCAPE && gtk_widget_get_visible(findbox) &&
-// !gtk_widget_has_focus(command_entry)) {
-// gtk_widget_hide(findbox);
-// gtk_widget_grab_focus(focused_view);
-// } else
curs_set(0); // disable cursor when Scintilla has focus
if (!lL_event(lua, "keypress", LUA_TNUMBER, c, LUA_TBOOLEAN,
key.modifiers & TERMKEY_KEYMOD_SHIFT, LUA_TBOOLEAN,