Index: apps/lang/english.lang
===================================================================
--- apps/lang/english.lang (Revision 23417)
+++ apps/lang/english.lang (Arbeitskopie)
@@ -11016,6 +11016,20 @@
</voice>
</phrase>
<phrase>
+ id: LANG_DICTIONARIES
+ desc: in the main menu
+ user: core
+ <source>
+ *: "Dictionaries"
+ </source>
+ <dest>
+ *: "Dictionaries"
+ </dest>
+ <voice>
+ *: "Dictionaries"
+ </voice>
+</phrase>
+<phrase>
id: VOICE_OF
desc: spoken only, as in 3/8 => 3 of 8
user: core
Index: apps/plugins/dict2.c
===================================================================
--- apps/plugins/dict2.c (Revision 0)
+++ apps/plugins/dict2.c (Revision 0)
@@ -0,0 +1,966 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id: dict.c 14250 2007-08-08 23:32:35Z peter $
+ *
+ * Copyright (C) 2005 Tomas Salfischberger, 2006 Timo Horstschäfer
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "lib/pluginlib_actions.h"
+#include "lib/configfile.h"
+#include "lib/playback_control.h"
+#include "lib/viewer.h"
+
+#include <ctype.h>
+
+PLUGIN_HEADER
+
+
+#define CONFIG_VERSION 3
+#define CACHE_VERSION 2
+
+//! Global dict variables
+static struct dict_s {
+ char path[255]; /**< Path of current dictionary */
+ char name[255]; /**< Basename of current dictionary */
+
+ int DataLen;
+
+ int fCache;
+ int fIndex;
+ int fDict;
+ //! Current description file (there may be more than one)
+ int fDict_n;
+
+ //! Alphabet offset table
+ int32_t charoft[26];
+} Dict;
+
+#define DESC_BUFFER_ADD 512
+
+#define OFT_DIR ROCKBOX_DIR
+
+#define CONFIG_FILENAME "dict2.cfg"
+#define CONFIG_ITEMS ( sizeof(Conf_data) / sizeof (struct configdata) )
+struct conf_s {
+ int max_list; /**< Max number of articles listed */
+ int viewer_scroll; /**< Number of lines to scroll */
+ int viewer_backlight;
+ int viewer_shortcut;
+} Conf;
+
+struct configdata Conf_data[] = {
+ { TYPE_INT, 1, 50, { .int_p = &Conf.max_list }, "max_list", NULL },
+ { TYPE_INT, 0, 4, { .int_p = &Conf.viewer_scroll }, "scroll", NULL },
+ { TYPE_INT, 0, 2, { .int_p = &Conf.viewer_backlight }, "backlight", NULL },
+ { TYPE_INT, 0, 2, { .int_p = &Conf.viewer_shortcut }, "shortcut", NULL },
+};
+
+#define MAX_LIST_DEFAULT 50
+
+//! Malloc variables
+static struct malloc_s {
+ void *buf;
+ size_t bufsize;
+ size_t bufpos;
+} Malloc;
+
+struct cache_h {
+ char magic[9];
+ uint16_t version;
+ int32_t charoft[26];
+};
+
+static struct cache_h Cache_h = {
+ "DICT_OFT",
+ CACHE_VERSION,
+ { 0 },
+};
+
+//! Length of word_str in stardict's DICT format
+#define WORDLEN 256
+
+//! Rockbox filesize limit
+#define MAX_FILESIZE 2147483647
+
+enum dict_action
+{
+ DICT_USB_CONNECTED = -4,
+ DICT_NOT_FOUND = -2,
+ DICT_QUIT = -3,
+ DICT_ERROR = -1,
+ DICT_OK,
+};
+
+#define VIEWER_NEW (VIEWER_CUSTOM)
+
+/** \brief Data structure in .idx files
+ *
+ * Structure of .idx files:
+ * \code
+ * char[] name; // variable length, zero-terminated, UTF-8
+ * uint32_t offset; // beginning of the article in the .dict file, Big Endian
+ * uint32_t size; // article size, Big Endian
+ * \endcode
+ */
+struct WordData_s
+{
+ uint32_t offset;
+ uint32_t size;
+};
+
+struct WordData
+{
+ uint64_t offset;
+ uint32_t size;
+};
+
+//! Internal structure for the result
+struct DictEntry
+{
+ char name[WORDLEN];
+ int32_t index;
+ struct WordData data;
+};
+
+#ifndef betoh64
+
+#ifdef ROCKBOX_LITTLE_ENDIAN
+static inline uint64_t swap64(uint64_t value)
+{
+ uint64_t hi = swap32(value >> 32);
+ uint64_t lo = swap32(value & 0xffffffff);
+ return (lo << 32) | hi;
+}
+#define betoh64(x) swap64(x)
+#else
+#define betoh64(x) (x)
+#endif
+
+#endif
+
+void str_toupper(const char *src, char *dst)
+{
+ while (*src)
+ *dst++ = toupper(*src++);
+ *dst = '\0';
+}
+
+//! Display an error message
+#define dict_error(...) \
+ DEBUGF(__VA_ARGS__); DEBUGF("\n"); \
+ LOGF(__VA_ARGS__); \
+ rb->splashf(HZ, __VA_ARGS__);
+
+
+//! Initialize the plugin buffer
+void dict_buf_init(void)
+{
+ Malloc.buf = rb->plugin_get_buffer((size_t *)&Malloc.bufsize);
+ Malloc.bufpos = 0;
+}
+
+/** \brief Stack-like malloc
+ *
+ * This malloc implementation only keeps track of how much memory is allocated
+ */
+void *dict_malloc(size_t size)
+{
+ void *p;
+ if (Malloc.bufpos+size <= Malloc.bufsize)
+ {
+ p = Malloc.buf + Malloc.bufpos;
+ if ((int)p%4) { DEBUGF("\n%d %% 4 = %d\n", (int)p, (int)p%4); }
+ Malloc.bufpos += size;
+ return (void *)Malloc.buf + Malloc.bufpos - size;
+ }
+ else
+ {
+ dict_error("Out of Memory");
+ return NULL;
+ }
+}
+
+size_t dict_malloc_align(void)
+{
+ size_t align = 4 - (((size_t)Malloc.buf + Malloc.bufpos) % 4);
+ Malloc.bufpos += align;
+ return align;
+}
+
+/** \brief Stack-like free
+ *
+ * @param size Nnumber of bytes to free, 0 will free the whole buffer
+ */
+void dict_free(size_t size)
+{
+ if (size == 0 || size > Malloc.bufpos)
+ Malloc.bufpos = 0;
+ else
+ Malloc.bufpos -= size;
+}
+
+//! Extract the path and name of the given filename
+void dict_get_name(const char *name)
+{
+ char *slash, *dot;
+ size_t n;
+
+ slash = rb->strrchr(name, '/');
+ dot = rb->strrchr(slash+1, '.');
+
+ /* get path */
+ n = slash+1-name;
+ rb->strlcpy(Dict.path, name, n);
+ Dict.path[n] = '\0';
+
+ /* get filename without extension */
+ n = dot-slash;
+ rb->strlcpy(Dict.name, slash+1, n);
+ Dict.name[n] = '\0';
+
+ /* get index type */
+ if (rb->strcmp(dot, ".lidx") == 0)
+ Dict.DataLen = sizeof(struct WordData);
+ else
+ Dict.DataLen = sizeof(struct WordData_s);
+}
+
+//! Generates the cache file to the current dictionary
+enum dict_action dict_create_cache(void)
+{
+ uint32_t offset;
+ int len;
+ char word[WORDLEN];
+ enum dict_action ret = DICT_OK;
+ int32_t count;
+ char a_c, a_cur = 'a'-1;
+
+ int button;
+ const struct button_mapping *plugin_contexts[] = {
+ generic_actions,
+ };
+
+ rb->splash(0, "Creating offset table... may take some minutes");
+
+ rb->memset(Cache_h.charoft, 0, sizeof(Cache_h.charoft));
+ /* write dummy header */
+ rb->write(Dict.fCache, &Cache_h, sizeof(struct cache_h));
+
+ offset = count = 0;
+ while (ret == DICT_OK)
+ {
+ /* get name length */
+ len = 0;
+ while (rb->read(Dict.fIndex, &word[len], 1) == 1)
+ {
+ if (word[len++] == '\0' || len >= WORDLEN)
+ break;
+ }
+ if (!len)
+ break;
+
+ /* character offset table */
+ a_c = tolower(word[0]);
+ if (a_c > a_cur && a_c >= 'a' && a_c <= 'z')
+ {
+ LOGF("'%s' (%ld)", word, count);
+ do
+ Cache_h.charoft[++a_cur-'a'] = count;
+ while (a_cur < a_c);
+ }
+
+ /* write current position to file */
+ rb->write(Dict.fCache, &offset, sizeof(uint32_t));
+
+ rb->lseek(Dict.fIndex, Dict.DataLen, SEEK_CUR);
+ offset += Dict.DataLen + len;
+ count++;
+
+ /* don't take over all control */
+ button = pluginlib_getaction(TIMEOUT_NOBLOCK, plugin_contexts, 1);
+ switch (button)
+ {
+ case PLA_QUIT:
+ ret = DICT_ERROR;
+ break;
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ ret = DICT_USB_CONNECTED;
+ break;
+ }
+ rb->yield();
+ }
+
+ /* Finish table */
+ if (a_cur < 'z')
+ {
+ do
+ Cache_h.charoft[++a_cur-'a'] = count;
+ while (a_cur < 'z');
+ }
+
+ /* write real header */
+ rb->lseek(Dict.fCache, 0, SEEK_SET);
+ rb->write(Dict.fCache, &Cache_h, sizeof(struct cache_h));
+
+ return ret;
+}
+
+//! Closes all file descriptors
+void dict_close(void)
+{
+ rb->close(Dict.fCache);
+ rb->close(Dict.fIndex);
+ rb->close(Dict.fDict);
+
+ Conf.viewer_scroll = viewer_get_scroll();
+ Conf.viewer_backlight = viewer_get_backlight();
+ Conf.viewer_shortcut = viewer_get_shortcut();
+ configfile_save(CONFIG_FILENAME, Conf_data,
+ CONFIG_ITEMS, CONFIG_VERSION);
+}
+
+enum dict_action dict_open(void)
+{
+ char fn[FAT_FILENAME_BYTES];
+
+ /* index file */
+ if (Dict.DataLen == sizeof(struct WordData))
+ rb->snprintf(fn, sizeof(fn), "%s/%s.lidx", Dict.path, Dict.name); // slash added
+ else
+ rb->snprintf(fn, sizeof(fn), "%s/%s.idx", Dict.path, Dict.name); // slash added
+ Dict.fIndex = rb->open(fn, O_RDONLY);
+ if (Dict.fIndex < 0)
+ {
+ dict_error("Failed to open index file: %s", fn);
+ dict_close();
+ return DICT_ERROR;
+ }
+
+ /* cache file */
+ rb->snprintf(fn, sizeof(fn), "%s/%s.oft", OFT_DIR, Dict.name); // slash added
+ Dict.fCache = rb->open(fn, O_RDONLY);
+ if (Dict.fCache >= 0)
+ {
+ /* check the cache file */
+ struct cache_h header;
+
+ rb->read(Dict.fCache, &header, sizeof(struct cache_h));
+ if (rb->strcmp(header.magic, Cache_h.magic) == 0 &&
+ header.version == Cache_h.version)
+ {
+ rb->memcpy(Dict.charoft, header.charoft, sizeof(Dict.charoft));
+ return PLUGIN_OK;
+ }
+
+ dict_error("Cache file outdated");
+
+ rb->close(Dict.fCache);
+ rb->remove(fn);
+ }
+
+ /* incorrect cache file, create a new one */
+ Dict.fCache = rb->creat(fn);
+ if (Dict.fCache >= 0)
+ {
+ if (dict_create_cache() == DICT_OK)
+ {
+ rb->memcpy(Dict.charoft, Cache_h.charoft, sizeof(Dict.charoft));
+
+ /* reopen read-only */
+ rb->close(Dict.fCache);
+ Dict.fCache = rb->open(fn, O_RDONLY);
+ return DICT_OK;
+ }
+ else
+ {
+ rb->close(Dict.fCache);
+ rb->remove(fn);
+ }
+ }
+
+ dict_error("Failed to open cache file: %s", fn);
+ return DICT_ERROR;
+}
+
+/** \brief Initializes everything related to dict.
+ *
+ * @param filename Filename of the .idx file for the dictionary
+ */
+enum dict_action dict_init(const char *file)
+{
+ /* malloc */
+ dict_buf_init();
+
+ /* variables */
+ Conf.viewer_scroll = viewer_get_scroll();
+ Conf.viewer_backlight = viewer_get_backlight();
+ Conf.viewer_shortcut = viewer_get_shortcut();
+ Conf.max_list = MAX_LIST_DEFAULT;
+ Dict.fDict = Dict.fDict_n = -1;
+
+ /* configuration data */
+ configfile_load(CONFIG_FILENAME, Conf_data,
+ CONFIG_ITEMS, CONFIG_VERSION);
+
+ /* dict name */
+ dict_get_name(file);
+
+ /* files */
+ return dict_open();
+}
+
+/** \brief Returns offset for an entry in the idx file
+ *
+ * This function allows random access to the idx file
+ */
+uint32_t dict_cache_offset(int32_t index)
+{
+ uint32_t offset;
+
+ /* read offset */
+ rb->lseek(Dict.fCache, sizeof(uint32_t)*index + sizeof(struct cache_h),
+ SEEK_SET);
+ rb->read(Dict.fCache, &offset, sizeof(uint32_t));
+
+ return offset;
+}
+
+//! Compare function, that represents the order in the .idx file
+int dict_index_strcmp(const char *str1, const char *str2)
+{
+ int a;
+
+ a = rb->strcasecmp(str1, str2);
+ if (!a)
+ {
+ return rb->strcmp(str1, str2);
+ }
+
+ return a;
+}
+
+/** \brief Reads up to n bytes from current fIndex position until a zero occurs
+ *
+ * \return Number of bytes read.
+ */
+int dict_index_gets(char *buf, int n)
+{
+ int i;
+ for (i=0; i<n; i++)
+ {
+ if (!rb->read(Dict.fIndex, buf, 1))
+ break;
+ if (! *buf++)
+ {
+ i++;
+ break;
+ }
+ }
+ return i;
+}
+
+/** \brief Reads up to n bytes from an entry in the idx file until a zero occurs
+ *
+ * @param index The index in idx t read from.
+ * \return Number of bytes read.
+ */
+int dict_index_gets_from(int32_t index, char *buf, int n)
+{
+ rb->lseek(Dict.fIndex, dict_cache_offset(index), SEEK_SET);
+ return dict_index_gets(buf, n);
+}
+
+/** \brief Extracts all data about an entry in idx
+ *
+ * \return length of the article name
+ */
+int dict_index_entry(int32_t index, struct DictEntry *out)
+{
+ int size;
+ char buf[sizeof(struct WordData)];
+
+ out->index = index;
+
+ size = dict_index_gets_from(index, out->name, WORDLEN);
+
+ rb->read(Dict.fIndex, buf, Dict.DataLen);
+
+ /* convert to host endianess */
+ if (Dict.DataLen == sizeof(struct WordData))
+ {
+ out->data.offset = betoh64( ((struct WordData *)buf)->offset );
+ out->data.size = betoh32( ((struct WordData *)buf)->size );
+ }
+ else
+ {
+ out->data.offset = betoh32( ((struct WordData_s *)buf)->offset );
+ out->data.size = betoh32( ((struct WordData_s *)buf)->size );
+ }
+
+ return size;
+}
+
+/** \brief Binary search function
+ *
+ * @return If nothing was found, the last probe is returned negative.
+ */
+int32_t dict_index_binary_search(const char *search,
+ int(*cmpf)(const char *, const char *))
+{
+ char word[WORDLEN];
+ int32_t high, low, probe = 0;
+ int cmp;
+
+ int count = 0;
+
+ int i = tolower(search[0]) - 'a';
+ if (i < 0)
+ {
+ low = -1;
+ high = Dict.charoft[0];
+ }
+ else if (i >= 25)
+ {
+ low = Dict.charoft[25];
+ high = (rb->filesize(Dict.fCache) - sizeof (struct cache_h)) /
+ sizeof(uint32_t);
+ }
+ else
+ {
+ low = Dict.charoft[i] - 1;
+ high = Dict.charoft[i+1] + 1;
+ }
+
+ while (high - low > 1)
+ {
+ count++;
+ probe = (high + low) / 2;
+
+ dict_index_gets_from(probe, word, WORDLEN);
+ LOGF("%ld: '%s'", probe, word);
+
+ /* jump according to the found word. */
+ cmp = cmpf(search, word);
+ if (cmp < 0)
+ high = probe;
+ else if (cmp > 0)
+ low = probe;
+ else
+ {
+ probe = -probe;
+ break;
+ }
+ }
+
+ LOGF("probes: %d", count);
+ return -probe;
+}
+
+/** \brief Shows a selection menu for all articles starting with the given word
+ *
+ * The selections screen is only displayed, if there's more than one result.
+ */
+int32_t dict_index_find_startingWith(const char *search)
+{
+ int32_t index;
+ char buf[WORDLEN];
+ int len, new_len, cmp, needmem;
+ int buffer_size;
+ int n = 0, sel = 0;
+
+ char **items;
+ buffer_size = dict_malloc_align() + Conf.max_list * sizeof(char *);
+ items = (char **) dict_malloc(buffer_size);
+
+ /* make uppercase to find lowest value */
+ str_toupper(search, buf);
+ index = dict_index_binary_search(buf, dict_index_strcmp);
+ if (index < 0)
+ index = -index;
+
+ rb->lseek(Dict.fIndex, dict_cache_offset(index), SEEK_SET);
+
+ /* find all words that start with the word */
+ len = rb->strlen(search);
+ while (n<Conf.max_list)
+ {
+ needmem = new_len = dict_index_gets(buf, WORDLEN);
+ if (!new_len)
+ break;
+ if (needmem%4 > 0)
+ needmem += 4 - needmem%4;
+
+ /* seek to next article name */
+ rb->lseek(Dict.fIndex, Dict.DataLen, SEEK_CUR);
+
+ cmp = rb->strncasecmp(search, buf, len);
+ if (cmp > 0)
+ {
+ index++;
+ continue;
+ }
+ else if (cmp < 0)
+ break;
+
+ items[n] = (char *)dict_malloc(needmem);
+ if (items[n] == NULL)
+ break;
+ buffer_size += needmem;
+
+ rb->strlcpy(items[n], buf, new_len);
+ DEBUGF("%s\n", buf);
+
+ n++;
+ }
+
+ /* show the selection list */
+ if (n > 1)
+ {
+ struct menu_callback_with_desc menu_ = {NULL,"Search Results", Icon_NOICON};
+ struct menu_item_ex menu = {
+ MT_RETURN_ID|MENU_HAS_DESC|MENU_ITEM_COUNT(n),
+ {.strings = (const char **)items},
+ {.callback_and_desc = &menu_}
+ };
+
+ sel = rb->do_menu(&menu, &sel, NULL, true);
+ }
+
+ dict_free(buffer_size);
+
+ if (n == 0)
+ return DICT_NOT_FOUND;
+
+ switch (sel)
+ {
+ case MENU_ATTACHED_USB:
+ return DICT_USB_CONNECTED;
+ case MENU_SELECTED_EXIT: case GO_TO_PREVIOUS: case GO_TO_ROOT:
+ return DICT_QUIT;
+ default:
+ return index + sel;
+ }
+}
+
+//! Wrapper to open the correct .desc file depending on the description offset
+enum dict_action dict_desc_open(int n)
+{
+ char fn[FAT_FILENAME_BYTES];
+
+ if (n == Dict.fDict_n)
+ return DICT_OK;
+
+ if (Dict.fDict > 0)
+ rb->close(Dict.fDict);
+
+ if (n < 1)
+ rb->snprintf(fn, sizeof(fn), "%s/%s.dict", Dict.path, Dict.name); // slash added
+ else
+ rb->snprintf(fn, sizeof(fn), "%s/%s.dict.%d", Dict.path, Dict.name, n); // slash added
+
+ Dict.fDict = rb->open(fn, O_RDONLY);
+ if (Dict.fDict < 0)
+ {
+ dict_error("Failed to open description file: %s", fn);
+ return DICT_ERROR;
+ }
+
+ Dict.fDict_n = n;
+
+ return DICT_OK;
+}
+
+/** \brief Reads the article description
+ *
+ * @param article The article to be read.
+ */
+char *dict_desc_read(struct WordData *artl, char *buf)
+{
+ uint64_t offset = artl->offset;
+ int len, n = offset/MAX_FILESIZE;
+
+ if (dict_desc_open(n) != DICT_OK)
+ return NULL;
+
+ offset %= MAX_FILESIZE;
+
+ rb->lseek(Dict.fDict, offset, SEEK_SET);
+ len = rb->read(Dict.fDict, buf, artl->size);
+
+ /* check if article is splitted between two files */
+ if (offset > MAX_FILESIZE - artl->size)
+ {
+ rb->close(Dict.fDict);
+ if (dict_desc_open(++n) != DICT_OK)
+ return NULL;
+
+ rb->read(Dict.fDict, buf+len, artl->size-len);
+ }
+ buf[artl->size] = '\0';
+
+ /* And print it to debug. */
+ DEBUGF("Description: %s\n", buf);
+
+ return buf;
+}
+
+void dict_menu(void)
+{
+ bool quit = false;
+ int selection = 0;
+
+ MENUITEM_STRINGLIST(menu, "DICT Menu", NULL,
+ "Viewer control",
+ "Playback control",
+ "Max listed articles");
+
+ rb->button_clear_queue();
+ rb->sleep(HZ/5);
+ while (!quit) {
+ selection = rb->do_menu(&menu, &selection, NULL, false);
+ switch (selection)
+ {
+ case 0:
+ quit = viewer_menu();
+ break;
+ case 1:
+ quit = playback_control(NULL);
+ break;
+ case 2:
+ quit = rb->set_int("Max listed articles", "", UNIT_INT,
+ &Conf.max_list, NULL, 10, 10, 50, NULL);
+ break;
+ default:
+ quit = true;
+ break;
+ }
+ }
+}
+
+void dict_viewer_callback(int button)
+{
+ switch (button)
+ {
+ case PLA_UP: case PLA_UP_REPEAT:
+ viewer_up();
+ break;
+ case PLA_DOWN: case PLA_DOWN_REPEAT:
+ viewer_down();
+ break;
+ case PLA_FIRE:
+ viewer_search();
+ break;
+ case PLA_QUIT:
+ viewer_exit(VIEWER_EXIT);
+ break;
+ case PLA_START:
+ viewer_exit(VIEWER_NEW);
+ break;
+ case PLA_MENU:
+ dict_menu();
+ break;
+#ifdef VIEWER_HAS_SHORTCUT
+ default:
+ if (rb->button_status() & BUTTON_VIEWER_SHORTCUT)
+ viewer_shortcut();
+ break;
+#endif
+
+ }
+}
+
+//! Parses #redirect from MediaWiki
+int parse_mw_redirect(struct DictEntry *a, const char *desc)
+{
+ char *p;
+ int len = 0;
+ int32_t index;
+ static const char redirect[] = "#redirect";
+
+ if ( rb->strncasecmp(desc, redirect, sizeof(redirect)-1) )
+ return 0;
+
+ /* get destination article */
+ p = rb->strchr(desc+sizeof(redirect)-1, '[') + 2;
+ while (p[len] != ']' && p[len] != '#')
+ len++;
+
+ rb->strlcpy(a->name, p, len);
+ a->name[len] = '\0';
+
+ /* replace '_' with ' ' */
+ for (p=a->name; *p; p++)
+ {
+ if (*p == '_')
+ *p = ' ';
+ }
+
+
+ index = dict_index_binary_search(a->name, dict_index_strcmp);
+ if (index < 0)
+ {
+ dict_error("Illegal redirect: %s", a->name);
+ return 0;
+ }
+ dict_index_entry(index, a);
+
+ return 1;
+}
+
+//! Dict main loop
+enum dict_action dict_main(void)
+{
+ struct DictEntry result;
+ char search[WORDLEN], old[WORDLEN];
+ char *desc;
+ int n, desc_size = 0, header_len = 0;
+ bool new_search = false;
+
+ search[0] = '\0';
+
+ viewer_init(rb);
+ viewer_set_scroll(Conf.viewer_scroll);
+ viewer_set_backlight(Conf.viewer_backlight);
+ viewer_set_shortcut(Conf.viewer_shortcut);
+ viewer_set_callback(dict_viewer_callback);
+
+ while (true)
+ {
+#ifndef HAVE_FLASH_STORAGE
+ /* keep the disk running */
+ rb->storage_spindown(255);
+#ifndef SIMULATOR
+// rb->ata_wakeup();
+#endif
+#endif /*HAVE_FLASH_STORAGE*/
+
+ rb->strcpy(old, search);
+ rb->kbd_input(search, WORDLEN);
+ /* exit if the search string is empty or the user didn't change it */
+ if (!rb->strlen(search) ||
+ (!new_search && !rb->strncmp(old, search, sizeof(old)) ))
+ {
+ return DICT_OK;
+ }
+ new_search = false;
+
+ result.index = dict_index_find_startingWith(search);
+ switch (result.index)
+ {
+ case DICT_NOT_FOUND:
+ dict_error("No results for \"%s\"", search);
+ continue;
+ case DICT_QUIT:
+ continue;
+ case DICT_USB_CONNECTED:
+ return DICT_USB_CONNECTED;
+ }
+
+ /* get result data */
+ dict_index_entry(result.index, &result);
+ do {
+ if (desc_size > 0)
+ dict_free(desc_size);
+
+ /* make title */
+ header_len = rb->strlen(result.name) + 2;
+ /* increase the buffer to put a title in front of the article */
+ desc_size = result.data.size + header_len + 1;
+ desc = (char *)dict_malloc(desc_size);
+
+ /* set desc pointer to beginning of the description */
+ if (!dict_desc_read(&result.data, desc+header_len))
+ return DICT_ERROR;
+
+ } while (parse_mw_redirect(&result, desc+header_len));
+
+ DEBUGF("\
+result:\n\
+ name: %s\n\
+ index: %ld\n\
+ offset: %llu (%llx)\n\
+ size: %lu\n",
+ result.name, result.index, result.data.offset,
+ result.data.offset, result.data.size);
+
+#ifndef HAVE_FLASH_STORAGE
+ rb->storage_spindown(rb->global_settings->disk_spindown);
+#endif
+
+ rb->snprintf(desc, header_len, "%s\n", result.name);
+ desc[header_len-1] = '\n';
+ desc[desc_size-1] = '\0';
+
+ desc_size += dict_malloc_align();
+ n = viewer_set_text(dict_malloc(0), desc);
+ dict_malloc(n);
+ desc_size += n;
+
+ while (!new_search)
+ {
+ switch(viewer_run())
+ {
+ case VIEWER_NEW:
+ new_search = true;
+ continue;
+ case VIEWER_ATTACHED_USB:
+ return DICT_USB_CONNECTED;
+ default:
+ return DICT_OK;
+ }
+ }
+
+ dict_free(desc_size);
+ }
+}
+
+enum plugin_status plugin_start(const void* file)
+{
+ enum plugin_status ret;
+
+ switch (dict_init(file))
+ {
+ case DICT_ERROR:
+ return PLUGIN_ERROR;
+ case DICT_USB_CONNECTED:
+ return PLUGIN_USB_CONNECTED;
+ default:
+ break;
+ }
+
+ switch (dict_main())
+ {
+ case DICT_OK:
+ ret = PLUGIN_OK;
+ break;
+ case DICT_USB_CONNECTED:
+ ret = PLUGIN_USB_CONNECTED;
+ break;
+ default:
+ ret = PLUGIN_ERROR;
+ break;
+ }
+
+#ifndef HAVE_FLASH_STORAGE
+ /* reset disk setting */
+ rb->storage_spindown(rb->global_settings->disk_spindown);
+#endif
+
+ dict_close();
+
+ return ret;
+}
Index: apps/plugins/lib/viewer.c
===================================================================
--- apps/plugins/lib/viewer.c (Revision 0)
+++ apps/plugins/lib/viewer.c (Revision 0)
@@ -0,0 +1,516 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id: viewer.c 12008 2007-01-14 13:48:09Z dave $
+*
+* Copyright (C) 2007 Timo Horstschäfer
+*
+*
+* All files in this archive are subject to the GNU General Public License.
+* See the file COPYING in the source tree root for full license agreement.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+
+#include "plugin.h"
+#include "pluginlib_actions.h"
+#include "viewer.h"
+
+#define VIEWER_LINE_BUF 255
+#define VIEWER_SCROLLBAR_WIDTH 4
+#define MAX_SEARCHLEN 255
+
+/** text structure */
+struct viewer_txt
+{
+ const char *p; /**< Text pointer */
+ int len; /**< Text size */
+ int pos; /**< Current line number */
+
+ int lines; /**< Number of lines */
+ const char **line; /**< Array of line pointers */
+
+ int sections; /**< Number of sections */
+ int *section; /**< Array of section lines */
+ const char **section_name; /**< Name of each section */
+};
+
+static void viewer_default_callback(int button);
+
+static struct viewer_txt *txt;
+
+/** Display and character dimensions*/
+static int cols, rows;
+#ifdef HAVE_REMOTE_LCD
+static int remote_cols, remote_rows;
+#endif
+#ifdef HAVE_LCD_BITMAP
+static int row_height;
+#endif
+
+static char search[255];
+static bool quit;
+static int retval;
+
+/* Customization (also default settings) */
+void (*viewer_shortcut)(void)
+ = (void *)viewer_menu_search;
+static void (*viewer_callback)(int button)
+ = viewer_default_callback;
+static int scroll = 0;
+static int backlight = 2;
+static int shortcut = 0;
+
+inline int viewer(const struct plugin_api *newrb, const char *text)
+{
+ static long buffer[128];
+ viewer_init(newrb);
+ viewer_set_text(buffer, text);
+ return viewer_run();
+}
+
+/** \brief Returns a pointer to the last character of a line.
+ * @param s Pointer to the actual line.
+ */
+static char *viewer_endl(const char *s, int width)
+{
+ const char *space = NULL;
+
+ while (true)
+ {
+ if (*s == '\n' || *s == '\0')
+ return (char *)s;
+ if (*s == ' ')
+ space = s;
+ if (!--width)
+ break;
+ s += rb->utf8seek(s, 1);
+ }
+
+ return (char *) ( (space) ? space : s );
+}
+
+/** \brief Draws text on the display.
+ *
+ * Handles newline characters and line wrapping.
+ */
+static void viewer_draw(const char *s)
+{
+ const char *p, *eol;
+ int line, len;
+ char buf[VIEWER_LINE_BUF];
+ int left_margin=0;
+
+ rb->lcd_clear_display();
+
+ /* Some Rockbox function restore the backlight settings; reset it */
+ viewer_set_backlight(backlight);
+
+#ifdef HAVE_LCD_BITMAP
+ if (txt->lines > rows)
+ {
+ rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN], 0, 0,
+ VIEWER_SCROLLBAR_WIDTH-1, LCD_HEIGHT,
+ txt->lines+rows-1, txt->pos, txt->pos+rows,
+ VERTICAL);
+ left_margin=VIEWER_SCROLLBAR_WIDTH;
+ }
+#endif
+
+ /* draw main display */
+ for (p=s, line=0; line<rows; line++)
+ {
+ eol = viewer_endl(p, cols);
+
+ len = eol-p;
+ // if (*eol != '\n' && *eol != '\0') //removed to fix missing last character at eol
+ len++;
+
+ rb->strlcpy(buf, p, len);
+ buf[len] = '\0';
+#ifdef HAVE_LCD_BITMAP
+ rb->lcd_putsxy(left_margin, line*row_height, buf);
+#else
+ rb->lcd_puts(0,line,buf);
+#endif
+ if (*eol)
+ p = eol + rb->utf8seek(eol, 1);
+ else
+ break;
+ }
+
+ rb->lcd_update();
+
+#ifdef HAVE_REMOTE_LCD
+ rb->lcd_remote_clear_display();
+
+ /* draw remote */
+ for (p=s, line=0; line<remote_rows; line++)
+ {
+ eol = viewer_endl(p, remote_cols);
+
+ len = eol-p;
+ if (*eol != '\n' && *eol != '\0')
+ len++;
+
+ rb->strlcpy(buf, p, len);
+ buf[len] = '\0';
+
+ rb->lcd_remote_puts(0, line, buf);
+
+ if (*eol)
+ p = eol + rb->utf8seek(eol, 1);
+ else
+ break;
+ }
+
+ rb->lcd_remote_update();
+#endif
+}
+
+static void viewer_default_callback(int button)
+{
+ switch (button)
+ {
+ case PLA_UP: case PLA_UP_REPEAT:
+ viewer_up();
+ break;
+ case PLA_DOWN: case PLA_DOWN_REPEAT:
+ viewer_down();
+ break;
+ case PLA_FIRE:
+ viewer_search();
+ break;
+ case PLA_QUIT:
+ viewer_exit(VIEWER_EXIT);
+ break;
+ case PLA_MENU:
+ viewer_menu();
+ break;
+#ifdef VIEWER_HAS_SHORTCUT
+ default:
+ /* Could need some better way to access the REC-button */
+ if (rb->button_status() & BUTTON_VIEWER_SHORTCUT)
+ viewer_shortcut();
+ break;
+#endif
+ }
+}
+
+void viewer_init(const struct plugin_api *newrb)
+{
+ rb = newrb;
+
+#ifdef HAVE_LCD_BITMAP
+ rb->lcd_getstringsize("o", &cols, &row_height);
+# ifdef HAVE_REMOTE_LCD
+ remote_cols = LCD_REMOTE_WIDTH / cols;
+ remote_rows = LCD_REMOTE_HEIGHT / row_height;
+# endif
+ cols = (LCD_WIDTH-VIEWER_SCROLLBAR_WIDTH) / cols;
+ rows = LCD_HEIGHT / row_height;
+#else
+ cols = 11;
+ rows = 2;
+#endif
+
+ viewer_callback = viewer_default_callback;
+ scroll = 0;
+}
+
+int viewer_run(void)
+{
+ int button;
+ const struct button_mapping *viewer_contexts[] = {
+ generic_directions,
+ generic_actions,
+ };
+
+ viewer_redraw();
+
+ quit = false;
+ retval = VIEWER_EXIT;
+
+ while (!quit)
+ {
+ rb->yield();
+ button = pluginlib_getaction(HZ, viewer_contexts, 2);
+
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ viewer_exit(VIEWER_ATTACHED_USB);
+
+ viewer_callback(button);
+ viewer_redraw();
+ }
+
+ /* restore backlight setting */
+ rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
+
+ return retval;
+}
+
+int viewer_line(const char *p)
+{
+ int line;
+
+ for (line=0; line<txt->lines; line++)
+ {
+ if (p < txt->line[line])
+ break;
+ }
+
+ return line ? line-1 : line;
+}
+
+int viewer_set_text(void *buffer, const char *text)
+{
+ size_t buffer_used;
+ const char *p;
+
+ txt = buffer;
+ buffer_used = sizeof(struct viewer_txt);
+
+ txt->p = text;
+ txt->len = rb->strlen(txt->p);
+ txt->pos = 0;
+
+ /* Create an array with the pointers to the beginning of each line */
+ txt->lines = 0;
+ txt->line = buffer + buffer_used;
+ for (p = txt->p; *p;)
+ {
+ txt->line[txt->lines++] = p;
+ p = viewer_endl(p, cols);
+ if (*p)
+ p += rb->utf8seek(p, 1);
+ }
+ buffer_used += txt->lines * sizeof txt->line;
+ return buffer_used;
+}
+
+void viewer_set_callback(void (*newcallback)(int button))
+{
+ viewer_callback = newcallback;
+}
+
+void viewer_set_scroll(int newscroll)
+{
+ scroll = newscroll < 0 ? 0 : newscroll;
+}
+
+void viewer_set_pos(int line)
+{
+ if (line < 0)
+ txt->pos = txt->lines-1 - (line%txt->lines);
+ else if (line >= txt->lines)
+ txt->pos = txt->lines-1;
+ else
+ txt->pos = line;
+}
+
+void viewer_set_backlight(int newbacklight)
+{
+ backlight = (newbacklight > 2) ? 2 : newbacklight;
+ switch (backlight)
+ {
+ case 0:
+ rb->backlight_set_timeout(0);
+ break;
+ case 1:
+ rb->backlight_set_timeout(1);
+ break;
+ default:
+ rb->backlight_set_timeout(rb->global_settings->backlight_timeout);
+ break;
+ }
+}
+
+void viewer_set_shortcut(int shortcutindex)
+{
+ switch (shortcutindex)
+ {
+ default:
+ case 0:
+ shortcutindex = 0;
+ viewer_shortcut = (void *)viewer_menu_search;
+ break;
+ case 1:
+ viewer_shortcut = (void *)viewer_menu_backlight;
+ break;
+ case 2:
+ viewer_shortcut = (void *)viewer_menu_scroll;
+ break;
+ }
+ shortcut = shortcutindex;
+}
+
+int viewer_get_scroll(void)
+{
+ return scroll;
+}
+
+int viewer_get_pos(void)
+{
+ return txt->pos;
+}
+
+int viewer_get_backlight(void)
+{
+ return backlight;
+}
+
+int viewer_get_shortcut(void)
+{
+ return shortcut;
+}
+
+void viewer_up(void)
+{
+ int n = scroll ? scroll : rows;
+ txt->pos = (txt->pos < n) ? 0 : txt->pos - n;
+}
+
+void viewer_down(void)
+{
+ int n = scroll ? scroll : rows;
+
+#ifdef HAVE_REMOTE_LCD
+ if (txt->lines < remote_rows)
+#else
+ if (txt->lines < rows)
+#endif
+ return;
+
+ if (txt->pos+n >= txt->lines)
+ txt->pos = txt->lines-1;
+ else
+ txt->pos += n;
+}
+
+void viewer_redraw(void)
+{
+ viewer_draw(txt->line[txt->pos]);
+}
+
+bool viewer_search(void)
+{
+ int line;
+ const char *p;
+ size_t n;
+
+ if (!search[0])
+ return false;
+
+ n = rb->strlen(search);
+ line = (txt->pos == txt->lines-1) ? 0 : txt->pos+1;
+ p = txt->line[line];
+ while (true)
+ {
+ if (!*p)
+ p = txt->p;
+
+ /* just a slow linear search */
+ if (rb->strncasecmp(search, p, n) == 0)
+ break;
+
+ if (p == txt->line[txt->pos])
+ return false;
+
+ p += rb->utf8seek(p, 1);
+ }
+
+ viewer_set_pos(viewer_line(p));
+ return true;
+}
+
+void viewer_exit(int newretval)
+{
+ retval = newretval;
+ quit = true;
+}
+
+/* Viewer menu */
+bool viewer_menu_search(void)
+{
+ rb->kbd_input(search, sizeof(search));
+ if (viewer_search())
+ return true;
+ else
+ {
+ rb->splashf(HZ, "\"%s\" not found", search);
+ return false;
+ }
+}
+
+bool viewer_menu_scroll(void)
+{
+ static const struct opt_items scroll_menu[] = {
+ { "Full page", -1 },
+ { "1 line", -1 },
+ { "2 lines", -1 },
+ { "3 lines", -1 },
+ { "4 lines", -1 },
+ { "5 lines", -1 },
+ };
+
+ return rb->set_option("Scrolling", &scroll, INT, scroll_menu, 6, NULL);
+}
+
+bool viewer_menu_backlight(void)
+{
+ static const struct opt_items backlight_menu[] = {
+ { "Off", -1 },
+ { "On", -1 },
+ { "use Rockox setting", -1 },
+ };
+
+ return rb->set_option("Backlight", &backlight, INT, backlight_menu, 3,
+ viewer_set_backlight);
+}
+
+bool viewer_menu_shortcut(void)
+{
+ static const struct opt_items shortcut_menu[] = {
+ { "Search...", -1 },
+ { "Backlight", -1 },
+ { "Scrolling", -1 },
+ };
+
+ return rb->set_option("REC shortcut", &shortcut, INT, shortcut_menu, 3,
+ viewer_set_shortcut);
+}
+
+MENUITEM_FUNCTION(search_item, MENU_FUNC_CHECK_RETVAL, "Search...",
+ viewer_menu_search, NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(scroll_item, 0, "Scrolling",
+ viewer_menu_scroll, NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(backlight_item, 0, "Backlight",
+ viewer_menu_backlight, NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(shortcut_item, 0, "REC shortcut",
+ viewer_menu_shortcut, NULL, NULL, Icon_NOICON);
+
+#ifdef VIEWER_HAS_SHORTCUT
+#define VIEWER_SC_ITEM ,&shortcut_item
+#else
+#define VIEWER_SC_ITEM
+#endif
+MAKE_MENU(viewer_control_menu, "Viewer Control", NULL, Icon_NOICON,
+ &search_item, &scroll_item, &backlight_item VIEWER_SC_ITEM);
+
+bool viewer_menu(void)
+{
+ switch(rb->do_menu(&viewer_control_menu, NULL, NULL, false))
+ {
+ case MENU_ATTACHED_USB:
+ case true:
+ return true;
+ default:
+ return false;
+ }
+}
Index: apps/plugins/lib/SOURCES
===================================================================
--- apps/plugins/lib/SOURCES (Revision 23417)
+++ apps/plugins/lib/SOURCES (Arbeitskopie)
@@ -5,6 +5,7 @@
playback_control.c
rgb_hsv.c
buflib.c
+viewer.c
display_text.c
strncpy.c
#if defined(HAVE_LCD_BITMAP) && (LCD_DEPTH < 4)
Index: apps/plugins/lib/viewer.h
===================================================================
--- apps/plugins/lib/viewer.h (Revision 0)
+++ apps/plugins/lib/viewer.h (Revision 0)
@@ -0,0 +1,84 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id: viewer.h 12008 2007-01-14 13:48:09Z dave $
+*
+* Copyright (C) 2007 Timo Horstschäfer
+*
+*
+* All files in this archive are subject to the GNU General Public License.
+* See the file COPYING in the source tree root for full license agreement.
+*
+* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+* KIND, either express or implied.
+*
+****************************************************************************/
+
+#include "plugin.h"
+
+#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD) || \
+ (CONFIG_KEYPAD == SANSA_E200_PAD)
+#define VIEWER_HAS_SHORTCUT
+#define BUTTON_VIEWER_SHORTCUT BUTTON_REC
+#endif
+
+/** \brief Shows a text viewer.
+ *
+ * How to view some text:
+ * viewer(api, textptr); // Text must be zero-terminated.
+ *
+ * For full control, call each function seperately:
+ * viewer_init(rb); // Must be done once in a plugin.
+ * viewer_set_text(bufferptr, textptr);
+ * viewer_run(); // Display the text.
+ *
+ * viewer() is just a short form for all three calls.
+ *
+ * @param text Zero-terminated string.
+ */
+int viewer(const struct plugin_api *api, const char *text);
+#define VIEWER_ATTACHED_USB (-2)
+#define VIEWER_EXIT 0
+#define VIEWER_CUSTOM (VIEWER_EXIT+1)
+/**< Use values starting with VIEWER_CUSTOM for your own return values. */
+
+void viewer_init(const struct plugin_api *api);
+int viewer_run(void);
+
+/** Get the line to which the address belongs to. */
+int viewer_line(const char *p);
+
+/** Initialize the viewer with a new text.
+ *
+ * @param buffer A small buffer.
+ * @param text The new text to be displayed.
+ * @return Needed size of the buffer.
+ */
+int viewer_set_text(void *buffer, const char *txt);
+void viewer_set_callback(void (*callback)(int button));
+void viewer_set_scroll(int newscroll);
+void viewer_set_pos(int line);
+void viewer_set_backlight(int newbacklight);
+void viewer_set_shortcut(int shortcutindex);
+
+int viewer_get_scroll(void);
+int viewer_get_pos(void);
+int viewer_get_backlight(void);
+int viewer_get_shortcut(void);
+
+/** Functions to control the viewer in the callback function. */
+void viewer_up(void);
+void viewer_down(void);
+bool viewer_search(void);
+bool viewer_menu(void);
+void viewer_redraw(void);
+void (*viewer_shortcut)(void);
+bool viewer_menu_search(void);
+bool viewer_menu_scroll(void);
+bool viewer_menu_backlight(void);
+void viewer_exit(int retval);
Index: apps/plugins/SOURCES
===================================================================
--- apps/plugins/SOURCES (Revision 23417)
+++ apps/plugins/SOURCES (Arbeitskopie)
@@ -3,6 +3,7 @@
credits.c
cube.c
dict.c
+dict2.c
jackpot.c
keybox.c
logo.c
Index: apps/plugins/viewers.config
===================================================================
--- apps/plugins/viewers.config (Revision 23417)
+++ apps/plugins/viewers.config (Arbeitskopie)
@@ -1,6 +1,8 @@
ch8,viewers/chip8,0
txt,viewers/viewer,1
nfo,viewers/viewer,1
+idx,viewers/dict2,1
+lidx,viewers/dict2,1
txt,apps/text_editor,2
jpg,viewers/jpeg,2
jpe,viewers/jpeg,2
Index: apps/settings.h
===================================================================
--- apps/settings.h (Revision 23417)
+++ apps/settings.h (Arbeitskopie)
@@ -78,6 +78,7 @@
#define RECPRESETS_DIR ROCKBOX_DIR "/recpresets"
#define FMPRESET_PATH ROCKBOX_DIR "/fmpresets"
#define PLAYLIST_CATALOG_DEFAULT_DIR "/Playlists"
+#define DICTS_DIR "/dicts"
#define VIEWERS_CONFIG ROCKBOX_DIR "/viewers.config"
#define CONFIGFILE ROCKBOX_DIR "/config.cfg"
@@ -144,7 +145,7 @@
enum { SHOW_ALL, SHOW_SUPPORTED, SHOW_MUSIC, SHOW_PLAYLIST, SHOW_ID3DB,
NUM_FILTER_MODES,
SHOW_WPS, SHOW_RWPS, SHOW_SBS, SHOW_RSBS, SHOW_FMR, SHOW_CFG,
- SHOW_LNG, SHOW_MOD, SHOW_FONT, SHOW_PLUGINS};
+ SHOW_LNG, SHOW_MOD, SHOW_FONT, SHOW_DICTS, SHOW_PLUGINS};
/* file and dir sort options */
enum { SORT_ALPHA, SORT_DATE, SORT_DATE_REVERSED, SORT_TYPE, /* available as settings */
Index: apps/filetree.c
===================================================================
--- apps/filetree.c (Revision 23417)
+++ apps/filetree.c (Arbeitskopie)
@@ -328,7 +328,8 @@
((*c->dirfilter == SHOW_MUSIC &&
(dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_AUDIO) &&
(dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_M3U) ||
- (*c->dirfilter == SHOW_SUPPORTED && !filetype_supported(dptr->attr)))) ||
+ ((*c->dirfilter == SHOW_SUPPORTED || *c->dirfilter == SHOW_DICTS)&&
+ !filetype_supported(dptr->attr)))) ||
(*c->dirfilter == SHOW_WPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_WPS) ||
#ifdef HAVE_LCD_BITMAP
(*c->dirfilter == SHOW_FONT && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_FONT) ||
@@ -671,6 +672,7 @@
if (*c->dirfilter > NUM_FILTER_MODES &&
*c->dirfilter != SHOW_CFG &&
*c->dirfilter != SHOW_FONT &&
+ *c->dirfilter != SHOW_DICTS &&
*c->dirfilter != SHOW_PLUGINS)
{
exit_func = true;
Index: apps/root_menu.c
===================================================================
--- apps/root_menu.c (Revision 23417)
+++ apps/root_menu.c (Arbeitskopie)
@@ -216,6 +216,10 @@
tc->selected_item = last_db_selection;
break;
#endif
+ case GO_TO_BROWSEDICTS:
+ filter = SHOW_DICTS;
+ snprintf(folder, MAX_PATH, "%s/", DICTS_DIR);
+ break;
case GO_TO_BROWSEPLUGINS:
filter = SHOW_PLUGINS;
strlcpy(folder, PLUGIN_DIR, MAX_PATH);
@@ -355,6 +359,7 @@
#endif
[GO_TO_RECENTBMARKS] = { load_bmarks, NULL, &bookmark_settings_menu },
+ [GO_TO_BROWSEDICTS] = { browser, (void*)GO_TO_BROWSEDICTS, NULL },
[GO_TO_BROWSEPLUGINS] = { plugins_menu, NULL, NULL },
};
@@ -368,6 +373,8 @@
MENUITEM_RETURNVALUE(db_browser, ID2P(LANG_TAGCACHE), GO_TO_DBBROWSER,
NULL, Icon_Audio);
#endif
+MENUITEM_RETURNVALUE(dicts_browser, ID2P(LANG_DICTIONARIES), GO_TO_BROWSEDICTS,
+ NULL, Icon_NOICON);
MENUITEM_RETURNVALUE(rocks_browser, ID2P(LANG_PLUGINS), GO_TO_BROWSEPLUGINS,
NULL, Icon_Plugin);
static char *get_wps_item_name(int selected_item, void * data, char *buffer)
@@ -419,7 +426,7 @@
#if CONFIG_TUNER
&fm,
#endif
- &playlist_options, &rocks_browser, &info_menu
+ &playlist_options, &dicts_browser, &rocks_browser, &info_menu
#ifdef HAVE_LCD_CHARCELLS
,&do_shutdown_item
Index: apps/root_menu.h
===================================================================
--- apps/root_menu.h (Revision 23417)
+++ apps/root_menu.h (Arbeitskopie)
@@ -50,6 +50,7 @@
/* Do Not add any items above here unless you want it to be able to
be the "start screen" after a boot up. The setting in settings_list.c
will need editing if this is the case. */
+ GO_TO_BROWSEDICTS,
GO_TO_BROWSEPLUGINS,
GO_TO_TIMESCREEN,
};