#include "audio_paths.nvgt" #include "file_viewer.nvgt" funcdef void docs_browser_speak_callback(const string &in message, bool interrupt); funcdef void docs_browser_tick_callback(); docs_browser_speak_callback@ docsBrowserSpeakCallback = null; docs_browser_tick_callback@ docsBrowserTickCallback = null; string docsBrowserDirectory = "files"; string docsBrowserMenuSoundDir = "sounds/menu"; bool docsBrowserWrap = true; string[] docsBrowserExtensions = {"md", "MD", "txt", "TXT"}; sound docsBrowserMoveSound; sound docsBrowserSelectSound; void docs_browser_set_speak_callback(docs_browser_speak_callback@ callback) { @docsBrowserSpeakCallback = @callback; } void docs_browser_set_tick_callback(docs_browser_tick_callback@ callback) { @docsBrowserTickCallback = @callback; } void docs_browser_set_docs_dir(const string docsDir) { docsBrowserDirectory = docsDir; } void docs_browser_set_menu_sound_dir(const string menuSoundDir) { docsBrowserMenuSoundDir = menuSoundDir; } void docs_browser_set_wrap(bool wrap) { docsBrowserWrap = wrap; } void docs_browser_set_extensions(string[]@ extensions) { docsBrowserExtensions.resize(0); if (@extensions is null) return; for (uint extIndex = 0; extIndex < extensions.length(); extIndex++) { docsBrowserExtensions.insert_last(extensions[extIndex]); } } void docs_browser_reset_default_extensions() { string[] defaults = {"md", "MD", "txt", "TXT"}; docs_browser_set_extensions(defaults); } void docs_browser_speak(const string &in message, bool interrupt) { if (@docsBrowserSpeakCallback !is null) { docsBrowserSpeakCallback(message, interrupt); return; } screen_reader_speak(message, interrupt); } void docs_browser_tick() { if (@docsBrowserTickCallback !is null) { docsBrowserTickCallback(); } } bool docs_browser_sort_case_insensitive(const string &in a, const string &in b) { return a.lower() < b.lower(); } void docs_browser_append_unique_case_insensitive(string[]@ items, const string&in value) { string lowerValue = value.lower(); for (uint itemIndex = 0; itemIndex < items.length(); itemIndex++) { if (items[itemIndex].lower() == lowerValue) return; } items.insert_last(value); } string docs_browser_collapse_spaces(const string&in text) { string result = text; while (result.find_first(" ") > -1) { result = result.replace(" ", " ", true); } return result; } string docs_browser_format_label(const string&in filename) { string name = filename; int dotPos = name.find_last_of("."); if (dotPos > 0) { name = name.substr(0, dotPos); } name = name.replace("_", " ", true); name = name.replace("-", " ", true); name = docs_browser_collapse_spaces(name); name = name.lower(); name.trim_whitespace_this(); if (name.length() == 0) return "Document"; string first = name.substr(0, 1).upper(); if (name.length() == 1) return first; return first + name.substr(1); } void docs_browser_collect_entries(string[]@ labels, string[]@ paths) { labels.resize(0); paths.resize(0); if (!directory_exists(docsBrowserDirectory)) return; string[] docFiles; for (uint extIndex = 0; extIndex < docsBrowserExtensions.length(); extIndex++) { string ext = docsBrowserExtensions[extIndex]; if (ext == "") continue; if (ext.substr(0, 1) == ".") { ext = ext.substr(1); } string[]@ found = find_files(docsBrowserDirectory + "/*." + ext); if (@found is null) continue; for (uint fileIndex = 0; fileIndex < found.length(); fileIndex++) { docs_browser_append_unique_case_insensitive(docFiles, found[fileIndex]); } } if (docFiles.length() > 1) { docFiles.sort(docs_browser_sort_case_insensitive); } for (uint fileIndex = 0; fileIndex < docFiles.length(); fileIndex++) { labels.insert_last(docs_browser_format_label(docFiles[fileIndex])); paths.insert_last(docsBrowserDirectory + "/" + docFiles[fileIndex]); } } void docs_browser_add_entries(string[]@ labels, string[]@ paths, int[]@ types, const int typeValue = 1) { string[] docLabels; string[] docPaths; docs_browser_collect_entries(docLabels, docPaths); for (uint entryIndex = 0; entryIndex < docLabels.length(); entryIndex++) { labels.insert_last(docLabels[entryIndex]); paths.insert_last(docPaths[entryIndex]); types.insert_last(typeValue); } } void docs_browser_play_ui_sound(sound &inout soundObj, const string basePath) { string soundPath = resolve_audio_path(basePath); if (soundPath == "") return; soundObj.close(); if (!soundObj.load(soundPath)) return; soundObj.play(); } void docs_browser_run_menu(const string menuTitle = "Documents") { string[] labels; string[] paths; docs_browser_collect_entries(labels, paths); docs_browser_speak(menuTitle + ".", true); if (labels.length() == 0) { docs_browser_speak("No documents found.", true); return; } int selection = 0; docs_browser_speak(labels[selection], true); while (true) { wait(5); docs_browser_tick(); if (key_pressed(KEY_ESCAPE)) { docs_browser_speak("Closed.", true); return; } if (key_pressed(KEY_DOWN)) { docs_browser_play_ui_sound(docsBrowserMoveSound, docsBrowserMenuSoundDir + "/menu_move"); selection++; if (selection >= int(labels.length())) { selection = docsBrowserWrap ? 0 : int(labels.length()) - 1; } docs_browser_speak(labels[selection], true); } if (key_pressed(KEY_UP)) { docs_browser_play_ui_sound(docsBrowserMoveSound, docsBrowserMenuSoundDir + "/menu_move"); selection--; if (selection < 0) { selection = docsBrowserWrap ? int(labels.length()) - 1 : 0; } docs_browser_speak(labels[selection], true); } if (key_pressed(KEY_RETURN)) { docs_browser_play_ui_sound(docsBrowserSelectSound, docsBrowserMenuSoundDir + "/menu_select"); file_viewer_file(paths[selection], labels[selection], true); docs_browser_speak(labels[selection], true); } } }