Index: apps/lang/english.lang =================================================================== --- apps/lang/english.lang (revision 13609) +++ apps/lang/english.lang (working copy) @@ -10979,6 +10979,20 @@ + id: LANG_DICTIONARIES + desc: in the main menu + user: + + *: "Dictionaries" + + + *: "Dictionaries" + + + *: "Dictionaries" + + + id: LANG_VOICE_DIR_TALK desc: Item of voice menu, whether to use directory .talk clips user: @@ -11006,3 +11020,4 @@ *: "Use File .talk Clips" +>>>>>>> .r13592 Index: apps/plugins/viewers.config =================================================================== --- apps/plugins/viewers.config (revision 13609) +++ apps/plugins/viewers.config (working copy) @@ -1,6 +1,8 @@ ch8,viewers/chip8,0 txt,viewers/viewer,1 nfo,viewers/viewer,1 +idx,viewers/dict,1 +lidx,viewers/dict,1 txt,rocks/text_editor,2 jpg,viewers/jpeg,2 jpe,viewers/jpeg,2 Index: apps/plugins/dict.c =================================================================== --- apps/plugins/dict.c (revision 13609) +++ apps/plugins/dict.c (working copy) @@ -7,7 +7,7 @@ * \/ \/ \/ \/ \/ * $Id$ * - * Copyright (C) 2005 Tomas Salfischberger + * 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. @@ -18,299 +18,949 @@ ****************************************************************************/ #include "plugin.h" +#include "pluginlib_actions.h" +#include "configfile.h" +#include "playback_control.h" +#include "viewer.h" +#include + PLUGIN_HEADER -/* save the plugin api pointer. */ static struct plugin_api* rb; -/* screen info */ -static int display_columns, display_lines; -/* Some lenghts */ -#define WORDLEN 32 /* has to be the same in rdf2binary.c */ +#define CONFIG_VERSION 3 +#define CACHE_VERSION 2 -/* Struct packing */ -#ifdef __GNUC__ -#define STRUCT_PACKED __attribute__((packed)) -#else -#define STRUCT_PACKED -#pragma pack (push, 2) -#endif +//! Global dict variables +static struct dict_s { + char path[255]; /**< Path of current dictionary */ + char name[255]; /**< Basename of current dictionary */ -/* The word struct :) */ -struct stWord + 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 CONFIG_FILENAME "dict.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, 1000, &Conf.max_list, "max_list", NULL, NULL }, + { TYPE_INT, 0, 4, &Conf.viewer_scroll, "scroll", NULL, NULL }, + { TYPE_INT, 0, 2, &Conf.viewer_backlight, "backlight", NULL, NULL }, + { TYPE_INT, 0, 2, &Conf.viewer_shortcut, "shortcut", NULL, NULL }, +}; + +#define MAX_LIST_DEFAULT 100 + +//! 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 { - char word[WORDLEN]; - long offset; -} STRUCT_PACKED; + DICT_USB_CONNECTED = -4, + DICT_NOT_FOUND = -2, + DICT_QUIT = -3, + DICT_ERROR = -1, + DICT_OK, +}; -/* A funtion to get width and height etc (from viewer.c) */ -void init_screen(void) +#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 { -#ifdef HAVE_LCD_BITMAP - int w,h; + uint32_t offset; + uint32_t size; +}; - rb->lcd_getstringsize("o", &w, &h); - display_lines = LCD_HEIGHT / h; - display_columns = LCD_WIDTH / w; +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 - display_lines = 2; - display_columns = 11; #endif + +//! work around gcc wanting memcpy +void *memcpy(void *destination, const void *source, size_t num) +{ + return rb->memcpy(destination, source, num); } -/* global vars for pl_malloc() */ -void *bufptr; -ssize_t bufleft; +void str_toupper(const char *src, char *dst) +{ + while (*src) + *dst++ = toupper(*src++); + *dst = '\0'; +} -/* simple function to "allocate" memory in pluginbuffer. */ -void *pl_malloc(ssize_t size) +//! Display an error message +#define dict_error(...) \ + DEBUGF(__VA_ARGS__); DEBUGF("\n"); \ + LOGF(__VA_ARGS__); \ + rb->splash(HZ, __VA_ARGS__); + + +//! Initialize the plugin buffer +void dict_buf_init(void) { - void *ptr; - ptr = bufptr; + Malloc.buf = rb->plugin_get_buffer((size_t *)&Malloc.bufsize); + Malloc.bufpos = 0; +} - if (bufleft < size) +/** \brief Stack-like malloc + * + * This malloc implementation only keeps track of how much memory is allocated + */ +void *dict_malloc(size_t size) +{ + if (Malloc.bufpos+size <= Malloc.bufsize) { + Malloc.bufpos += size; + return (void *)Malloc.buf + Malloc.bufpos - size; + } + else + { + dict_error("Out of Memory"); return NULL; } +} + +/** \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->strncpy(Dict.path, name, n); + Dict.path[n] = '\0'; + + /* get filename without extension */ + n = dot-slash-1; + rb->strncpy(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) { - bufptr += size; - return ptr; + /* 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(rb, 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; } -/* init function for pl_malloc() */ -void pl_malloc_init(void) +//! Closes all file descriptors +void dict_close(void) { - bufptr = rb->plugin_get_buffer((size_t *)&bufleft); + 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); } -/* for endian problems */ -#ifdef ROCKBOX_BIG_ENDIAN -#define reverse(x) x -#else -long reverse (long N) { - unsigned char B[4]; - B[0] = (N & 0x000000FF) >> 0; - B[1] = (N & 0x0000FF00) >> 8; - B[2] = (N & 0x00FF0000) >> 16; - B[3] = (N & 0xFF000000) >> 24; - return ((B[0] << 24) | (B[1] << 16) | (B[2] << 8) | (B[3] << 0)); +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); + else + rb->snprintf(fn, sizeof(fn), "%s/%s.idx", Dict.path, Dict.name); + 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", ROCKBOX_DIR, Dict.name); + 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; } -#endif -/* Button definitions */ -#if CONFIG_KEYPAD == PLAYER_PAD -#define LP_QUIT BUTTON_STOP -#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ - (CONFIG_KEYPAD == IPOD_3G_PAD) -#define LP_QUIT BUTTON_MENU -#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD -#define LP_QUIT BUTTON_PLAY -#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD -#define LP_QUIT BUTTON_POWER -#elif CONFIG_KEYPAD == GIGABEAT_PAD -#define LP_QUIT BUTTON_POWER -#elif CONFIG_KEYPAD == SANSA_E200_PAD -#define LP_QUIT BUTTON_POWER -#elif CONFIG_KEYPAD == IRIVER_H10_PAD -#define LP_QUIT BUTTON_POWER -#else -#define LP_QUIT BUTTON_OFF -#endif +/** \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(); -/* data files */ -#define DICT_INDEX ROCKBOX_DIR "/dict.index" -#define DICT_DESC ROCKBOX_DIR "/dict.desc" + /* 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; -/* the main plugin function */ -enum plugin_status plugin_start(struct plugin_api* api, void* parameter) + /* configuration data */ + configfile_init(rb); + 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) { - char searchword[WORDLEN]; /* word to search for */ - char *description; /* pointer to description buffer */ - char *output; /* pointer to output buffer */ - char *ptr, *space; - struct stWord word; /* the struct to read into */ - int fIndex, fData; /* files */ - int filesize, high, low, probe; - int lines, len, outputted, next; + uint32_t offset; - /* plugin stuff */ - (void)parameter; - rb = api; + /* read offset */ + rb->lseek(Dict.fCache, sizeof(uint32_t)*index + sizeof(struct cache_h), + SEEK_SET); + rb->read(Dict.fCache, &offset, sizeof(uint32_t)); - /* get screen info */ - init_screen(); + return offset; +} - /* get pl_malloc() buffer ready. */ - pl_malloc_init(); +//! Compare function, that represents the order in the .idx file +int dict_index_strcmp(const char *str1, const char *str2) +{ + int a; - /* init description buffer (size is because we don't have scrolling)*/ - description = (char *)pl_malloc(display_columns * display_lines); - if (description == NULL) + a = rb->strcasecmp(str1, str2); + if (!a) { - DEBUGF("Err: failed to allocate description buffer."); - return PLUGIN_ERROR; + return rb->strcmp(str1, str2); } + + return a; +} - /* init output buffer */ - output = (char *)pl_malloc(display_columns); - if (output == NULL) +/** \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; iread(Dict.fIndex, buf, 1)) + break; + if (! *buf++) + { + i++; + break; + } } + return i; +} - /* "clear" input buffer */ - searchword[0] = '\0'; +/** \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); +} - rb->kbd_input(searchword, sizeof(searchword)); /* get the word to search */ +/** \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; + void *buf; - fIndex = rb->open(DICT_INDEX, O_RDONLY); /* index file */ - if (fIndex < 0) + buf = dict_malloc(Dict.DataLen); + 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)) { - DEBUGF("Err: Failed to open index file.\n"); - rb->splash(HZ*2, "Failed to open index."); - return PLUGIN_ERROR; + 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 ); + } - filesize = rb->filesize(fIndex); /* get filesize */ + dict_free(Dict.DataLen); - DEBUGF("Filesize: %d bytes = %d words \n", filesize, - (filesize / (int)sizeof(struct stWord))); + return size; +} - /* for the searching algorithm */ - high = filesize / sizeof( struct stWord ); - low = -1; +/** \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; - /* Jump to word pointed by probe, and read it. */ - rb->lseek(fIndex, sizeof(struct stWord) * probe, SEEK_SET); - rb->read(fIndex, &word, sizeof(struct stWord)); + dict_index_gets_from(probe, word, WORDLEN); + LOGF("%ld: '%s'", probe, word); /* jump according to the found word. */ - if (rb->strcasecmp(searchword, word.word) < 0) - { + cmp = cmpf(search, word); + if (cmp < 0) high = probe; - } + else if (cmp > 0) + low = probe; else { - low = probe; + probe = -probe; + break; } } - /* read in the word */ - rb->lseek(fIndex, sizeof(struct stWord) * low, SEEK_SET); - rb->read(fIndex, &word, sizeof(struct stWord)); + LOGF("probes: %d", count); + return -probe; +} - /* Check if we found something */ - if (low == -1 || rb->strcasecmp(searchword, word.word) != 0) +/** \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; + int buffer_size; + int n = 0, sel = 0; + int statusbar_setting; + + char **items; + buffer_size = 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 (nsplash(HZ*2, "Not found."); - rb->close(fIndex); - return PLUGIN_OK; + new_len = dict_index_gets(buf, WORDLEN); + if (!new_len) + break; + + /* 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(new_len); + if (items[n] == NULL) + break; + buffer_size += new_len; + + rb->strncpy(items[n], buf, new_len); + + n++; } - DEBUGF("Found %s at offset %ld\n", word.word, reverse(word.offset)); + /* 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_} + }; - /* now open the description file */ - fData = rb->open(DICT_DESC, O_RDONLY); - if (fData < 0) - { - DEBUGF("Err: Failed to open description file.\n"); - rb->splash(HZ*2, "Failed to open descriptions."); - rb->close(fIndex); - return PLUGIN_ERROR; + /* disable status bar */ + statusbar_setting = rb->global_settings->statusbar; + rb->global_settings->statusbar = false; + + sel = rb->do_menu(&menu, &sel); + + /* restore status bar */ + rb->global_settings->statusbar = statusbar_setting; } - /* seek to the right offset */ - rb->lseek(fData, (off_t)reverse(word.offset), SEEK_SET); + dict_free(buffer_size); - /* Read in the description */ - rb->read_line(fData, description, display_columns * display_lines); + if (n == 0) + return DICT_NOT_FOUND; - /* And print it to debug. */ - DEBUGF("Description: %s\n", description); + 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; + } +} - /* get pointer to first char */ - ptr = description; +//! 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]; - lines = 0; - outputted = 0; - len = rb->strlen(description); + if (n == Dict.fDict_n) + return DICT_OK; - /* clear screen */ - rb->lcd_clear_display(); + if (Dict.fDict > 0) + rb->close(Dict.fDict); - /* for large screens display the searched word. */ - if(display_lines > 4) + if (n < 1) + rb->snprintf(fn, sizeof(fn), "%s%s.dict", Dict.path, Dict.name); + else + rb->snprintf(fn, sizeof(fn), "%s%s.dict.%d", Dict.path, Dict.name, n); + + Dict.fDict = rb->open(fn, O_RDONLY); + if (Dict.fDict < 0) { - rb->lcd_puts(0, lines, searchword); - lines++; + dict_error("Failed to open description file: %s", fn); + return DICT_ERROR; } - /* TODO: Scroll, or just stop when there are to much lines. */ - while (1) + 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) { - /* copy one lcd line */ - rb->strncpy(output, ptr, display_columns); - output[display_columns] = '\0'; + rb->close(Dict.fDict); + if (dict_desc_open(++n) != DICT_OK) + return NULL; - /* typecast to kill a warning... */ - if((int)rb->strlen(ptr) < display_columns) + 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); + switch (selection) { - rb->lcd_puts(0, lines, output); - lines++; - break; + case 0: + quit = viewer_menu(); + break; + case 1: + quit = playback_control(rb); + break; + case 2: + quit = rb->set_int("Max listed articles", "", UNIT_INT, + &Conf.max_list, NULL, 10, 10, 100, 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 - /* get the last spacechar */ - space = rb->strrchr(output, ' '); + } +} - if (space != NULL) +//! 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; + + /* find destination article */ + p = rb->strchr(desc+sizeof(redirect)-1, '[') + 2; + while (p[len] != ']' && p[len] != '#') + len++; + + rb->strncpy(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) + { + /* keep the disk running */ + rb->ata_spindown(255); + + 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)) )) { - *space = '\0'; - next = (space - (char*)output) + 1; + return DICT_OK; } - else + new_search = false; + + result.index = dict_index_find_startingWith(search); + switch (result.index) { - next = display_columns; + case DICT_NOT_FOUND: + dict_error("No results for \"%s\"", search); + continue; + case DICT_QUIT: + continue; + case DICT_USB_CONNECTED: + return DICT_USB_CONNECTED; } - /* put the line on screen */ - rb->lcd_puts(0, lines, output); + /* get result data */ + dict_index_entry(result.index, &result); + do { + if (desc_size > 0) + dict_free(desc_size); - /* get output count */ - outputted += rb->strlen(output); + /* 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); - if (outputted < len) + /* 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); + /* stop the disk */ + + rb->ata_spindown(1); +#ifndef SIMULATOR + rb->ata_sleep(); +#endif + + rb->snprintf(desc, header_len, "%s\n", result.name); + desc[header_len-1] = '\n'; + desc[desc_size-1] = '\0'; + + n = viewer_set_text(Malloc.buf+Malloc.bufpos, desc); + dict_malloc(n); + desc_size += n; + + while (!new_search) { - /* set pointer to the next part */ - ptr += next; - lines++; + switch(viewer_run()) + { + case VIEWER_NEW: + new_search = true; + continue; + case VIEWER_ATTACHED_USB: + return DICT_USB_CONNECTED; + default: + return DICT_OK; + } } - else - { + + dict_free(desc_size); + } +} + +enum plugin_status plugin_start(struct plugin_api* api, void* file) +{ + enum plugin_status ret; + + rb = api; + + switch (dict_init(file)) + { + case DICT_ERROR: + return PLUGIN_ERROR; + case DICT_USB_CONNECTED: + return PLUGIN_USB_CONNECTED; + default: break; - } } - rb->lcd_update(); - /* wait for keypress */ - while(rb->button_get(true) != LP_QUIT) + switch (dict_main()) { - /* do nothing */ - /* maybe define some keys for navigation here someday. */ + case DICT_OK: + ret = PLUGIN_OK; + break; + case DICT_USB_CONNECTED: + ret = PLUGIN_USB_CONNECTED; + break; + default: + ret = PLUGIN_ERROR; + break; } - rb->close(fIndex); - rb->close(fData); - return PLUGIN_OK; + /* reset disk setting */ + rb->ata_spindown(rb->global_settings->disk_spindown); + + dict_close(); + + return ret; } Index: apps/plugins/lib/pluginlib_actions.h =================================================================== --- apps/plugins/lib/pluginlib_actions.h (revision 13609) +++ apps/plugins/lib/pluginlib_actions.h (working copy) @@ -47,9 +47,27 @@ static const struct button_mapping generic_directions[] = { -#if (CONFIG_KEYPAD == IRIVER_H100_PAD) \ - || (CONFIG_KEYPAD == IRIVER_H300_PAD) \ - || (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) \ +#if (CONFIG_KEYPAD == IRIVER_H100_PAD) + { PLA_UP, BUTTON_UP, BUTTON_NONE}, + { PLA_DOWN, BUTTON_DOWN, BUTTON_NONE}, + { PLA_LEFT, BUTTON_LEFT, BUTTON_NONE}, + { PLA_RIGHT, BUTTON_RIGHT, BUTTON_NONE}, + { PLA_UP_REPEAT, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE}, + { PLA_DOWN_REPEAT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE}, + { PLA_LEFT_REPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE}, + { PLA_RIGHT_REPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE}, + + /* remote */ + { PLA_UP, BUTTON_RC_SOURCE, BUTTON_NONE}, + { PLA_DOWN, BUTTON_RC_BITRATE, BUTTON_NONE}, + { PLA_LEFT, BUTTON_RC_REW, BUTTON_NONE}, + { PLA_RIGHT, BUTTON_RC_FF, BUTTON_NONE}, + { PLA_UP_REPEAT, BUTTON_RC_SOURCE|BUTTON_REPEAT, BUTTON_NONE}, + { PLA_DOWN_REPEAT, BUTTON_RC_BITRATE|BUTTON_REPEAT, BUTTON_NONE}, + { PLA_LEFT_REPEAT, BUTTON_RC_REW|BUTTON_REPEAT, BUTTON_NONE}, + { PLA_RIGHT_REPEAT, BUTTON_RC_FF|BUTTON_REPEAT, BUTTON_NONE}, +#elif (CONFIG_KEYPAD == IRIVER_H300_PAD) \ + || (CONFIG_KEYPAD == IAUDIO_X5M5_PAD) \ || (CONFIG_KEYPAD == GIGABEAT_PAD) \ || (CONFIG_KEYPAD == RECORDER_PAD) \ || (CONFIG_KEYPAD == ARCHOS_AV300_PAD) \ @@ -176,13 +194,25 @@ /* these were taken from the bubbles plugin, so may need tweaking */ static const struct button_mapping generic_actions[] = { -#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD) +#if (CONFIG_KEYPAD == IRIVER_H100_PAD) {PLA_QUIT, BUTTON_OFF, BUTTON_NONE}, - {PLA_QUIT, BUTTON_RC_STOP, BUTTON_NONE}, {PLA_START, BUTTON_ON, BUTTON_NONE}, {PLA_MENU, BUTTON_MODE, BUTTON_NONE}, {PLA_FIRE, BUTTON_SELECT, BUTTON_NONE}, {PLA_FIRE_REPEAT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE}, + + /* remote */ + {PLA_QUIT, BUTTON_RC_STOP, BUTTON_NONE}, + {PLA_START, BUTTON_RC_ON, BUTTON_NONE }, + {PLA_START, BUTTON_RC_MENU|BUTTON_REL, BUTTON_RC_MENU }, + {PLA_MENU, BUTTON_RC_MODE|BUTTON_REL, BUTTON_RC_MODE }, + {PLA_FIRE, BUTTON_RC_MODE|BUTTON_REPEAT, BUTTON_RC_MODE }, +#elif (CONFIG_KEYPAD == IRIVER_H300_PAD) + {PLA_QUIT, BUTTON_OFF, BUTTON_NONE}, + {PLA_START, BUTTON_ON, BUTTON_NONE}, + {PLA_MENU, BUTTON_MODE, BUTTON_NONE}, + {PLA_FIRE, BUTTON_SELECT, BUTTON_NONE}, + {PLA_FIRE_REPEAT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_NONE}, #elif (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD) {PLA_QUIT, BUTTON_MENU|BUTTON_SELECT, BUTTON_NONE}, {PLA_START, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY}, Index: apps/plugins/lib/viewer.c =================================================================== --- apps/plugins/lib/viewer.c (revision 0) +++ apps/plugins/lib/viewer.c (revision 0) @@ -0,0 +1,510 @@ +/*************************************************************************** +* __________ __ ___. +* 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 plugin_api *rb; +static struct viewer_txt *txt; + +/** Display dimensions */ +static int cols, rows; +#ifdef HAVE_REMOTE_LCD +static int remote_cols, remote_rows; +#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(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]; + + rb->lcd_clear_display(); + +#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); + rb->lcd_setmargins(VIEWER_SCROLLBAR_WIDTH, 0); + } +#endif + + /* draw main display */ + for (p=s, line=0; linestrncpy(buf, p, len); + buf[len] = '\0'; + + rb->lcd_puts(0, line, buf); + + 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; linestrncpy(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(struct plugin_api *newrb) +{ + rb = newrb; + +#ifdef HAVE_LCD_BITMAP + rb->lcd_getstringsize("o", &cols, &rows); +# ifdef HAVE_REMOTE_LCD + remote_cols = LCD_REMOTE_WIDTH / cols; + remote_rows = LCD_REMOTE_HEIGHT / rows; +# endif + cols = (LCD_WIDTH-VIEWER_SCROLLBAR_WIDTH) / cols; + rows = LCD_HEIGHT / rows; +#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(rb, 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; linelines; 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; + + DEBUGF("viewer buffer: %d bytes", (int)buffer_used); + 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->splash(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)) + { + case MENU_ATTACHED_USB: + case true: + return true; + default: + return false; + } +} Index: apps/plugins/lib/SOURCES =================================================================== --- apps/plugins/lib/SOURCES (revision 13609) +++ apps/plugins/lib/SOURCES (working copy) @@ -29,3 +29,4 @@ #endif #endif pluginlib_actions.c +viewer.c 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(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(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/settings.h =================================================================== --- apps/settings.h (revision 13609) +++ apps/settings.h (working copy) @@ -64,6 +64,7 @@ #define CODECS_DIR ROCKBOX_DIR "/codecs" #define RECPRESETS_DIR ROCKBOX_DIR "/recpresets" #define FMPRESET_PATH ROCKBOX_DIR "/fmpresets" +#define DICTS_DIR "/dicts" #define VIEWERS_CONFIG ROCKBOX_DIR "/viewers.config" #define CONFIGFILE ROCKBOX_DIR "/config.cfg" @@ -159,7 +160,8 @@ * must be added after NUM_FILTER_MODES. */ enum { SHOW_ALL, SHOW_SUPPORTED, SHOW_MUSIC, SHOW_PLAYLIST, SHOW_ID3DB, NUM_FILTER_MODES, - SHOW_WPS, SHOW_RWPS, SHOW_FMR, SHOW_CFG, SHOW_LNG, SHOW_MOD, SHOW_FONT, SHOW_PLUGINS}; + SHOW_WPS, SHOW_RWPS, SHOW_FMR, SHOW_CFG, SHOW_LNG, + SHOW_MOD, SHOW_FONT, SHOW_DICTS, SHOW_PLUGINS}; /* recursive dir insert options */ enum { RECURSE_OFF, RECURSE_ON, RECURSE_ASK }; Index: apps/filetree.c =================================================================== --- apps/filetree.c (revision 13609) +++ apps/filetree.c (working copy) @@ -266,7 +266,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_REMOTE_LCD (*c->dirfilter == SHOW_RWPS && (dptr->attr & FILE_ATTR_MASK) != FILE_ATTR_RWPS) || @@ -572,6 +573,7 @@ else { if (*c->dirfilter > NUM_FILTER_MODES && *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 13609) +++ apps/root_menu.c (working copy) @@ -172,6 +172,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; snprintf(folder, MAX_PATH, "%s/", PLUGIN_DIR); @@ -282,6 +286,7 @@ #endif [GO_TO_RECENTBMARKS] = { load_bmarks, NULL, &bookmark_settings_menu }, + [GO_TO_BROWSEDICTS] = { browser, (void*)GO_TO_BROWSEDICTS, NULL }, [GO_TO_BROWSEPLUGINS] = { browser, (void*)GO_TO_BROWSEPLUGINS, NULL }, }; @@ -295,6 +300,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); char *get_wps_item_name(int selected_item, void * data, char *buffer) @@ -346,7 +353,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 13609) +++ apps/root_menu.h (working copy) @@ -46,6 +46,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, };