diff --git a/pd/nw/dialog_prefs.html b/pd/nw/dialog_prefs.html index e5b7d77533c670e2ae667a99b1aa02a73a7e29ad..8148d0d5c749a533cc7d968e4e14f0e75c7ff62b 100644 --- a/pd/nw/dialog_prefs.html +++ b/pd/nw/dialog_prefs.html @@ -302,6 +302,23 @@ <input type="checkbox" id="save_zoom" name="save_zoom"> <span data-i18n="prefs.gui.zoom.save_zoom"></span> </label> + <br/><br/> + <span data-i18n="prefs.gui.browser.browser_title"></span> + <br/> + <label data-i18n="[title]prefs.gui.browser.browser_doc_tt"> + <input type="checkbox" id="browser_doc" name="browser_doc"> + <span data-i18n="prefs.gui.browser.browser_doc"></span> + </label> + <br/> + <label data-i18n="[title]prefs.gui.browser.browser_path_tt"> + <input type="checkbox" id="browser_path" name="browser_path"> + <span data-i18n="prefs.gui.browser.browser_path"></span> + </label> + <br/> + <label data-i18n="[title]prefs.gui.browser.browser_init_tt"> + <input type="checkbox" id="browser_init" name="browser_init"> + <span data-i18n="prefs.gui.browser.browser_init"></span> + </label> </div> </div> @@ -396,8 +413,8 @@ function get_gui_preset() { return document.getElementById("gui_preset").selectedOptions[0].value; } -function get_save_zoom() { - return +document.getElementById("save_zoom").checked; +function get_bool_elem(elem) { + return +document.getElementById(elem).checked; } // startup config data @@ -606,9 +623,10 @@ function apply(save_prefs) { midi_use_alsa ? get_attr("midi-outdev-names", attrs).length : 0 ); - // Send the gui prefs (currently just the name of the gui preset and the - // status of the save-zoom toggle) to Pd - pdgui.pdsend("pd gui-prefs", get_gui_preset(), get_save_zoom()); + // Send the gui prefs (currently just the name of the gui preset, the + // status of the save-zoom toggle and various options related to the help + // browser) to Pd + pdgui.pdsend("pd gui-prefs", get_gui_preset(), get_bool_elem("save_zoom"), get_bool_elem("browser_doc"), get_bool_elem("browser_path"), get_bool_elem("browser_init")); // Send the startup config data to Pd pdgui.pdsend.apply(null, ["pd path-dialog", startup_use_stdpath, startup_verbose].concat(get_path_array())); @@ -847,7 +865,7 @@ function midi_prefs_callback(attrs) { pdgui.resize_window(pd_object_callback); } -function gui_prefs_callback(name, save_zoom) { +function gui_prefs_callback(name, save_zoom, browser_doc, browser_path, browser_init) { var s = document.getElementById("gui_preset"), i; for (i = 0; i < s.options.length; i++) { @@ -857,6 +875,9 @@ function gui_prefs_callback(name, save_zoom) { } } document.getElementById("save_zoom").checked = !!save_zoom; + document.getElementById("browser_doc").checked = !!browser_doc; + document.getElementById("browser_path").checked = !!browser_path; + document.getElementById("browser_init").checked = !!browser_init; } // startup settings diff --git a/pd/nw/dialog_search.html b/pd/nw/dialog_search.html index 53d8e74abcf449940fe6dd2ab02914886d2a8771..19343356d190fb2ea7a896382bbbd5a90b5177be 100644 --- a/pd/nw/dialog_search.html +++ b/pd/nw/dialog_search.html @@ -4,27 +4,18 @@ <link id="page_style" rel="stylesheet" type="text/css" href="css/default.css"> <title>Pd Search Engine</title> - <script type="text/javascript" src="./elasticlunr.js"></script> <script type="text/javascript" src="./console_search.js"></script> <script> "use strict"; var pdgui = require("./pdgui.js"); var fs = require("fs"); var path = require("path"); -var dive = require("./dive.js"); // small module to recursively search dirs var l = pdgui.get_local_string; -var index = elasticlunr(); +var index; pdgui.skin.apply(window); -index.addField("title"); -index.addField("keywords"); -index.addField("description"); -//index.addField("body"); -index.addField("path"); -index.setRef("id"); - var filetypes = [".pd", ".txt", ".htm", ".html", ".pdf"]; // Table of Contents to start with @@ -159,54 +150,6 @@ function translate_form() { } } -function add_doc_to_index(filename, data) { - var title = path.basename(filename, ".pd"), - big_line = data.replace("\n", " "), - keywords, - desc; - // We use [\s\S] to match across multiple lines... - keywords = big_line - .match(/#X text \-?[0-9]+ \-?[0-9]+ KEYWORDS ([\s\S]*?);/i), - desc = big_line - .match(/#X text \-?[0-9]+ \-?[0-9]+ DESCRIPTION ([\s\S]*?);/i); - keywords = keywords && keywords.length > 1 ? keywords[1].trim() : null; - desc = desc && desc.length > 1 ? desc[1].trim() : null; - // Remove the Pd escapes for commas - desc = desc ? desc.replace(" \\,", ",") : null; - if (desc) { - // format Pd's "comma atoms" as normal commas - desc = desc.replace(" \\,", ","); - } - if (title.slice(-5) === "-help") { - title = title.slice(0, -5); - } - index.addDoc({ - "id": filename, - "title": title, - "keywords": keywords, - "description": desc - //"body": big_line, - }); -} - -function read_file(err, filename, stat) { - if (!err) { - if (filename.slice(-3) === ".pd") { - fs.readFile(filename, { encoding: "utf8", flag: "r" }, - function(read_err, data) { - if (!read_err) { - add_doc_to_index(filename, data); - } else { - pdgui.post("err: " + read_err); - } - } - ); - } - } else { - pdgui.post("err: " + err); - } -} - function click_toc(dir) { document.getElementById("search_text").value = dir; doc_search(); @@ -251,18 +194,13 @@ function display_toc() { }); } -function finish_build() { +function finish_build(idx) { + index = idx; document.getElementById("search_text").disabled = false; clear_results(); display_toc(); } -function build_index() { - var doc_path = path.join(pdgui.get_lib_dir(), "doc"); - pdgui.post("doc path is " + doc_path); - dive(doc_path, read_file, finish_build); -} - function clear_results() { document.getElementById("results").innerHTML = ""; } @@ -528,7 +466,7 @@ function register_window_id(id, attrs) { document.getElementById("search_text").disabled = true; document.getElementById("file_browser").setAttribute("nwworkingdir", doc_path); // Probably need a doc getter in pdgui - build_index(); + pdgui.build_index(finish_build); } function display_no_results() { diff --git a/pd/nw/locales/de/translation.json b/pd/nw/locales/de/translation.json index 6b0ea4e01febdba7a7e0ea4addc83c8668359a81..f382fa17c9889c466db4412024d44700bf72664e 100644 --- a/pd/nw/locales/de/translation.json +++ b/pd/nw/locales/de/translation.json @@ -407,6 +407,15 @@ "zoom": { "save_zoom": "Speichern/Laden der Vergrößerung im Patch", "save_zoom_tt": "Speichere die aktuelle Vergrößerung mit dem Patch und stelle diese beim Laden des Patches wieder her" + }, + "browser": { + "browser_title": "Hilfe-Browser-Einstellungen (WARNUNG: Änderungen können Startup-Zeiten beeinflussen!)", + "browser_doc": "Hilfe-Browser durchsucht nur den doc-Ordner", + "browser_doc_tt": "Der Hilfe-Browser durchkämmt nur Hilfe-Patches im doc-Ordner nach Suchbegriffen (schneller)", + "browser_path": "Hilfe-Browser durchsucht auch den Hilfe-Pfad", + "browser_path_tt": "Der Hilfe-Browser durchkämmt auch Hilfe-Patches im benutzerdefinierten Hilfe-Pfad nach Suchbegriffen (langsamer)", + "browser_init": "Erstelle den Hilfe-Index beim Programmstart", + "browser_init_tt": "Falls aktiviert, erstelle den Index für den Hilfe-Browser bereits beim Programmstart" } }, "audio": { diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json index 2032010d8526513fd8ca91f3dd4843836ed8a4cd..c779742a70f17f466865b89090416169b593fd64 100644 --- a/pd/nw/locales/en/translation.json +++ b/pd/nw/locales/en/translation.json @@ -408,6 +408,15 @@ "zoom": { "save_zoom": "save/load zoom level with patch", "save_zoom_tt": "Save the current zoom level with the patch and restore it when reloading the patch" + }, + "browser": { + "browser_title": "Help browser settings (WARNING: changing these may affect startup times!)", + "browser_doc": "help browser only searches the doc folder", + "browser_doc_tt": "Only scan help patches in the doc folder for searchable keywords (faster)", + "browser_path": "help browser also searches the help path", + "browser_path_tt": "Also scan help patches in the user-defined help path for searchable keywords (slower)", + "browser_init": "prepare the help index at application start", + "browser_init_tt": "If checked, prepare the index for the help browser already when the application starts" } }, "audio": { diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js index 08a1247482e13402eb9dae9da82be652d3526675..7f3bffa474585e25a182bf0d711651e32d1a29f3 100644 --- a/pd/nw/pdgui.js +++ b/pd/nw/pdgui.js @@ -2,6 +2,7 @@ var pwd; var lib_dir; +var help_path, browser_doc, browser_path, browser_init; var pd_engine_id; exports.set_pwd = function(pwd_string) { @@ -26,6 +27,19 @@ exports.set_pd_engine_id = function (id) { exports.defunkify_windows_path = defunkify_windows_path; +function gui_set_browser_config(doc_flag, path_flag, init_flag, helppath) { + // post("gui_set_browser_config: " + helppath.join(":")); + browser_doc = doc_flag; + browser_path = path_flag; + browser_init = init_flag; + help_path = helppath; + // AG: Start building the keyword index for dialog_search.html. We do this + // here so that we can be sure that lib_dir and help_path are known already. + // (This may also be deferred until the browser is launched for the first + // time, depending on the value of browser_init.) + if (browser_init == 1) make_index(); +} + function gui_set_lib_dir(dir) { lib_dir = dir; } @@ -60,10 +74,140 @@ exports.set_focused_patchwin = function(cid) { last_focused = cid; } +// Keyword index (cf. dialog_search.html) + +var fs = require("fs"); +var path = require("path"); +var dive = require("./dive.js"); // small module to recursively search dirs +var elasticlunr = require("./elasticlunr.js"); // lightweight full-text search engine in JavaScript, cf. https://github.com/weixsong/elasticlunr.js/ + +var index = elasticlunr(); + +index.addField("title"); +index.addField("keywords"); +index.addField("description"); +//index.addField("body"); +index.addField("path"); +index.setRef("id"); + +function add_doc_to_index(filename, data) { + var title = path.basename(filename, ".pd"), + big_line = data.replace("\n", " "), + keywords, + desc; + // We use [\s\S] to match across multiple lines... + keywords = big_line + .match(/#X text \-?[0-9]+ \-?[0-9]+ KEYWORDS ([\s\S]*?);/i), + desc = big_line + .match(/#X text \-?[0-9]+ \-?[0-9]+ DESCRIPTION ([\s\S]*?);/i); + keywords = keywords && keywords.length > 1 ? keywords[1].trim() : null; + desc = desc && desc.length > 1 ? desc[1].trim() : null; + // Remove the Pd escapes for commas + desc = desc ? desc.replace(" \\,", ",") : null; + if (desc) { + // format Pd's "comma atoms" as normal commas + desc = desc.replace(" \\,", ","); + } + if (title.slice(-5) === "-help") { + title = title.slice(0, -5); + } + index.addDoc({ + "id": filename, + "title": title, + "keywords": keywords, + "description": desc + //"body": big_line, + }); +} + +function read_file(err, filename, stat) { + if (!err) { + if (filename.slice(-3) === ".pd") { + // AG: We MUST read the files synchronously here. This might be a + // performance issue on some systems, but if we don't do this then + // we may open a huge number of files simultaneously, causing the + // process to run out of file handles. + try { + var data = fs.readFileSync(filename, { encoding: "utf8", flag: "r" }); + add_doc_to_index(filename, data); + } catch (read_err) { + post("err: " + read_err); + } + } + } else { + // AG: Simply ignore missing/unreadable files and directories. + // post("err: " + err); + } +} + +var index_done = false; +var index_started = false; + +function finish_index() { + index_done = true; + post("finished building help index"); +} + +// AG: pilfered from https://stackoverflow.com/questions/21077670 +function expand_tilde(filepath) { + if (filepath[0] === '~') { + return path.join(process.env.HOME, filepath.slice(1)); + } + return filepath; +} + +// AG: This is supposed to be executed only once, after lib_dir has been set. +// Note that dive() traverses lib_dir asynchronously, so we report back in +// finish_index() when this is done. +function make_index() { + var doc_path = browser_doc?path.join(lib_dir, "doc"):lib_dir; + var i = 0; + var l = help_path.length; + function make_index_cont() { + if (i < l) { + var doc_path = help_path[i++]; + // AG: These paths might not exist, ignore them in this case. Also + // note that we need to expand ~ here. + var full_path = expand_tilde(doc_path); + fs.lstat(full_path, function(err, stat) { + if (!err) { + post("building help index in " + doc_path); + dive(full_path, read_file, make_index_cont); + } else { + make_index_cont(); + } + }); + } else { + finish_index(); + } + } + index_started = true; + post("building help index in " + doc_path); + dive(doc_path, read_file, browser_path?make_index_cont:finish_index); +} + +// AG: This is called from dialog_search.html with a callback that expects to +// receive the finished index as its sole argument. We also build the index +// here if needed, using make_index, then simply wait until make_index +// finishes and finally invoke the callback on the resulting index. +function build_index(cb) { + function build_index_worker() { + if (index_done == true) { + cb(index); + } else { + setTimeout(build_index_worker, 500); + } + } + if (index_started == false) { + make_index(); + } + build_index_worker(); +} + +exports.build_index = build_index; + // Modules -var fs = require("fs"); // for fs.existsSync -var path = require("path"); // for path.dirname path.extname path.join var cp = require("child_process"); // for starting core Pd from GUI in OSX var parse_svg_path = require("./parse-svg-path.js"); @@ -4964,9 +5108,9 @@ function gui_midi_properties(gfxstub, sys_indevs, sys_outdevs, } } -function gui_gui_properties(dummy, name, save_zoom) { +function gui_gui_properties(dummy, name, save_zoom, browser_doc, browser_path, browser_init) { if (dialogwin["prefs"] !== null) { - dialogwin["prefs"].window.gui_prefs_callback(name, save_zoom); + dialogwin["prefs"].window.gui_prefs_callback(name, save_zoom, browser_doc, browser_path, browser_init); } } diff --git a/pd/src/m_glob.c b/pd/src/m_glob.c index 87f00fb38df85531c42ee47c8f0c2a6a76f19738..42e135d0de56f110f0356e33382bfd4b5916d787 100644 --- a/pd/src/m_glob.c +++ b/pd/src/m_glob.c @@ -75,18 +75,21 @@ static void glob_perf(t_pd *dummy, float f) sys_perf = (f != 0); } -extern int sys_zoom; +extern int sys_zoom, sys_browser_doc, sys_browser_path, sys_browser_init; extern t_symbol *sys_gui_preset; -static void glob_gui_prefs(t_pd *dummy, t_symbol *s, float f) +static void glob_gui_prefs(t_pd *dummy, t_symbol *s, float f, float f2, float f3, float f4) { sys_gui_preset = s; sys_zoom = !!(int)f; + sys_browser_doc = !!(int)f2; + sys_browser_path = !!(int)f3; + sys_browser_init = !!(int)f4; } -/* just the gui-preset and the save-zoom toggle for now */ +/* just the gui-preset, the save-zoom toggle and various help browser options for now */ static void glob_gui_properties(t_pd *dummy) { - gui_vmess("gui_gui_properties", "xsi", 0, sys_gui_preset->s_name, sys_zoom); + gui_vmess("gui_gui_properties", "xsiiii", 0, sys_gui_preset->s_name, sys_zoom, sys_browser_doc, sys_browser_path, sys_browser_init); } // ths one lives inside g_editor so that it can access the clipboard @@ -172,7 +175,7 @@ void glob_init(void) class_addmethod(glob_pdobject, (t_method)glob_clipboard_text, gensym("clipboardtext"), A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_gui_prefs, - gensym("gui-prefs"), A_SYMBOL, A_FLOAT, 0); + gensym("gui-prefs"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); class_addmethod(glob_pdobject, (t_method)glob_gui_properties, gensym("gui-properties"), 0); class_addmethod(glob_pdobject, (t_method)glob_recent_files, diff --git a/pd/src/s_file.c b/pd/src/s_file.c index 22d660f29af432a69c10196dfd8d1f5da9c95fcc..30a39d61aa69f99c4db46f78e38ed3450107e309 100644 --- a/pd/src/s_file.c +++ b/pd/src/s_file.c @@ -40,7 +40,7 @@ #define snprintf sprintf_s #endif -int sys_defeatrt, sys_zoom; +int sys_defeatrt, sys_zoom, sys_browser_doc = 1, sys_browser_path, sys_browser_init; t_symbol *sys_flags = &s_; void sys_doflags( void); @@ -669,6 +669,12 @@ void sys_loadpreferences( void) sscanf(prefbuf, "%d", &sys_defeatrt); if (sys_getpreference("savezoom", prefbuf, MAXPDSTRING)) sscanf(prefbuf, "%d", &sys_zoom); + if (sys_getpreference("browser_doc", prefbuf, MAXPDSTRING)) + sscanf(prefbuf, "%d", &sys_browser_doc); + if (sys_getpreference("browser_path", prefbuf, MAXPDSTRING)) + sscanf(prefbuf, "%d", &sys_browser_path); + if (sys_getpreference("browser_init", prefbuf, MAXPDSTRING)) + sscanf(prefbuf, "%d", &sys_browser_init); if (sys_getpreference("guipreset", prefbuf, MAXPDSTRING)) { char preset_buf[MAXPDSTRING]; @@ -808,6 +814,12 @@ void glob_savepreferences(t_pd *dummy) sys_putpreference("defeatrt", buf1); sprintf(buf1, "%d", sys_zoom); sys_putpreference("savezoom", buf1); + sprintf(buf1, "%d", sys_browser_doc); + sys_putpreference("browser_doc", buf1); + sprintf(buf1, "%d", sys_browser_path); + sys_putpreference("browser_path", buf1); + sprintf(buf1, "%d", sys_browser_init); + sys_putpreference("browser_init", buf1); sys_putpreference("guipreset", sys_gui_preset->s_name); sys_putpreference("flags", (sys_flags ? sys_flags->s_name : "")); diff --git a/pd/src/s_main.c b/pd/src/s_main.c index 2f2f0d821bfdfe0020b5537c4cec0d3d74c1aeb5..42a2e59f12136c820c5b2391aff20594fd7ad59d 100644 --- a/pd/src/s_main.c +++ b/pd/src/s_main.c @@ -285,11 +285,13 @@ void glob_forward_files_from_secondary_instance(void) } extern void glob_recent_files(t_pd *dummy); +extern int sys_browser_doc, sys_browser_path, sys_browser_init; /* this is called from main() in s_entry.c */ int sys_main(int argc, char **argv) { int i, noprefs; + t_namelist *nl; sys_externalschedlib = 0; sys_extraflags = 0; sys_gui_preset = gensym("default"); @@ -325,6 +327,17 @@ int sys_main(int argc, char **argv) gui_vmess("gui_set_gui_preset", "s", sys_gui_preset->s_name); /* send the recent files list */ glob_recent_files(0); + /* AG: send the browser config; this must come *after* gui_set_lib_dir + so that the lib_dir is available when help indexing starts */ + gui_start_vmess("gui_set_browser_config", "iii", + sys_browser_doc, sys_browser_path, sys_browser_init); + gui_start_array(); + for (nl = sys_helppath; nl; nl = nl->nl_next) + { + gui_s(nl->nl_string); + } + gui_end_array(); + gui_end_vmess(); if (sys_externalschedlib) return (sys_run_scheduler(sys_externalschedlibname,