diff options
-rw-r--r-- | core/.ui.dialogs.luadoc | 26 | ||||
-rw-r--r-- | core/locale.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.ar.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.de.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.es.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.fr.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.it.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.pl.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.ru.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.sv.conf | 4 | ||||
-rw-r--r-- | core/locales/locale.zh.conf | 4 | ||||
-rw-r--r-- | core/ui.lua | 8 | ||||
-rw-r--r-- | doc/manual.md | 37 | ||||
-rw-r--r-- | modules/textadept/find.lua | 84 | ||||
-rw-r--r-- | src/Makefile | 6 | ||||
-rw-r--r-- | src/textadept.c | 31 | ||||
-rw-r--r-- | test/test.lua | 27 |
17 files changed, 147 insertions, 112 deletions
diff --git a/core/.ui.dialogs.luadoc b/core/.ui.dialogs.luadoc index 0a6579d1..08a810d1 100644 --- a/core/.ui.dialogs.luadoc +++ b/core/.ui.dialogs.luadoc @@ -325,6 +325,32 @@ function filesave(options) end function textbox(options) end --- +-- Displays a progressbar dialog defined by dialog options table *options* and +-- updates from function *f*. +-- Returns "stopped" if *options*.`stoppable` is `true` and the user clicked the +-- "Stop" button. Otherwise, returns `nil`. +-- @param options Table of key-value option pairs for the progressbar dialog. +-- +-- * `title`: The dialog's title text. +-- * `percent`: The initial progressbar percentage between 0 and 100. +-- * `text`: The initial progressbar display text (GTK only). +-- * `indeterminate`: Show the progress bar as "busy", with no percentage +-- updates. +-- * `stoppable`: Show the "Stop" button. +-- * `width`: The dialog's pixel width. +-- * `height`: The dialog's pixel height. +-- @param f Function repeatedly called to do work and provide progress updates. +-- The function is called without arguments and must return either `nil`, +-- which indicates work is complete, or a progress percentage number in the +-- range 0-100 and optional string text to display (GTK only). If the text +-- is either "stop disable" or "stop enable" and *options*.`stoppable` is +-- `true`, the "Stop" button is disabled or enabled, respectively. +-- @return nil or "stopped" +-- @usage ui.dialogs.progressbar({stoppable = true}, +-- function() if work() then return percent, status else return nil end end) +function progressbar(options, f) end + +--- -- Prompts the user with a drop-down item selection dialog defined by dialog -- options table *options*, returning the selected button's index along with the -- index of the selected item. diff --git a/core/locale.conf b/core/locale.conf index bdeb6a84..ea100822 100644 --- a/core/locale.conf +++ b/core/locale.conf @@ -121,10 +121,6 @@ Select Directory = Select Directory # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Binary file matches. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Continue? -Still searching in files... Continue waiting? = Still searching in files... Continue waiting? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Find in Files aborted # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.ar.conf b/core/locales/locale.ar.conf index e8a1b9fa..32a82f20 100644 --- a/core/locales/locale.ar.conf +++ b/core/locales/locale.ar.conf @@ -121,10 +121,6 @@ Select Directory = ابحث في الملفات # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Binary file matches. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Continue? -Still searching in files... Continue waiting? = Still searching in files... Continue waiting? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Find in Files aborted # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.de.conf b/core/locales/locale.de.conf index 84e79bb0..2207fe6e 100644 --- a/core/locales/locale.de.conf +++ b/core/locales/locale.de.conf @@ -121,10 +121,6 @@ Select Directory = In Dateien suchen # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Übereinstimmungen in Binärdatei. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Fortsetzen? -Still searching in files... Continue waiting? = Suche noch in Dateien ... Weiter warten? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Suche in Dateien abgebrochen # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.es.conf b/core/locales/locale.es.conf index 895a99c1..68de3735 100644 --- a/core/locales/locale.es.conf +++ b/core/locales/locale.es.conf @@ -121,10 +121,6 @@ Select Directory = Buscar en ficheros # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Coincidencia en fichero binario. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Continue? -Still searching in files... Continue waiting? = Still searching in files... Continue waiting? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Find in Files aborted # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.fr.conf b/core/locales/locale.fr.conf index ae8e826b..ec72f531 100644 --- a/core/locales/locale.fr.conf +++ b/core/locales/locale.fr.conf @@ -122,10 +122,6 @@ Select Directory = Rechercher dans les fichiers # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Correspondances dans un fichier binaire -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Continue? -Still searching in files... Continue waiting? = Still searching in files... Continue waiting? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Find in Files aborted # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.it.conf b/core/locales/locale.it.conf index dc14be61..1faf8045 100644 --- a/core/locales/locale.it.conf +++ b/core/locales/locale.it.conf @@ -121,10 +121,6 @@ Select Directory = Trova nei file # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Corrispondenze in un file binario. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Continue? -Still searching in files... Continue waiting? = Still searching in files... Continue waiting? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Find in Files aborted # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.pl.conf b/core/locales/locale.pl.conf index f48bd677..dbae0312 100644 --- a/core/locales/locale.pl.conf +++ b/core/locales/locale.pl.conf @@ -122,10 +122,6 @@ Select Directory = Znajdź w plikach # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Znalezione w pliku binarnym. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Kontynuować? -Still searching in files... Continue waiting? = Wciąż trwa szukanie w plikach... Kontynuować czekanie? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Szukanie w plikach przerwane # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.ru.conf b/core/locales/locale.ru.conf index 20218cee..a1ad36ab 100644 --- a/core/locales/locale.ru.conf +++ b/core/locales/locale.ru.conf @@ -121,10 +121,6 @@ Select Directory = Найти в файлах # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Соответствие в двоичном файле. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Continue? -Still searching in files... Continue waiting? = Still searching in files... Continue waiting? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Find in Files aborted # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.sv.conf b/core/locales/locale.sv.conf index 0d4b5d89..691dce71 100644 --- a/core/locales/locale.sv.conf +++ b/core/locales/locale.sv.conf @@ -121,10 +121,6 @@ Select Directory = Sök i filer # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = Binär fil matchar. -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = Continue? -Still searching in files... Continue waiting? = Still searching in files... Continue waiting? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = Find in Files aborted # The statusbar text shown after performing a "Replace All". diff --git a/core/locales/locale.zh.conf b/core/locales/locale.zh.conf index cd1f7ed0..d38fb2fb 100644 --- a/core/locales/locale.zh.conf +++ b/core/locales/locale.zh.conf @@ -121,10 +121,6 @@ Select Directory = 在文件中查找 # The "Find in Files" result for text found in a binary file. This result is # shown in place of binary buffer text. Binary file matches. = 二进制文件匹配。 -# The text displayed in a dialog when the user is prompted to continue a "Find -# in Files" search that has taken longer than X seconds to complete. -Continue? = 继续吗? -Still searching in files... Continue waiting? = 仍在搜索文件... 继续等待? # The message displayed when a "Find in Files" search is aborted by the user. Find in Files aborted = 在文件中查找已中止 # The statusbar text shown after performing a "Replace All". diff --git a/core/ui.lua b/core/ui.lua index 5adbc758..21217609 100644 --- a/core/ui.lua +++ b/core/ui.lua @@ -99,8 +99,9 @@ ui.dialogs = setmetatable({}, {__index = function(_, k) -- a set of command line arguments and transforming the resulting standard -- output into Lua objects. -- @param options Table of key-value command line options for gtdialog. + -- @param f Work function for progressbar dialogs. -- @return Lua objects depending on the dialog kind - return function(options) + return function(options, f) if not options.button1 then options.button1 = _L['OK'] end if k == 'filteredlist' and not options.width then options.width = ui.size[1] - 2 * (CURSES and 1 or 100) @@ -129,6 +130,9 @@ ui.dialogs = setmetatable({}, {__index = function(_, k) if type(value) ~= 'boolean' then args[#args + 1] = value end end end + if k == 'progressbar' then + args[#args + 1] = assert_type(f, 'function', 2) + end -- Call gtdialog, stripping any trailing newline in the standard output. local result = ui.dialog( k:gsub('_', '-'), table.unpack(args)):match('^(.-)\n?$') @@ -161,7 +165,7 @@ ui.dialogs = setmetatable({}, {__index = function(_, k) local r, g, b = result:match('^#(%x%x)(%x%x)(%x%x)$') local bgr = r and g and b and string.format('0x%s%s%s', b, g, r) or nil return tonumber(bgr) - elseif k == 'fontselect' then + elseif k == 'fontselect' or k == 'progressbar' then return result ~= '' and result or nil elseif not options.string_output then local i, value = result:match('^(%-?%d+)\n?(.*)$') diff --git a/doc/manual.md b/doc/manual.md index 91cb0f8b..96f588ff 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -720,15 +720,10 @@ cycling through results. Textadept does not support replacing in files directly. You must "Find in Files" first, and then "Replace All" for each file containing a result. The "Match Case", "Whole Word", and "Regex" flags still apply. -_Warning_: currently, the [find API][] provides the only means to specify a +_Note_: currently, the [find API][] provides the only means to specify a file-type filter. While the default filter excludes many common binary files and version control folders from searches, Find in Files could still scan -unrecognized binary files or large, unwanted sub-directories. Searches also -block Textadept from receiving additional input, making the interface -temporarily unresponsive. By default, every 10 seconds or so, Textadept will -prompt you to continue a "Find in Files" search, allowing you to cancel one that -is taking too long. You can change this timeout in your -[preferences](#Module.Preferences). +unrecognized binary files or large, unwanted sub-directories. ![Find in Files](images/findinfiles.png) @@ -1023,13 +1018,11 @@ curses) or by reading the [buffer API documentation][]. Many of Textadept's default modules come with configurable settings that can be changed from your *~/.textadept/init.lua* (which is executed after those modules are loaded). Each module's [API documentation][] lists any configurable settings -it has. For example, in order to always hide the tab bar, shorten the "Find in -Files" timeout prompt, disable character autopairing with typeover, strip -trailing whitespace on save, and use C99-style line comments in C code, add the -following to *~/.textadept/init.lua*: +it has. For example, in order to always hide the tab bar, disable character +autopairing with typeover, strip trailing whitespace on save, and use C99-style +line comments in C code, add the following to *~/.textadept/init.lua*: ui.tabs = false - ui.find.find_in_files_timeout = 5 textadept.editing.auto_pairs = nil textadept.editing.typeover_chars = nil textadept.editing.strip_trailing_spaces = true @@ -1778,16 +1771,15 @@ made to CDK are in *src/cdk.patch* and listed as follows: * Excluded the following source files: *alphalist.c*, *button.c*, *calendar.c*, *cdk_compat.{c,h}*, *cdk_params.c*, *cdk_test.h*, *debug.c*, *dialog.c*, - *{d,f}scale.{c,h}*, *fslider.{c,h}*, *gen-{scale,slider}.{c,h}*, - *get_index.c*, *get_string.c*, *graph.c*, *histogram.c*, *marquee.c*, - *matrix.c*, *menu.c*, *popup_dialog.c*, *position.c*, *radio.c*, - *scale.{c,h}*, *slider.{c,h}*, *swindow.c*, *template.c*, - *u{scale,slider}.{c,h}*, *view_{file,info}.c*, and *viewer.c*. + *{d,f}scale.{c,h}*, *fslider.{c,h}*, *gen-scale.{c,h}*, *get_index.c*, + *get_string.c*, *graph.c*, *histogram.c*, *marquee.c*, *matrix.c*, *menu.c*, + *popup_dialog.c*, *position.c*, *radio.c*, *scale.{c,h}*, *swindow.c*, + *template.c*, *u{scale,slider}.{c,h}*, *view_{file,info}.c*, and *viewer.c*. * *binding.c* utilizes libtermkey for universal input. * *cdk.h* does not `#include` "matrix.h", "viewer.h", and any headers labeled - "Generated headers" due to their machine-dependence. It also `#define`s - `boolean` as `CDKboolean` on Windows platforms since the former is already - `typedef`ed. + "Generated headers" due to their machine-dependence, except for "slider.h". It + also `#define`s `boolean` as `CDKboolean` on Windows platforms since the + former is already `typedef`ed. * *cdk_config.h* no longer defines `HAVE_SETLOCALE` since Textadept handles locale settings, no longer defines `HAVE_NCURSES_H` and `NCURSES` since Textadept supports multiple curses implementations (not just ncurses), @@ -2033,6 +2025,8 @@ lexers |Removed |N/A<sup>a</sup> \_cancel_current() |Renamed |[cancel_current()][] \_select() |Renamed |[select()][] \_paths |Renamed |[paths][] +**ui.find** | | +find\_in\_files\_timeout|Removed |N/A <sup>a</sup>Use `for name in buffer:private_lexer_call(_SCINTILLA.functions.property_names[1]):gmatch('[^\n]+') do ... end`. @@ -2185,7 +2179,7 @@ goto\_view(n, relative) |Changed |[goto\_view][](view) **ui.find** | | FILTER |Renamed |[find\_in\_files\_filter][] find\_in\_files(dir) |Changed |[find\_in\_files][](dir, filter) -N/A |Added |[find\_in\_files\_timeout][] +N/A |Added |find\_in\_files\_timeout lua |Changed |[regex][] lua\_pattern\_label\_text |Changed |[regex\_label\_text][] **view** | | @@ -2230,7 +2224,6 @@ MAX\_RECENT\_FILES |Renamed |max\_recent\_files [goto\_view]: api.html#ui.goto_view [find\_in\_files\_filter]: api.html#ui.find.find_in_files_filter [find\_in\_files]: api.html#ui.find.find_in_files -[find\_in\_files\_timeout]: api.html#ui.find.find_in_files_timeout [regex]: api.html#ui.find.regex [regex\_label\_text]: api.html#ui.find.regex_label_text [goto\_buffer]: api.html#view.goto_buffer diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua index bea17dd2..113a32bb 100644 --- a/modules/textadept/find.lua +++ b/modules/textadept/find.lua @@ -52,10 +52,6 @@ local M = ui.find -- @field in_files_label_text (string, Write-only) -- The text of the "In files" label. -- This is primarily used for localization. --- @field find_in_files_timeout (number) --- The approximate interval in seconds between prompts for continuing an --- "In files" search. --- The default value is 10 seconds. -- @field INDIC_FIND (number) -- The find in files highlight indicator number. -- @field _G.events.FIND_WRAPPED (string) @@ -78,7 +74,6 @@ M.whole_word_label_text = not CURSES and _L['Whole word'] or _L['Word(F2)'] M.regex_label_text = not CURSES and _L['Regex'] or _L['Regex(F3)'] M.in_files_label_text = not CURSES and _L['In files'] or _L['Files(F4)'] -M.find_in_files_timeout = 10 M.INDIC_FIND = _SCINTILLA.next_indic_number() -- Events. @@ -267,16 +262,25 @@ function M.find_in_files(dir, filter) _L['[Files Found Buffer]'], string.format('%s %s', _L['Find:']:gsub('_', ''), M.find_entry_text)) buffer.indicator_current = M.INDIC_FIND - local ff_buffer = buffer - local buffer = buffer.new() -- temporary buffer - buffer.code_page = 0 - local text, found, ref_time = M.find_entry_text, false, os.time() - buffer.search_flags = get_flags() + -- Determine which files to search. + local filenames, utf8_filenames = {}, {} lfs.dir_foreach(dir, function(filename) - buffer:clear_all() - buffer:empty_undo_buffer() - local f = io.open(filename, 'rb') + filenames[#filenames + 1] = filename + utf8_filenames[#utf8_filenames + 1] = filename:iconv('UTF-8', _CHARSET) + end, filter or M.find_in_files_filters[dir] or lfs.default_filter) + + -- Perform the search in a temporary buffer and print results. + local orig_buffer, buffer = buffer, buffer.new() + view:goto_buffer(orig_buffer) + buffer.code_page = 0 -- default is UTF-8 + buffer.search_flags = get_flags() + local text, i, found = M.find_entry_text, 1, false + local stopped = ui.dialogs.progressbar({ + title = string.format('%s: %s', _L['Find in Files']:gsub('_', ''), text), + text = utf8_filenames[i], stoppable = true + }, function() + local f = io.open(filenames[i], 'rb') buffer:set_text(f:read('a')) f:close() local binary = nil -- determine lazily for performance reasons @@ -284,40 +288,34 @@ function M.find_in_files(dir, filter) while buffer:search_in_target(text) > -1 do found = true if binary == nil then binary = buffer:text_range(0, 65536):find('\0') end - local utf8_filename = filename:iconv('UTF-8', _CHARSET) - if not binary then - local line_num = buffer:line_from_position(buffer.target_start) - local line = buffer:get_line(line_num) - ff_buffer:append_text( - string.format('%s:%d:%s', utf8_filename, line_num + 1, line)) - local pos = ff_buffer.length - #line + - buffer.target_start - buffer:position_from_line(line_num) - ff_buffer:indicator_fill_range( - pos, buffer.target_end - buffer.target_start) - if not line:find('\n$') then ff_buffer:append_text('\n') end - else - ff_buffer:append_text( - string.format('%s:1:%s\n', utf8_filename, _L['Binary file matches.'])) + if binary then + _G.buffer:append_text(string.format( + '%s:1:%s\n', utf8_filenames[i], _L['Binary file matches.'])) break end + local line_num = buffer:line_from_position(buffer.target_start) + local line = buffer:get_line(line_num) + _G.buffer:append_text( + string.format('%s:%d:%s', utf8_filenames[i], line_num + 1, line)) + local pos = _G.buffer.length - #line + + buffer.target_start - buffer:position_from_line(line_num) + _G.buffer:indicator_fill_range( + pos, buffer.target_end - buffer.target_start) + if not line:find('\n$') then _G.buffer:append_text('\n') end buffer:set_target_range(buffer.target_end, buffer.length) end - if os.difftime(os.time(), ref_time) >= M.find_in_files_timeout then - local button = ui.dialogs.yesno_msgbox{ - title = _L['Continue?'], - text = _L['Still searching in files... Continue waiting?'], - icon = 'gtk-dialog-question', no_cancel = true - } - if button ~= 1 then - ff_buffer:append_text(_L['Find in Files aborted'] .. '\n') - return false - end - ref_time = os.time() - end - end, filter or M.find_in_files_filters[dir] or lfs.default_filter) - if not found then ff_buffer:append_text(_L['No results found']) end - buffer:delete() -- delete temporary buffer - ui._print(_L['[Files Found Buffer]'], '') -- goto end, set save pos, etc. + buffer:clear_all() + buffer:empty_undo_buffer() + _G.buffer:goto_pos(_G.buffer.length) -- [Files Found Buffer] + i = i + 1 + if i > #filenames then return nil end + return i * 100 / #filenames, utf8_filenames[i] + end) + buffer:close(true) -- temporary buffer + ui._print( + _L['[Files Found Buffer]'], + stopped and _L['Find in Files aborted'] .. '\n' or + not found and _L['No results found'] .. '\n' or '') end -- Unescapes \uXXXX sequences in the string *text* and returns the result. diff --git a/src/Makefile b/src/Makefile index 9706b777..aab07f49 100644 --- a/src/Makefile +++ b/src/Makefile @@ -143,7 +143,7 @@ cdk_objs = $(addprefix cdk-, binding.o buttonbox.o cdk.o cdk_display.o \ cdk_objs.o cdkscreen.o draw.o entry.o fselect.o \ itemlist.o label.o mentry.o popup_label.o \ scroll.o scroller.o select_file.o selection.o \ - traverse.o version.o) + slider.o traverse.o version.o) # Add debugging symbols and disable optimizations when DEBUG=1. # Note: In order to profile with gprof (-pg), do not retain symbols in LDFLAGS. @@ -370,7 +370,7 @@ scintilla_zip = 429993cf4429.zip lua_tgz = lua-5.3.5.tar.gz lpeg_tgz = lpeg-1.0.2.tar.gz lfs_zip = v1_7_0_2.zip -gtdialog_zip = db67f8a489e8.zip +gtdialog_zip = 6edd57eeed93.zip cdk_tgz = cdk-5.0-20150928.tgz termkey_tgz = libtermkey-0.20.tar.gz win32gtk_zip = win32gtk-2.24.32.zip @@ -410,6 +410,8 @@ cdk: cdk.patch | $(cdk_tgz) if [ -d $@ ]; then rm -r $@; fi mkdir $@ && tar xzf $| -C $@ && mv $@/*/* $@ mv $@/include/*.h $@ + $@/gen-scale.sh SLIDER Slider Int int $@/gen-slider.h > $@/slider.h + $@/gen-scale.sh SLIDER Slider Int int $@/gen-slider.c > $@/slider.c patch -d $@ -N -p1 < $< $(termkey_tgz): ; $(WGET) http://www.leonerd.org.uk/code/libtermkey/$@ termkey: termkey.patch | $(termkey_tgz) diff --git a/src/textadept.c b/src/textadept.c index c63fd195..14ba536e 100644 --- a/src/textadept.c +++ b/src/textadept.c @@ -462,6 +462,7 @@ static int lfind_focus(lua_State *L) { if (focused_entry->exitType == vNORMAL) { f_clicked(getCDKButtonboxCurrentButton(buttonbox), NULL); refresh_all(); + if (toggled(in_files)) refreshCDKScreen(findbox); // splits cause trouble } find_entry->exitType = replace_entry->exitType = vNEVER_ACTIVATED; activateCDKEntry(focused_entry, NULL); @@ -569,12 +570,33 @@ static int lce_focus(lua_State *L) { return 0; } +/** Runs the work function passed to `ui.dialogs.progressbar()`. */ +static char *progressbar_cb() { + lua_getfield(lua, LUA_REGISTRYINDEX, "ta_progress"); + if (lua_pcall(lua, 0, 2, 0) == LUA_OK) { + if (lua_isnil(lua, -2)) return (lua_pop(lua, 2), NULL); + if (lua_isnil(lua, -1)) lua_pushliteral(lua, ""), lua_replace(lua, -2); + if (lua_isnumber(lua, -2)) { + lua_pushliteral(lua, " "), lua_insert(lua, -2), + lua_pushliteral(lua, "\n"); + lua_concat(lua, 4); // "num str\n" + char *s = strcpy(malloc(lua_rawlen(lua, -1) + 1), lua_tostring(lua, -1)); + return (lua_pop(lua, 1), s); // will be freed by gtdialog + } else lua_pop(lua, 2), lua_pushliteral(lua, "invalid return values"); + } + lL_event(lua, "error", LUA_TSTRING, lua_tostring(lua, -1), -1); + return (lua_pop(lua, 1), NULL); +} + /** `ui.dialog()` Lua function. */ static int lui_dialog(lua_State *L) { + GTDialogType type = gtdialog_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_istable(L, i)) argc += lua_rawlen(L, i) - 1; - const char **argv = malloc((argc + 1) * sizeof(const char *)); + if (type == GTDIALOG_PROGRESSBAR) + lua_pushnil(L), lua_setfield(L, LUA_REGISTRYINDEX, "ta_progress"), argc--; + const char *argv[argc + 1]; // not malloc since luaL_checklstring throws for (i = 0, j = 2; j < n + 2; j++) if (lua_istable(L, j)) { int len = lua_rawlen(L, j); @@ -583,11 +605,14 @@ static int lui_dialog(lua_State *L) { argv[i++] = luaL_checkstring(L, -1); lua_pop(L, 1); } + } else if (lua_isfunction(L, j) && type == GTDIALOG_PROGRESSBAR) { + lua_pushvalue(L, j), lua_setfield(L, LUA_REGISTRYINDEX, "ta_progress"); + gtdialog_set_progressbar_callback(progressbar_cb, NULL); } else argv[i++] = luaL_checkstring(L, j); argv[argc] = NULL; - char *out = gtdialog(gtdialog_type(luaL_checkstring(L, 1)), argc, argv); + char *out = gtdialog(type, argc, argv); lua_pushstring(L, out); - free(out), free(argv); + free(out); #if (CURSES && _WIN32) redrawwin(scintilla_get_window(focused_view)); // needed for pdcurses #endif diff --git a/test/test.lua b/test/test.lua index b884f188..78291350 100644 --- a/test/test.lua +++ b/test/test.lua @@ -908,6 +908,33 @@ function test_ui_dialogs_optionselect_interactive() assert_raises(function() ui.dialogs.optionselect{items = {'foo', 'bar', 'baz'}, select = {1, 'bar'}} end, "bad argument #select[2] to 'optionselect' (number expected, got string") end +function test_ui_dialogs_progressbar_interactive() + local i = 0 + ui.dialogs.progressbar({title = 'foo'}, function() + os.execute('sleep 0.1') + i = i + 10 + if i > 100 then return nil end + return i, i .. '%' + end) + + local stopped = ui.dialogs.progressbar({ + title = 'foo', indeterminite = true, stoppable = true + }, function() + os.execute('sleep 0.1') + return 50 + end) + assert(stopped, 'progressbar not stopped') + + ui.update() -- allow GTK to remove callback for previous function + i = 0 + ui.dialogs.progressbar({title = 'foo', stoppable = true}, function() + os.execute('sleep 0.1') + i = i + 10 + if i > 100 then return nil end + return i, i <= 50 and "stop disable" or "stop enable" + end) +end + function test_ui_dialogs_textbox_interactive() ui.dialogs.textbox{ text = 'foo', editable = true, selected = true, monospaced_font = true |