/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "plugin.h" #include #include "lib/playback_control.h" PLUGIN_HEADER /* global settings file * binary file, so dont use .cfg * * setting file format * * part byte count * -------------------------------- * 'TVGS' 4 * version 1 * word_mode 1 * line_mode 1 * view_mode 1 * alignment 1 * encoding 1 * scrollbar_mode 1 * need_scrollbar 1 * page_mode 1 * page_number_mode 1 * title_mode 1 * scroll_mode 1 * autoscroll_speed 1 * font name MAX_PATH */ #define GLOBAL_SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* temporary file */ #define GLOBAL_SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp" #define GLOBAL_SETTINGS_HEADER "\x54\x56\x47\x53" /* header="TVGS" */ #define GLOBAL_SETTINGS_H_SIZE 5 #define GLOBAL_SETTINGS_VERSION 0x32 /* version=2 */ #define GLOBAL_SETTINGS_FIRST_VERSION 0x31 /* preferences and bookmarks at each file * binary file, so dont use .cfg * * setting file format * * part byte count * -------------------------------- * 'TVS' 3 * version 1 * file count 2 * [1st file] * file path MAX_PATH * next file pos 2 * [preferences] * word_mode 1 * line_mode 1 * view_mode 1 * alignment 1 * encoding 1 * scrollbar_mode 1 * need_scrollbar 1 * page_mode 1 * header_mode 1 * footer_mode 1 * scroll_mode 1 * autoscroll_speed 1 * font name MAX_PATH * bookmark count 1 * [1st bookmark] * file_position 4 * page 2 * line 1 * flag 1 * [2nd bookmark] * ... * [last bookmark] * [2nd file] * ... * [last file] */ #define SETTINGS_FILE VIEWERS_DIR "/viewer_file.dat" /* temporary file */ #define SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp" #define SETTINGS_HEADER "\x54\x56\x53" /* header="TVS" */ #define SETTINGS_H_SIZE 4 #define SETTINGS_VERSION 0x33 /* version=3 */ #define SETTINGS_FIRST_VERSION 0x32 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */ #define NARROW_MAX_COLUMNS 64 /* Max displayable string len [narrow] (over-estimate) */ #define WIDE_MAX_COLUMNS 128 /* Max displayable string len [wide] (over-estimate) */ #define MAX_WIDTH 910 /* Max line length in WIDE mode */ #define READ_PREV_ZONE (block_size*9/10) /* Arbitrary number less than SMALL_BLOCK_SIZE */ #define SMALL_BLOCK_SIZE block_size /* Smallest file chunk we will read */ #define LARGE_BLOCK_SIZE (block_size << 1) /* Preferable size of file chunk to read */ #define TOP_SECTOR buffer #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE) #define BOTTOM_SECTOR (buffer + (SMALL_BLOCK_SIZE << 1)) #undef SCROLLBAR_WIDTH #define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width #define MAX_PAGE 9999 #define BOOKMARK_SIZE 8 #define MAX_BOOKMARKS 10 /* user setting bookmarks + last read page */ #define BOOKMARK_LAST 1 #define BOOKMARK_USER 2 #ifndef HAVE_LCD_BITMAP #define BOOKMARK_ICON "\xee\x84\x81\x00" #endif #define PREFERENCES_SIZE (12 + MAX_PATH) /* Out-Of-Bounds test for any pointer to data in the buffer */ #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end) /* Does the buffer contain the beginning of the file? */ #define BUFFER_BOF() (file_pos==0) /* Does the buffer contain the end of the file? */ #define BUFFER_EOF() (file_size-file_pos <= buffer_size) /* Formula for the endpoint address outside of buffer data */ #define BUFFER_END() \ ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size)) /* Is the entire file being shown in one screen? */ #define ONE_SCREEN_FITS_ALL() \ (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF()) /* Is a scrollbar called for on the current screen? */ #define NEED_SCROLLBAR() \ ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON)) /* variable button definitions */ /* Recorder keys */ #if CONFIG_KEYPAD == RECORDER_PAD #define VIEWER_QUIT BUTTON_OFF #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_F1 #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP) #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN) #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT) #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT) #define VIEWER_BOOKMARK BUTTON_F2 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD #define VIEWER_QUIT BUTTON_OFF #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_F1 #define VIEWER_AUTOSCROLL BUTTON_SELECT #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP) #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN) #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT) #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT) #define VIEWER_BOOKMARK BUTTON_F2 /* Ondio keys */ #elif CONFIG_KEYPAD == ONDIO_PAD #define VIEWER_QUIT BUTTON_OFF #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT) #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL) #define VIEWER_BOOKMARK (BUTTON_MENU|BUTTON_OFF) /* Player keys */ #elif CONFIG_KEYPAD == PLAYER_PAD #define VIEWER_QUIT BUTTON_STOP #define VIEWER_PAGE_UP BUTTON_LEFT #define VIEWER_PAGE_DOWN BUTTON_RIGHT #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT) #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT) #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_BOOKMARK BUTTON_ON /* iRiver H1x0 && H3x0 keys */ #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) #define VIEWER_QUIT BUTTON_OFF #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MODE #define VIEWER_AUTOSCROLL BUTTON_SELECT #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP) #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN) #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT) #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT) #define VIEWER_BOOKMARK (BUTTON_ON | BUTTON_SELECT) #define VIEWER_RC_QUIT BUTTON_RC_STOP /* iPods */ #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) || \ (CONFIG_KEYPAD == IPOD_1G2G_PAD) #define VIEWER_QUIT_PRE BUTTON_SELECT #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU) #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_BOOKMARK BUTTON_SELECT /* iFP7xx keys */ #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD #define VIEWER_QUIT BUTTON_PLAY #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MODE #define VIEWER_AUTOSCROLL BUTTON_SELECT #define VIEWER_BOOKMARK (BUTTON_LEFT|BUTTON_SELECT) /* iAudio X5 keys */ #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_SELECT #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_BOOKMARK BUTTON_REC /* GIGABEAT keys */ #elif CONFIG_KEYPAD == GIGABEAT_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_A #define VIEWER_BOOKMARK BUTTON_SELECT /* Sansa E200 keys */ #elif CONFIG_KEYPAD == SANSA_E200_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_SELECT #define VIEWER_AUTOSCROLL BUTTON_REC #define VIEWER_LINE_UP BUTTON_SCROLL_BACK #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT) /* Sansa Fuze keys */ #elif CONFIG_KEYPAD == SANSA_FUZE_PAD #define VIEWER_QUIT (BUTTON_HOME|BUTTON_REPEAT) #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN #define VIEWER_LINE_UP BUTTON_SCROLL_BACK #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD #define VIEWER_BOOKMARK BUTTON_SELECT /* Sansa C200 keys */ #elif CONFIG_KEYPAD == SANSA_C200_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_VOL_UP #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_SELECT #define VIEWER_AUTOSCROLL BUTTON_REC #define VIEWER_LINE_UP BUTTON_UP #define VIEWER_LINE_DOWN BUTTON_DOWN #define VIEWER_BOOKMARK (BUTTON_DOWN | BUTTON_SELECT) /* Sansa Clip keys */ #elif CONFIG_KEYPAD == SANSA_CLIP_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_VOL_UP #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_SELECT #define VIEWER_AUTOSCROLL BUTTON_HOME #define VIEWER_LINE_UP BUTTON_UP #define VIEWER_LINE_DOWN BUTTON_DOWN #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT) /* Sansa M200 keys */ #elif CONFIG_KEYPAD == SANSA_M200_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_VOL_UP #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP) #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL) #define VIEWER_LINE_UP BUTTON_UP #define VIEWER_LINE_DOWN BUTTON_DOWN #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT) /* iriver H10 keys */ #elif CONFIG_KEYPAD == IRIVER_H10_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_SCROLL_UP #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_REW #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_BOOKMARK BUTTON_FF /*M-Robe 500 keys */ #elif CONFIG_KEYPAD == MROBE500_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_RC_PLAY #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_RC_HEART #define VIEWER_AUTOSCROLL BUTTON_RC_MODE #define VIEWER_BOOKMARK BUTTON_CENTER /*Gigabeat S keys */ #elif CONFIG_KEYPAD == GIGABEAT_S_PAD #define VIEWER_QUIT BUTTON_BACK #define VIEWER_PAGE_UP BUTTON_PREV #define VIEWER_PAGE_DOWN BUTTON_NEXT #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT) #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT) #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL) #define VIEWER_LINE_UP BUTTON_UP #define VIEWER_LINE_DOWN BUTTON_DOWN #define VIEWER_COLUMN_LEFT BUTTON_LEFT #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT #define VIEWER_BOOKMARK BUTTON_SELECT /*M-Robe 100 keys */ #elif CONFIG_KEYPAD == MROBE100_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_DISPLAY #define VIEWER_BOOKMARK BUTTON_SELECT /* iAUdio M3 keys */ #elif CONFIG_KEYPAD == IAUDIO_M3_PAD #define VIEWER_QUIT BUTTON_REC #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN #define VIEWER_SCREEN_LEFT BUTTON_RC_REW #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF #define VIEWER_MENU BUTTON_RC_MENU #define VIEWER_AUTOSCROLL BUTTON_RC_MODE #define VIEWER_RC_QUIT BUTTON_RC_REC #define VIEWER_BOOKMARK BUTTON_RC_PLAY /* Cowon D2 keys */ #elif CONFIG_KEYPAD == COWON_D2_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_MENU BUTTON_MENU #define VIEWER_PAGE_UP BUTTON_MINUS #define VIEWER_PAGE_DOWN BUTTON_PLUS #define VIEWER_BOOKMARK (BUTTON_MENU|BUTTON_PLUS) #elif CONFIG_KEYPAD == IAUDIO67_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_VOLUP #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_RC_QUIT BUTTON_STOP #define VIEWER_BOOKMARK (BUTTON_LEFT|BUTTON_PLAY) /* Creative Zen Vision:M keys */ #elif CONFIG_KEYPAD == CREATIVEZVM_PAD #define VIEWER_QUIT BUTTON_BACK #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_SELECT #define VIEWER_BOOKMARK BUTTON_PLAY /* Philips HDD1630 keys */ #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_VIEW #define VIEWER_BOOKMARK BUTTON_SELECT /* Philips SA9200 keys */ #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_PREV #define VIEWER_SCREEN_RIGHT BUTTON_NEXT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_BOOKMARK BUTTON_RIGHT /* Onda VX747 keys */ #elif CONFIG_KEYPAD == ONDAVX747_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_MENU BUTTON_MENU #define VIEWER_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER) /* Onda VX777 keys */ #elif CONFIG_KEYPAD == ONDAVX777_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER) /* SAMSUNG YH-820 / YH-920 / YH-925 keys */ #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD #define VIEWER_QUIT BUTTON_REC #define VIEWER_PAGE_UP BUTTON_UP #define VIEWER_PAGE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_LEFT #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_PLAY #define VIEWER_AUTOSCROLL BUTTON_REW #define VIEWER_BOOKMARK BUTTON_FFWD /* Packard Bell Vibe 500 keys */ #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD #define VIEWER_QUIT BUTTON_REC #define VIEWER_PAGE_UP BUTTON_OK #define VIEWER_PAGE_DOWN BUTTON_CANCEL #define VIEWER_LINE_UP BUTTON_UP #define VIEWER_LINE_DOWN BUTTON_DOWN #define VIEWER_SCREEN_LEFT BUTTON_PREV #define VIEWER_SCREEN_RIGHT BUTTON_NEXT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_PLAY #define VIEWER_BOOKMARK BUTTON_POWER #else #error No keymap defined! #endif #ifdef HAVE_TOUCHSCREEN #ifdef VIEWER_QUIT #define VIEWER_QUIT2 BUTTON_TOPLEFT #else #define VIEWER_QUIT BUTTON_TOPLEFT #endif #ifdef VIEWER_PAGE_UP #define VIEWER_PAGE_UP2 BUTTON_TOPMIDDLE #else #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE #endif #ifdef VIEWER_PAGE_DOWN #define VIEWER_PAGE_DOWN2 BUTTON_BOTTOMMIDDLE #else #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE #endif #ifndef VIEWER_SCREEN_LEFT #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT #endif #ifndef VIEWER_SCREEN_RIGHT #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT #endif #ifdef VIEWER_MENU #define VIEWER_MENU2 BUTTON_TOPRIGHT #else #define VIEWER_MENU BUTTON_TOPRIGHT #endif #ifndef VIEWER_AUTOSCROLL #define VIEWER_AUTOSCROLL BUTTON_CENTER #endif #endif /* stuff for the bookmarking */ struct bookmark_info { long file_position; int page; int line; unsigned char flag; }; struct preferences { enum { WRAP=0, CHOP, } word_mode; enum { NORMAL=0, JOIN, EXPAND, REFLOW, /* won't be set on charcell LCD, must be last */ } line_mode; enum { NARROW=0, WIDE, } view_mode; enum { LEFT=0, RIGHT, } alignment; enum codepages encoding; enum { SB_OFF=0, SB_ON, } scrollbar_mode; bool need_scrollbar; enum { NO_OVERLAP=0, OVERLAP, } page_mode; enum { HD_NONE = 0, HD_PATH, HD_SBAR, HD_BOTH, } header_mode; enum { FT_NONE = 0, FT_PAGE, FT_SBAR, FT_BOTH, } footer_mode; enum { PAGE=0, LINE, } scroll_mode; int autoscroll_speed; unsigned char font[MAX_PATH]; }; enum { VIEWER_FONT_MENU = 0, VIEWER_FONT_TEXT, }; struct preferences prefs; struct preferences old_prefs; static unsigned char *buffer; static long buffer_size; static long block_size = 0x1000; static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'}; static int display_columns; /* number of (pixel) columns on the display */ static int display_lines; /* number of lines on the display */ static int draw_columns; /* number of (pixel) columns available for text */ static int par_indent_spaces; /* number of spaces to indent first paragraph */ static int fd; static const char *file_name; static long file_size; static long start_position; /* position in the file after the viewer is started */ static bool mac_text; static long file_pos; /* Position of the top of the buffer in the file */ static long last_file_pos; static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/ static int max_width; static int max_columns; static int cline = 1; static int cpage = 1; static int lpage = 0; static unsigned char *screen_top_ptr; static unsigned char *next_screen_ptr; static unsigned char *next_screen_to_draw_ptr; static unsigned char *next_line_ptr; static unsigned char *last_screen_top_ptr = NULL; #ifdef HAVE_LCD_BITMAP static struct font *pf; static int header_height = 0; static int footer_height = 0; #endif struct bookmark_info bookmarks[MAX_BOOKMARKS]; static int bookmark_count; /* UTF-8 BOM */ #define BOM "\xef\xbb\xbf" #define BOM_SIZE 3 static bool is_bom = false; /* We draw a diacritic char over a non-diacritic one. Therefore, such chars are * not considered to occupy space, therefore buffers might have more than * max_columns characters. The DIACRITIC_FACTOR is the max ratio between all * characters and non-diacritic characters in the buffer */ #define DIACRITIC_FACTOR 2 /* calculate the width of a UCS character (zero width for diacritics) */ static int glyph_width(unsigned short ch) { if (ch == 0) ch = ' '; #ifdef HAVE_LCD_BITMAP if (rb->is_diacritic(ch, NULL)) return 0; return rb->font_get_width(pf, ch); #else return 1; #endif } /* get UCS character from string */ static unsigned char* get_ucs(const unsigned char* str, unsigned short* ch) { unsigned char utf8_tmp[6]; int count = 2; if (prefs.encoding == UTF_8) return (unsigned char*)rb->utf8decode(str, ch); rb->iso_decode(str, utf8_tmp, prefs.encoding, count); rb->utf8decode(utf8_tmp, ch); /* return a pointer after the parsed section of the string */ #ifdef HAVE_LCD_BITMAP if (prefs.encoding >= SJIS && *str >= 0x80 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0)) return (unsigned char*)str+2; else #endif return (unsigned char*)str+1; } /* decode iso string into UTF-8 string */ static unsigned char *decode2utf8(const unsigned char *src, unsigned char *dst, int skip_width, int disp_width) { unsigned short ucs[max_columns * DIACRITIC_FACTOR + 1]; unsigned short ch; const unsigned char *oldstr = src; const unsigned char *str = src; unsigned char *utf8 = dst; int chars = 0; int idx = 0; int width = max_width; if (prefs.alignment == LEFT) { /* skip the skip_width */ if (skip_width > 0) { while (skip_width > 0 && *str != '\0') { oldstr = str; str = get_ucs(oldstr, &ch); skip_width -= glyph_width(ch); } if (skip_width < 0) str = oldstr; } /* decode until string end or disp_width reached */ while(*str != '\0') { str = get_ucs(str, &ch); disp_width -= glyph_width(ch); if (disp_width < 0) break; utf8 = rb->utf8encode(ch, utf8); } } else { while (width > 0 && *str != '\0') { str = get_ucs(str, &ch); ucs[chars++] = ch; } ucs[chars] = 0; skip_width = max_width - skip_width - disp_width; if (skip_width > 0) { while (skip_width > 0 && chars-- > 0) skip_width -= glyph_width(ucs[chars]); if (skip_width < 0) chars++; } else { idx = chars; while (disp_width > 0 && idx-- > 0) disp_width -= glyph_width(ucs[idx]); if (disp_width < 0 || idx < 0) idx++; } for ( ; idx < chars; idx++) utf8 = rb->utf8encode(ucs[idx], utf8); } *utf8 = '\0'; /* return a pointer after the dst string ends */ return utf8; } /* set max_columns and max_width */ static void calc_max_width(void) { if (prefs.view_mode == NARROW) { max_columns = NARROW_MAX_COLUMNS; max_width = draw_columns; } else { max_columns = WIDE_MAX_COLUMNS; max_width = 2 * draw_columns; } } static bool done = false; static int col = 0; static inline void advance_conters(unsigned short ch, int* k, int* width) { #ifdef HAVE_LCD_BITMAP /* diacritics do not count */ if (rb->is_diacritic(ch, NULL)) return; #endif *width += glyph_width(ch); (*k)++; } static inline bool line_is_full(int k, int width) { return ((k >= max_columns - 1) || (width >= max_width)); } static unsigned char* crop_at_width(const unsigned char* p) { int k,width; unsigned short ch; const unsigned char *oldp = p; k=width=0; while (!line_is_full(k, width)) { oldp = p; if (BUFFER_OOB(p)) break; p = get_ucs(p, &ch); advance_conters(ch, &k, &width); } return (unsigned char*)oldp; } static unsigned char* find_first_feed(const unsigned char* p, int size) { int s = 0; unsigned short ch; const unsigned char *oldp = p; const unsigned char *lbrkp = NULL; int j; int width = 0; while(s <= size) { if (*p == 0) return (unsigned char*)p; oldp = p; p = get_ucs(p, &ch); if (prefs.word_mode == WRAP) { for (j = 0; j < ((int) sizeof(line_break)); j++) { if (ch == line_break[j]) { lbrkp = p; break; } } } width += glyph_width(ch); if (width > max_width) return (lbrkp == NULL)? (unsigned char*)oldp : (unsigned char*)lbrkp; s += (p - oldp); } return NULL; } static unsigned char* find_last_feed(const unsigned char* p, int size) { int i; for (i=size-1; i>=0; i--) if (p[i] == 0) return (unsigned char*) p+i; return NULL; } static unsigned char* find_last_space(const unsigned char* p, int size) { int i, j, k; k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1; i = size; if (!BUFFER_OOB(&p[i])) for (j=k; j < ((int) sizeof(line_break)) - 1; j++) { if (p[i] == line_break[j]) return (unsigned char*) p+i; } if (prefs.word_mode == WRAP) { for (i=size-1; i>=0; i--) { for (j=k; j < (int) sizeof(line_break) - 1; j++) { if (p[i] == line_break[j]) return (unsigned char*) p+i; } } } return NULL; } static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short) { const unsigned char *next_line = NULL; int size, i, j, j_next, j_prev, k, width, search_len, spaces, newlines; bool first_chars; unsigned short ch; if (is_short != NULL) *is_short = true; if BUFFER_OOB(cur_line) return NULL; if (prefs.view_mode == WIDE) { search_len = MAX_WIDTH; } else { /* prefs.view_mode == NARROW */ search_len = crop_at_width(cur_line) - cur_line; } size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len; if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) { /* Need to scan ahead and possibly increase search_len and size, or possibly set next_line at second hard return in a row. */ next_line = NULL; first_chars=true; j_next=j=k=width=spaces=newlines=0; while (1) { const unsigned char *p, *oldp; j_prev = j; j = j_next; if (BUFFER_OOB(cur_line+j)) return NULL; if (line_is_full(k, width)) { size = search_len = j_prev; break; } oldp = p = &cur_line[j]; p = get_ucs(p, &ch); j_next = j + (p - oldp); switch (ch) { case ' ': if (prefs.line_mode == REFLOW) { if (newlines > 0) { size = j; next_line = cur_line + size; return (unsigned char*) next_line; } if (j==0) /* i=1 is intentional */ for (i=0; i 0) { size = j; next_line = cur_line + size - spaces; if (next_line != cur_line) return (unsigned char*) next_line; break; } newlines++; size += spaces -1; if (BUFFER_OOB(cur_line+size) || size > 2*search_len) return NULL; search_len = size; spaces = first_chars? 0:1; break; default: if (prefs.line_mode==JOIN || newlines>0) { while (spaces) { spaces--; advance_conters(' ', &k, &width); if (line_is_full(k, width)) { size = search_len = j; break; } } newlines=0; } else if (spaces) { /* REFLOW, multiple spaces between words: count only * one. If more are needed, they will be added * while drawing. */ search_len = size; spaces=0; advance_conters(' ', &k, &width); if (line_is_full(k, width)) { size = search_len = j; break; } } first_chars = false; advance_conters(ch, &k, &width); break; } } } else { /* find first hard return */ next_line = find_first_feed(cur_line, size); } if (next_line == NULL) if (size == search_len) { if (prefs.word_mode == WRAP) /* Find last space */ next_line = find_last_space(cur_line, size); if (next_line == NULL) { next_line = crop_at_width(cur_line); } else { if (prefs.word_mode == WRAP) { for (i=0;i READ_PREV_ZONE) prev_line = p = buffer; /* (else return NULL and read previous block) */ /* Wrap downwards until too far, then use the one before. */ while (p != NULL && p < cur_line) { prev_line = p; p = find_next_line(prev_line, NULL); } if (BUFFER_OOB(prev_line)) return NULL; return (unsigned char*) prev_line; } static void check_bom(void) { unsigned char bom[BOM_SIZE]; off_t orig = rb->lseek(fd, 0, SEEK_CUR); is_bom = false; rb->lseek(fd, 0, SEEK_SET); if (rb->read(fd, bom, BOM_SIZE) == BOM_SIZE) is_bom = !memcmp(bom, BOM, BOM_SIZE); rb->lseek(fd, orig, SEEK_SET); } static void fill_buffer(long pos, unsigned char* buf, unsigned size) { /* Read from file and preprocess the data */ /* To minimize disk access, always read on sector boundaries */ unsigned numread, i; bool found_CR = false; off_t offset = rb->lseek(fd, pos, SEEK_SET); if (offset == 0 && prefs.encoding == UTF_8 && is_bom) rb->lseek(fd, BOM_SIZE, SEEK_SET); numread = rb->read(fd, buf, size - 1); buf[numread] = 0; rb->button_clear_queue(); /* clear button queue */ for(i = 0; i < numread; i++) { switch(buf[i]) { case '\r': if (mac_text) { buf[i] = 0; } else { buf[i] = ' '; found_CR = true; } break; case '\n': buf[i] = 0; found_CR = false; break; case 0: /* No break between case 0 and default, intentionally */ buf[i] = ' '; default: if (found_CR) { buf[i - 1] = 0; found_CR = false; mac_text = true; } break; } } } static int viewer_find_bookmark(int page, int line) { int i; for (i = 0; i < bookmark_count; i++) { if (bookmarks[i].page == page && bookmarks[i].line == line) return i; } return -1; } static int read_and_synch(int direction) { /* Read next (or prev) block, and reposition global pointers. */ /* direction: 1 for down (i.e., further into file), -1 for up */ int move_size, move_vector, offset; unsigned char *fill_buf; if (direction == -1) /* up */ { move_size = SMALL_BLOCK_SIZE; offset = 0; fill_buf = TOP_SECTOR; rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE); rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE); } else /* down */ { if (prefs.view_mode == WIDE) { /* WIDE mode needs more buffer so we have to read smaller blocks */ move_size = SMALL_BLOCK_SIZE; offset = LARGE_BLOCK_SIZE; fill_buf = BOTTOM_SECTOR; rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE); rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE); } else { move_size = LARGE_BLOCK_SIZE; offset = SMALL_BLOCK_SIZE; fill_buf = MID_SECTOR; rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE); } } move_vector = direction * move_size; screen_top_ptr -= move_vector; file_pos += move_vector; buffer_end = BUFFER_END(); /* Update whenever file_pos changes */ fill_buffer(file_pos + offset, fill_buf, move_size); return move_vector; } static void get_next_line_position(unsigned char **line_begin, unsigned char **line_end, bool *is_short) { int resynch_move; *line_begin = *line_end; *line_end = find_next_line(*line_begin, is_short); if (*line_end == NULL && !BUFFER_EOF()) { resynch_move = read_and_synch(1); /* Read block & move ptrs */ *line_begin -= resynch_move; if (next_line_ptr > buffer) next_line_ptr -= resynch_move; *line_end = find_next_line(*line_begin, is_short); } } static void increment_current_line(void) { if (cline < display_lines) cline++; else if (cpage < MAX_PAGE) { cpage++; cline = 1; } } static void decrement_current_line(void) { if (cline > 1) cline--; else if (cpage > 1) { cpage--; cline = display_lines; } } static void viewer_scroll_up(void) { unsigned char *p; p = find_prev_line(screen_top_ptr); if (p == NULL && !BUFFER_BOF()) { read_and_synch(-1); p = find_prev_line(screen_top_ptr); } if (p != NULL) screen_top_ptr = p; decrement_current_line(); } static void viewer_scroll_down(bool autoscroll) { if (cpage == lpage) return; if (next_line_ptr != NULL) screen_top_ptr = next_line_ptr; if (prefs.scroll_mode == LINE || autoscroll) increment_current_line(); cline++; if (cline > display_lines) { cline = 1; cpage++; } } static void viewer_scroll_to_top_line(void) { int line; for (line = cline; line > 1; line--) viewer_scroll_up(); } #ifdef HAVE_LCD_BITMAP static void viewer_scrollbar(void) { int items, min_shown, max_shown, sb_begin_y, sb_height; items = (int) file_size; /* (SH1 int is same as long) */ min_shown = (int) file_pos + (screen_top_ptr - buffer); if (next_screen_ptr == NULL) max_shown = items; else max_shown = min_shown + (next_screen_ptr - screen_top_ptr); sb_begin_y = header_height; sb_height = LCD_HEIGHT - header_height - footer_height; rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, sb_begin_y, SCROLLBAR_WIDTH-1, sb_height, items, min_shown, max_shown, VERTICAL); } #endif #ifdef HAVE_LCD_BITMAP static void viewer_show_header(void) { if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH) rb->gui_syncstatusbar_draw(rb->statusbars, true); if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH) rb->lcd_putsxy(0, header_height - pf->height, file_name); } static void viewer_show_footer(void) { if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH) rb->gui_syncstatusbar_draw(rb->statusbars, true); if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH) { unsigned char buf[12]; if (cline == 1) rb->snprintf(buf, sizeof(buf), "%d", cpage); else rb->snprintf(buf, sizeof(buf), "%d - %d", cpage, cpage+1); rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf); } } #endif static void viewer_draw(int col) { int i, j, k, line_len, line_width, spaces, left_col=0; int width, extra_spaces, indent_spaces, spaces_per_word, spaces_width, disp_width = 0; bool multiple_spacing, line_is_short; unsigned short ch; unsigned char *str, *oldstr; unsigned char *line_begin; unsigned char *line_end; unsigned char c; int max_chars = max_columns * DIACRITIC_FACTOR; unsigned char scratch_buffer[max_chars * 4 + 1]; unsigned char utf8_buffer[max_chars * 4 + 1]; /* If col==-1 do all calculations but don't display */ if (col != -1) { #ifdef HAVE_LCD_BITMAP left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0; #else left_col = 0; #endif rb->lcd_clear_display(); } line_begin = line_end = screen_top_ptr; for (i = 0; i < display_lines; i++) { if (BUFFER_OOB(line_end)) { if (lpage == cpage) break; /* Happens after display last line at BUFFER_EOF() */ if (lpage == 0 && cline == 1) { lpage = cpage; last_screen_top_ptr = screen_top_ptr; last_file_pos = file_pos; } } get_next_line_position(&line_begin, &line_end, &line_is_short); if (line_end == NULL) { if (BUFFER_OOB(line_begin)) break; line_end = buffer_end + 1; } line_len = line_end - line_begin; /* calculate line_len */ str = oldstr = line_begin; j = -1; while (str < line_end) { oldstr = str; str = crop_at_width(str); j++; if (oldstr == str) { oldstr = line_end; break; } } /* width of un-displayed part of the line */ line_width = j*draw_columns; spaces_width = 0; while (oldstr < line_end) { oldstr = get_ucs(oldstr, &ch); /* add width of displayed part of the line */ if (ch) { int dw = glyph_width(ch); /* avoid counting spaces at the end of the line */ if (ch == ' ') { spaces_width += dw; } else { line_width += dw + spaces_width; spaces_width = 0; } } } if (prefs.line_mode == JOIN) { if (line_begin[0] == 0) { line_begin++; if (prefs.word_mode == CHOP) line_end++; else line_len--; } for (j=k=spaces=0; j < line_len; j++) { if (k == max_chars) break; c = line_begin[j]; switch (c) { case ' ': spaces++; break; case 0: spaces = 0; scratch_buffer[k++] = ' '; break; default: while (spaces) { spaces--; scratch_buffer[k++] = ' '; if (k == max_chars - 1) break; } scratch_buffer[k++] = c; break; } } scratch_buffer[k] = 0; } else if (prefs.line_mode == REFLOW) { if (line_begin[0] == 0) { line_begin++; if (prefs.word_mode == CHOP) line_end++; else line_len--; } indent_spaces = 0; if (!line_is_short) { multiple_spacing = false; width=spaces=0; for (str = line_begin; str < line_end; ) { str = get_ucs(str, &ch); switch (ch) { case 0: case ' ': if (str == line_begin) { if (prefs.word_mode == WRAP && prefs.alignment == LEFT) { /* special case: indent the paragraph, * don't count spaces */ indent_spaces = par_indent_spaces; } } else if (!multiple_spacing) spaces++; multiple_spacing = true; break; default: multiple_spacing = false; width += glyph_width(ch); break; } } if (multiple_spacing) spaces--; if (spaces) { /* total number of spaces to insert between words */ extra_spaces = (max_width-width)/glyph_width(' ') - indent_spaces; /* number of spaces between each word*/ spaces_per_word = extra_spaces / spaces; /* number of words with n+1 spaces (to fill up) */ extra_spaces = extra_spaces % spaces; if (spaces_per_word > 2) { /* too much spacing is awful */ spaces_per_word = 3; extra_spaces = 0; } } else { /* this doesn't matter much... no spaces anyway */ spaces_per_word = extra_spaces = 0; } } else { /* end of a paragraph: don't fill line */ spaces_per_word = 1; extra_spaces = 0; } multiple_spacing = false; for (j=k=spaces=0; j < line_len; j++) { if (k == max_chars) break; c = line_begin[j]; switch (c) { case 0: if (j == line_len - 1) break; case ' ': if (j==0) { /* indent paragraph */ if (prefs.word_mode == WRAP && prefs.alignment == LEFT) { for (j=0; jmemcpy(scratch_buffer, line_begin, line_len); scratch_buffer[line_len] = '\0'; } } /* create displayed line */ if (col != -1) { decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns); rb->lcd_getstringsize(utf8_buffer, &disp_width, NULL); } /* display on screen the displayed part of the line */ if (col != -1) { bool in_page = (cline+i <= display_lines); int dpage = cpage + (in_page ? 0 : 1); int dline = cline + i - (in_page ? 0 : display_lines); bool bflag = (viewer_find_bookmark(dpage, dline) >= 0); #ifdef HAVE_LCD_BITMAP int dy = i * pf->height + header_height; int dx = (prefs.alignment == LEFT)? left_col : LCD_WIDTH - disp_width; #endif if (bflag) #ifdef HAVE_LCD_BITMAP { rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG); rb->lcd_fillrect(left_col, dy, LCD_WIDTH - left_col, pf->height); rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); } rb->lcd_putsxy(dx, dy, utf8_buffer); rb->lcd_set_drawmode(DRMODE_SOLID); #else { rb->lcd_puts(left_col, i, BOOKMARK_ICON); } rb->lcd_puts(left_col+1, i, utf8_buffer); #endif } if (i == 0) next_line_ptr = line_end; } next_screen_ptr = line_end; if (BUFFER_OOB(next_screen_ptr)) next_screen_ptr = NULL; #ifdef HAVE_LCD_BITMAP next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr; if (prefs.need_scrollbar) viewer_scrollbar(); #else next_screen_to_draw_ptr = next_screen_ptr; #endif #ifdef HAVE_LCD_BITMAP /* show header */ viewer_show_header(); /* show footer */ viewer_show_footer(); #endif if (col != -1) rb->lcd_update(); } static void viewer_top(void) { /* Read top of file into buffer and point screen pointer to top */ if (file_pos != 0) { rb->splash(0, "Loading..."); file_pos = 0; buffer_end = BUFFER_END(); /* Update whenever file_pos changes */ fill_buffer(0, buffer, buffer_size); } screen_top_ptr = buffer; cpage = 1; cline = 1; } static void viewer_bottom(void) { unsigned char *line_begin; unsigned char *line_end; rb->splash(0, "Loading..."); if (last_screen_top_ptr) { cpage = lpage; cline = 1; screen_top_ptr = last_screen_top_ptr; file_pos = last_file_pos; fill_buffer(file_pos, buffer, buffer_size); buffer_end = BUFFER_END(); return; } line_end = screen_top_ptr; while (!BUFFER_EOF() || !BUFFER_OOB(line_end)) { get_next_line_position(&line_begin, &line_end, NULL); if (line_end == NULL) break; increment_current_line(); if (cline == 1) screen_top_ptr = line_end; } lpage = cpage; cline = 1; last_screen_top_ptr = screen_top_ptr; last_file_pos = file_pos; buffer_end = BUFFER_END(); } #ifdef HAVE_LCD_BITMAP static void init_need_scrollbar(void) { /* Call viewer_draw in quiet mode to initialize next_screen_ptr, and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */ viewer_draw(-1); prefs.need_scrollbar = NEED_SCROLLBAR(); draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns; par_indent_spaces = draw_columns/(5*glyph_width(' ')); calc_max_width(); } static void init_header_and_footer(void) { header_height = 0; footer_height = 0; if (rb->global_settings->statusbar == STATUSBAR_TOP) { if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH) header_height = STATUSBAR_HEIGHT; if (prefs.footer_mode == FT_SBAR) prefs.footer_mode = FT_NONE; else if (prefs.footer_mode == FT_BOTH) prefs.footer_mode = FT_PAGE; } else if (rb->global_settings->statusbar == STATUSBAR_BOTTOM) { if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH) footer_height = STATUSBAR_HEIGHT; if (prefs.header_mode == HD_SBAR) prefs.header_mode = HD_NONE; else if (prefs.header_mode == HD_BOTH) prefs.header_mode = HD_PATH; } else /* STATUSBAR_OFF || STATUSBAR_CUSTOM */ { if (prefs.header_mode == HD_SBAR) prefs.header_mode = HD_NONE; else if (prefs.header_mode == HD_BOTH) prefs.header_mode = HD_PATH; if (prefs.footer_mode == FT_SBAR) prefs.footer_mode = FT_NONE; else if (prefs.footer_mode == FT_BOTH) prefs.footer_mode = FT_PAGE; } if (prefs.header_mode == HD_NONE || prefs.header_mode == HD_PATH || prefs.footer_mode == FT_NONE || prefs.footer_mode == FT_PAGE) rb->gui_syncstatusbar_draw(rb->statusbars, false); if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH) header_height += pf->height; if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH) footer_height += pf->height; display_lines = (LCD_HEIGHT - header_height - footer_height) / pf->height; lpage = 0; last_file_pos = 0; last_screen_top_ptr = NULL; } static bool change_font(unsigned char *font) { unsigned char buf[MAX_PATH]; if (font == NULL || *font == '\0') return false; rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font); if (rb->font_load(NULL, buf) < 0) { rb->splash(HZ/2, "Font load failed."); return false; } return true; } #endif static bool viewer_init(void) { #ifdef HAVE_LCD_BITMAP /* initialize fonts */ pf = rb->font_get(FONT_UI); if (pf == NULL) return false; draw_columns = display_columns = LCD_WIDTH; #else /* REAL fixed pitch :) all chars use up 1 cell */ display_lines = 2; draw_columns = display_columns = 11; par_indent_spaces = 2; #endif fd = rb->open(file_name, O_RDONLY); if (fd < 0) return false; /* Init mac_text value used in processing buffer */ mac_text = false; return true; } /* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8, * then file size decreases only BOM_SIZE. */ static void get_filesize(void) { file_size = rb->filesize(fd); if (file_size == -1) return; if (prefs.encoding == UTF_8 && is_bom) file_size -= BOM_SIZE; } static int bm_comp(const void *a, const void *b) { struct bookmark_info *pa; struct bookmark_info *pb; pa = (struct bookmark_info*)a; pb = (struct bookmark_info*)b; if (pa->page != pb->page) return pa->page - pb->page; return pa->line - pb->line; } static void viewer_add_bookmark(void) { if (bookmark_count >= MAX_BOOKMARKS-1) return; bookmarks[bookmark_count].file_position = file_pos + screen_top_ptr - buffer; bookmarks[bookmark_count].page = cpage; bookmarks[bookmark_count].line = cline; bookmarks[bookmark_count].flag = BOOKMARK_USER; bookmark_count++; } static int viewer_add_last_read_bookmark(void) { int i; i = viewer_find_bookmark(cpage, cline); if (i >= 0) bookmarks[i].flag |= BOOKMARK_LAST; else { viewer_add_bookmark(); i = bookmark_count-1; bookmarks[i].flag = BOOKMARK_LAST; } return i; } static void viewer_remove_bookmark(int i) { int j; if (i < 0 || i >= bookmark_count) return; for (j = i+1; j < bookmark_count; j++) rb->memcpy(&bookmarks[j-1], &bookmarks[j], sizeof(struct bookmark_info)); bookmark_count--; } static void viewer_remove_last_read_bookmark(void) { int i, j; for (i = 0; i < bookmark_count; i++) { if (bookmarks[i].flag & BOOKMARK_LAST) { if (bookmarks[i].flag == BOOKMARK_LAST) { for (j = i+1; j < bookmark_count; j++) rb->memcpy(&bookmarks[j-1], &bookmarks[j], sizeof(struct bookmark_info)); bookmark_count--; } else bookmarks[i].flag = BOOKMARK_USER; break; } } } static int viewer_get_last_read_bookmark(void) { int i; for (i = 0; i < bookmark_count; i++) { if (bookmarks[i].flag & BOOKMARK_LAST) return i; } return -1; } static void viewer_select_bookmark(int initval) { int i; int ipage = 0; int iline = 0; int screen_pos; int screen_top; int selected = -1; struct opt_items items[bookmark_count]; unsigned char names[bookmark_count][38]; if (initval >= 0 && initval < bookmark_count) { ipage = bookmarks[initval].page; iline = bookmarks[initval].line; } rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info), bm_comp); for (i = 0; i < bookmark_count; i++) { rb->snprintf(names[i], sizeof(names[0]), #if CONFIG_KEYPAD != PLAYER_PAD "%sPage: %d Line: %d", #else "%sP:%d L:%d", #endif (bookmarks[i].flag&BOOKMARK_LAST)? "*":" ", bookmarks[i].page, bookmarks[i].line); items[i].string = names[i]; items[i].voice_id = -1; if (selected < 0 && bookmarks[i].page == ipage && bookmarks[i].line == iline) selected = i; } rb->set_option("Select bookmark", &selected, INT, items, sizeof(items) / sizeof(items[0]), NULL); if (selected < 0 || selected >= bookmark_count) { if (initval < 0 || (selected = viewer_get_last_read_bookmark()) < 0) { if (initval < 0) rb->splash(HZ, "Start the first page."); file_pos = 0; screen_top_ptr = buffer; cpage = 1; cline = 1; buffer_end = BUFFER_END(); return; } } screen_pos = bookmarks[selected].file_position; screen_top = screen_pos % buffer_size; file_pos = screen_pos - screen_top; screen_top_ptr = buffer + screen_top; cpage = bookmarks[selected].page; cline = bookmarks[selected].line; buffer_end = BUFFER_END(); } static void viewer_default_preferences(void) { prefs.word_mode = WRAP; prefs.line_mode = NORMAL; prefs.view_mode = NARROW; prefs.alignment = LEFT; prefs.scroll_mode = PAGE; prefs.page_mode = NO_OVERLAP; prefs.scrollbar_mode = SB_OFF; rb->memset(prefs.font, 0, MAX_PATH); #ifdef HAVE_LCD_BITMAP prefs.header_mode = HD_BOTH; prefs.footer_mode = FT_BOTH; rb->snprintf(prefs.font, MAX_PATH, "%s", rb->global_settings->font_file); #else prefs.header_mode = HD_NONE; prefs.footer_mode = FT_NONE; #endif prefs.autoscroll_speed = 1; /* Set codepage to system default */ prefs.encoding = rb->global_settings->default_codepage; } static bool viewer_read_preferences(int pfd, int version, struct preferences *prf) { unsigned char buf[PREFERENCES_SIZE]; unsigned char *p = buf; int read_size = PREFERENCES_SIZE; if (version == 0) read_size--; if (rb->read(pfd, buf, read_size) != read_size) return false; prf->word_mode = *p++; prf->line_mode = *p++; prf->view_mode = *p++; if (version > 0) prf->alignment = *p++; else prf->alignment = LEFT; prf->encoding = *p++; prf->scrollbar_mode = *p++; prf->need_scrollbar = *p++; prf->page_mode = *p++; prf->header_mode = *p++; prf->footer_mode = *p++; prf->scroll_mode = *p++; prf->autoscroll_speed = *p++; rb->memcpy(prf->font, p, MAX_PATH); return true; } static bool viewer_write_preferences(int pfd, const struct preferences *prf) { unsigned char buf[PREFERENCES_SIZE]; unsigned char *p = buf; *p++ = prf->word_mode; *p++ = prf->line_mode; *p++ = prf->view_mode; *p++ = prf->alignment; *p++ = prf->encoding; *p++ = prf->scrollbar_mode; *p++ = prf->need_scrollbar; *p++ = prf->page_mode; *p++ = prf->header_mode; *p++ = prf->footer_mode; *p++ = prf->scroll_mode; *p++ = prf->autoscroll_speed; rb->memcpy(p, prf->font, MAX_PATH); return (rb->write(pfd, buf, sizeof(buf)) == sizeof(buf)); } static bool viewer_read_bookmark_info(int bfd, struct bookmark_info *b) { unsigned char buf[BOOKMARK_SIZE]; if (rb->read(bfd, buf, sizeof(buf)) != sizeof(buf)) return false; b->file_position = (buf[0] << 24)|(buf[1] << 16)|(buf[2] << 8)|buf[3]; b->page = (buf[4] << 8)|buf[5]; b->line = buf[6]; b->flag = buf[7]; return true; } static bool viewer_read_bookmark_infos(int bfd) { unsigned char c; int i; if (rb->read(bfd, &c, 1) != 1) { bookmark_count = 0; return false; } bookmark_count = c; if (bookmark_count > MAX_BOOKMARKS) bookmark_count = MAX_BOOKMARKS; for (i = 0; i < bookmark_count; i++) { if (!viewer_read_bookmark_info(bfd, &bookmarks[i])) { bookmark_count = i; return false; } } return true; } static bool viewer_write_bookmark_info(int bfd, struct bookmark_info *b) { unsigned char buf[BOOKMARK_SIZE]; unsigned char *p = buf; unsigned long ul; ul = b->file_position; *p++ = ul >> 24; *p++ = ul >> 16; *p++ = ul >> 8; *p++ = ul; ul = b->page; *p++ = ul >> 8; *p++ = ul; *p++ = b->line; *p = b->flag; return (rb->write(bfd, buf, sizeof(buf)) == sizeof(buf)); } static bool viewer_write_bookmark_infos(int bfd) { unsigned char c = bookmark_count; int i; if (rb->write(bfd, &c, 1) != 1) return false; for (i = 0; i < bookmark_count; i++) { if (!viewer_write_bookmark_info(bfd, &bookmarks[i])) return false; } return true; } static bool viewer_load_global_settings(void) { unsigned buf[GLOBAL_SETTINGS_H_SIZE]; int sfd = rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY); int version; bool res = false; if (sfd < 0) return false; if ((rb->read(sfd, buf, GLOBAL_SETTINGS_H_SIZE) == GLOBAL_SETTINGS_H_SIZE) || (rb->memcmp(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1) == 0)) { version = buf[GLOBAL_SETTINGS_H_SIZE - 1] - GLOBAL_SETTINGS_FIRST_VERSION; res = viewer_read_preferences(sfd, version, &prefs); } rb->close(sfd); return res; } static bool viewer_save_global_settings(void) { int sfd = rb->open(GLOBAL_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC); unsigned char buf[GLOBAL_SETTINGS_H_SIZE]; if (sfd < 0) return false; rb->memcpy(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1); buf[GLOBAL_SETTINGS_H_SIZE - 1] = GLOBAL_SETTINGS_VERSION; if (rb->write(sfd, buf, GLOBAL_SETTINGS_H_SIZE) != GLOBAL_SETTINGS_H_SIZE || !viewer_write_preferences(sfd, &prefs)) { rb->close(sfd); rb->remove(GLOBAL_SETTINGS_TMP_FILE); return false; } rb->close(sfd); rb->remove(GLOBAL_SETTINGS_FILE); rb->rename(GLOBAL_SETTINGS_TMP_FILE, GLOBAL_SETTINGS_FILE); return true; } static bool viewer_convert_settings(int sfd, int dfd, int old_ver) { struct preferences new_prefs; off_t old_pos; off_t new_pos; unsigned char buf[MAX_PATH + 2]; int settings_size; rb->read(sfd, buf, MAX_PATH + 2); rb->write(dfd, buf, MAX_PATH + 2); settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1]; old_pos = rb->lseek(sfd, 0, SEEK_CUR); new_pos = rb->lseek(dfd, 0, SEEK_CUR); /* * when the settings size != preferences size + bookmarks size, * settings data are considered to be old version. */ if (old_ver > 0 && ((settings_size - PREFERENCES_SIZE) % 8) == 0) old_ver = 0; if (!viewer_read_preferences(sfd, old_ver, &new_prefs)) return false; if (!viewer_write_preferences(dfd, &new_prefs)) return false; settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos); if (settings_size > 0) { rb->read(sfd, buf, settings_size); rb->write(dfd, buf, settings_size); } settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos; buf[0] = settings_size >> 8; buf[1] = settings_size; rb->lseek(dfd, new_pos - 2, SEEK_SET); rb->write(dfd, buf, 2); rb->lseek(dfd, settings_size, SEEK_CUR); return true; } static bool viewer_convert_settings_file(void) { unsigned char buf[SETTINGS_H_SIZE+2]; int sfd; int tfd; int i; int fcount; int version; bool res = true; if ((sfd = rb->open(SETTINGS_FILE, O_RDONLY)) < 0) return false; if ((tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC)) < 0) { rb->close(sfd); return false; } rb->read(sfd, buf, SETTINGS_H_SIZE + 2); version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION; fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE + 1]; buf[SETTINGS_H_SIZE - 1] = SETTINGS_VERSION; rb->write(tfd, buf, SETTINGS_H_SIZE + 2); for (i = 0; i < fcount; i++) { if (!viewer_convert_settings(sfd, tfd, version)) { res = false; break; } } rb->close(sfd); rb->close(tfd); if (!res) rb->remove(SETTINGS_TMP_FILE); else { rb->remove(SETTINGS_FILE); rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE); } return res; } static bool viewer_load_settings(void) { unsigned char buf[MAX_PATH+2]; unsigned int fcount; unsigned int i; bool res = false; int sfd; unsigned int size; int version; sfd = rb->open(SETTINGS_FILE, O_RDONLY); if (sfd < 0) goto read_end; if ((rb->read(sfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) || rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1)) { /* illegal setting file */ rb->close(sfd); if (rb->file_exists(SETTINGS_FILE)) rb->remove(SETTINGS_FILE); goto read_end; } if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION) { rb->close(sfd); if (!viewer_convert_settings_file()) goto read_end; return viewer_load_settings(); } version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION; fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1]; for (i = 0; i < fcount; i++) { if (rb->read(sfd, buf, MAX_PATH+2) != MAX_PATH+2) break; size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1]; /* * when the settings size != preferences size + bookmarks size, * the settings file converts to the newer. */ if (version > 0 && ((size - PREFERENCES_SIZE) % 8) == 0) { rb->close(sfd); if (!viewer_convert_settings_file()) break; return viewer_load_settings(); } if (rb->strcmp(buf, file_name)) { if (rb->lseek(sfd, size, SEEK_CUR) < 0) break; continue; } if (!viewer_read_preferences(sfd, version, &prefs)) break; res = viewer_read_bookmark_infos(sfd); break; } rb->close(sfd); read_end: if (!res) { /* load global settings */ if (!viewer_load_global_settings()) viewer_default_preferences(); file_pos = 0; screen_top_ptr = buffer; cpage = 1; cline = 1; bookmark_count = 0; } rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences)); calc_max_width(); if (bookmark_count > 1) viewer_select_bookmark(-1); else if (bookmark_count == 1) { int screen_pos; int screen_top; screen_pos = bookmarks[0].file_position; screen_top = screen_pos % buffer_size; file_pos = screen_pos - screen_top; screen_top_ptr = buffer + screen_top; cpage = bookmarks[0].page; cline = bookmarks[0].line; } viewer_remove_last_read_bookmark(); check_bom(); get_filesize(); buffer_end = BUFFER_END(); /* Update whenever file_pos changes */ if (BUFFER_OOB(screen_top_ptr)) screen_top_ptr = buffer; fill_buffer(file_pos, buffer, buffer_size); if (prefs.scroll_mode == PAGE && cline > 1) viewer_scroll_to_top_line(); /* remember the current position */ start_position = file_pos + screen_top_ptr - buffer; #ifdef HAVE_LCD_BITMAP /* load prefs font if it is different than the global settings font */ if (rb->strcmp(prefs.font, rb->global_settings->font_file)) { if (!change_font(prefs.font)) { /* fallback by resetting prefs font to the global settings font */ rb->memset(prefs.font, 0, MAX_PATH); rb->snprintf(prefs.font, MAX_PATH, "%s", rb->global_settings->font_file); if (!change_font(prefs.font)) return false; } } init_need_scrollbar(); init_header_and_footer(); #endif return true; } static bool copy_bookmark_file(int sfd, int dfd, off_t start, off_t size) { off_t rsize; if (rb->lseek(sfd, start, SEEK_SET) < 0) return false; while (size > 0) { if (size > buffer_size) rsize = buffer_size; else rsize = size; size -= rsize; if (rb->read(sfd, buffer, rsize) != rsize || rb->write(dfd, buffer, rsize) != rsize) return false; } return true; } static bool viewer_save_settings(void) { unsigned char buf[MAX_PATH+2]; unsigned int fcount = 0; unsigned int i; int idx; int ofd; int tfd; off_t first_copy_size = 0; off_t second_copy_start_pos = 0; off_t size; /* add reading page to bookmarks */ idx = viewer_find_bookmark(cpage, cline); if (idx >= 0) bookmarks[idx].flag |= BOOKMARK_LAST; else { viewer_add_bookmark(); bookmarks[bookmark_count-1].flag = BOOKMARK_LAST; } tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC); if (tfd < 0) return false; ofd = rb->open(SETTINGS_FILE, O_RDWR); if (ofd >= 0) { if ((rb->read(ofd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) || rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1)) { rb->close(ofd); goto save_err; } if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION) { rb->close(ofd); if (!viewer_convert_settings_file()) goto save_err; viewer_save_settings(); } fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1]; for (i = 0; i < fcount; i++) { if (rb->read(ofd, buf, MAX_PATH+2) != MAX_PATH+2) { rb->close(ofd); goto save_err; } size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1]; if (rb->strcmp(buf, file_name)) { if (rb->lseek(ofd, size, SEEK_CUR) < 0) { rb->close(ofd); goto save_err; } } else { first_copy_size = rb->lseek(ofd, 0, SEEK_CUR); if (first_copy_size < 0) { rb->close(ofd); goto save_err; } second_copy_start_pos = first_copy_size + size; first_copy_size -= MAX_PATH+2; fcount--; break; } } if (first_copy_size == 0) first_copy_size = rb->filesize(ofd); if (!copy_bookmark_file(ofd, tfd, 0, first_copy_size)) { rb->close(ofd); goto save_err; } if (second_copy_start_pos > 0) { if (!copy_bookmark_file(ofd, tfd, second_copy_start_pos, rb->filesize(ofd) - second_copy_start_pos)) { rb->close(ofd); goto save_err; } } rb->close(ofd); } else { rb->memcpy(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1); buf[SETTINGS_H_SIZE-1] = SETTINGS_VERSION; buf[SETTINGS_H_SIZE ] = 0; buf[SETTINGS_H_SIZE+1] = 0; if (rb->write(tfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) goto save_err; } /* copy to current read file's bookmarks */ rb->memset(buf, 0, MAX_PATH); rb->snprintf(buf, MAX_PATH, "%s", file_name); size = PREFERENCES_SIZE + bookmark_count * BOOKMARK_SIZE + 1; buf[MAX_PATH] = size >> 8; buf[MAX_PATH+1] = size; if (rb->write(tfd, buf, MAX_PATH+2) != MAX_PATH+2) goto save_err; if (!viewer_write_preferences(tfd, &prefs)) goto save_err; if (!viewer_write_bookmark_infos(tfd)) goto save_err; if (rb->lseek(tfd, SETTINGS_H_SIZE, SEEK_SET) < 0) goto save_err; fcount++; buf[0] = fcount >> 8; buf[1] = fcount; if (rb->write(tfd, buf, 2) != 2) goto save_err; rb->close(tfd); rb->remove(SETTINGS_FILE); rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE); return true; save_err: rb->close(tfd); rb->remove(SETTINGS_TMP_FILE); return false; } static void viewer_exit(void *parameter) { (void)parameter; /* save preference and bookmarks */ if (!viewer_save_settings()) rb->splash(HZ, "Can't save preference and bookmarks."); rb->close(fd); #ifdef HAVE_LCD_BITMAP if (rb->strcmp(prefs.font, rb->global_settings->font_file)) change_font(rb->global_settings->font_file); #endif } static void calc_page(void) { int i; unsigned char *line_begin; unsigned char *line_end; off_t sfp; unsigned char *sstp; rb->splash(0, "Calculating page/line number..."); /* add reading page to bookmarks */ viewer_add_last_read_bookmark(); rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info), bm_comp); cpage = 1; cline = 1; file_pos = 0; screen_top_ptr = buffer; buffer_end = BUFFER_END(); fill_buffer(file_pos, buffer, buffer_size); line_end = line_begin = buffer; /* update page and line of all bookmark */ for (i = 0; i < bookmark_count; i++) { sfp = bookmarks[i].file_position; sstp = buffer; while ((line_begin > sstp || sstp >= line_end) || (file_pos > sfp || sfp >= file_pos + BUFFER_END() - buffer)) { get_next_line_position(&line_begin, &line_end, NULL); if (line_end == NULL) break; next_line_ptr = line_end; if (sstp == buffer && file_pos <= sfp && sfp < file_pos + BUFFER_END() - buffer) sstp = sfp - file_pos + buffer; increment_current_line(); } decrement_current_line(); bookmarks[i].page = cpage; bookmarks[i].line = cline; bookmarks[i].file_position = file_pos + (line_begin - buffer); increment_current_line(); } /* remove reading page's bookmark */ for (i = 0; i < bookmark_count; i++) { if (bookmarks[i].flag & BOOKMARK_LAST) { int screen_pos; int screen_top; screen_pos = bookmarks[i].file_position; screen_top = screen_pos % buffer_size; file_pos = screen_pos - screen_top; screen_top_ptr = buffer + screen_top; cpage = bookmarks[i].page; cline = bookmarks[i].line; bookmarks[i].flag ^= BOOKMARK_LAST; buffer_end = BUFFER_END(); fill_buffer(file_pos, buffer, buffer_size); if (bookmarks[i].flag == 0) viewer_remove_bookmark(i); if (prefs.scroll_mode == PAGE && cline > 1) viewer_scroll_to_top_line(); break; } } } static int col_limit(int col) { if (col < 0) col = 0; else if (col >= max_width - draw_columns) col = max_width - draw_columns; return col; } /* settings helper functions */ static bool encoding_setting(void) { static struct opt_items names[NUM_CODEPAGES]; int idx; bool res; enum codepages oldenc = prefs.encoding; for (idx = 0; idx < NUM_CODEPAGES; idx++) { names[idx].string = rb->get_codepage_name(idx); names[idx].voice_id = -1; } res = rb->set_option("Encoding", &prefs.encoding, INT, names, sizeof(names) / sizeof(names[0]), NULL); /* When prefs.encoding changes into UTF-8 or changes from UTF-8, * filesize (file_size) might change. * In addition, if prefs.encoding is UTF-8, then BOM does not read. */ if (oldenc != prefs.encoding && (oldenc == UTF_8 || prefs.encoding == UTF_8)) { check_bom(); get_filesize(); fill_buffer(file_pos, buffer, buffer_size); } return res; } static bool word_wrap_setting(void) { static const struct opt_items names[] = { {"On", -1}, {"Off (Chop Words)", -1}, }; return rb->set_option("Word Wrap", &prefs.word_mode, INT, names, 2, NULL); } static bool line_mode_setting(void) { static const struct opt_items names[] = { {"Normal", -1}, {"Join Lines", -1}, {"Expand Lines", -1}, #ifdef HAVE_LCD_BITMAP {"Reflow Lines", -1}, #endif }; return rb->set_option("Line Mode", &prefs.line_mode, INT, names, sizeof(names) / sizeof(names[0]), NULL); } static bool view_mode_setting(void) { static const struct opt_items names[] = { {"No (Narrow)", -1}, {"Yes", -1}, }; bool ret; ret = rb->set_option("Wide View", &prefs.view_mode, INT, names , 2, NULL); if (prefs.view_mode == NARROW) col = 0; calc_max_width(); return ret; } static bool scroll_mode_setting(void) { static const struct opt_items names[] = { {"Scroll by Page", -1}, {"Scroll by Line", -1}, }; return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT, names, 2, NULL); } #ifdef HAVE_LCD_BITMAP static bool page_mode_setting(void) { static const struct opt_items names[] = { {"No", -1}, {"Yes", -1}, }; return rb->set_option("Overlap Pages", &prefs.page_mode, INT, names, 2, NULL); } static bool scrollbar_setting(void) { static const struct opt_items names[] = { {"Off", -1}, {"On", -1} }; return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT, names, 2, NULL); } static bool header_setting(void) { int len = (rb->global_settings->statusbar == STATUSBAR_TOP)? 4 : 2; struct opt_items names[len]; names[0].string = "None"; names[0].voice_id = -1; names[1].string = "File path"; names[1].voice_id = -1; if (rb->global_settings->statusbar == STATUSBAR_TOP) { names[2].string = "Status bar"; names[2].voice_id = -1; names[3].string = "Both"; names[3].voice_id = -1; } return rb->set_option("Show Header", &prefs.header_mode, INT, names, len, NULL); } static bool footer_setting(void) { int len = (rb->global_settings->statusbar == STATUSBAR_BOTTOM)? 4 : 2; struct opt_items names[len]; names[0].string = "None"; names[0].voice_id = -1; names[1].string = "Page Num"; names[1].voice_id = -1; if (rb->global_settings->statusbar == STATUSBAR_BOTTOM) { names[2].string = "Status bar"; names[2].voice_id = -1; names[3].string = "Both"; names[3].voice_id = -1; } return rb->set_option("Show Footer", &prefs.footer_mode, INT, names, len, NULL); } static int font_comp(const void *a, const void *b) { struct opt_items *pa; struct opt_items *pb; pa = (struct opt_items *)a; pb = (struct opt_items *)b; return rb->strcmp(pa->string, pb->string); } static bool font_setting(void) { int count = 0; DIR *dir; struct dirent *entry; int i = 0; int len; int new_font = 0; int old_font; bool res; int size = 0; dir = rb->opendir(FONT_DIR); if (!dir) { rb->splash(HZ/2, "Font dir is not accessible"); return false; } while (1) { entry = rb->readdir(dir); if (entry == NULL) break; len = rb->strlen(entry->d_name); if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt")) continue; size += len-3; count++; } rb->closedir(dir); struct opt_items names[count]; unsigned char font_names[size]; unsigned char *p = font_names; dir = rb->opendir(FONT_DIR); if (!dir) { rb->splash(HZ/2, "Font dir is not accessible"); return false; } while (1) { entry = rb->readdir(dir); if (entry == NULL) break; len = rb->strlen(entry->d_name); if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt")) continue; rb->snprintf(p, len-3, "%s", entry->d_name); names[i].string = p; names[i].voice_id = -1; p += len-3; i++; if (i >= count) break; } rb->closedir(dir); rb->qsort(names, count, sizeof(struct opt_items), font_comp); for (i = 0; i < count; i++) { if (!rb->strcmp(names[i].string, prefs.font)) { new_font = i; break; } } old_font = new_font; res = rb->set_option("Select Font", &new_font, INT, names, count, NULL); if (new_font != old_font) { /* load selected font */ if (!change_font((unsigned char *)names[new_font].string)) { /* revert by re-loading the preferences font */ change_font(prefs.font); return false; } rb->memset(prefs.font, 0, MAX_PATH); rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string); } return res; } static bool alignment_setting(void) { static const struct opt_items names[] = { {"Left", -1}, {"Right", -1}, }; return rb->set_option("Alignment", &prefs.alignment, INT, names , 2, NULL); } #endif static bool autoscroll_speed_setting(void) { return rb->set_int("Auto-scroll Speed", "", UNIT_INT, &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL); } MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting, NULL, NULL, Icon_NOICON); #ifdef HAVE_LCD_BITMAP MENUITEM_FUNCTION(alignment_item, 0, "Alignment", alignment_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(header_item, 0, "Show Header", header_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(footer_item, 0, "Show Footer", footer_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(font_item, 0, "Font", font_setting, NULL, NULL, Icon_NOICON); #endif MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting, NULL, NULL, Icon_NOICON); MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed", autoscroll_speed_setting, NULL, NULL, Icon_NOICON); MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON, &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item, #ifdef HAVE_LCD_BITMAP &alignment_item, &scrollbar_item, &page_mode_item, &header_item, &footer_item, &font_item, #endif &scroll_mode_item, &autoscroll_speed_item); static bool viewer_options_menu(bool is_global) { bool result; struct preferences tmp_prefs; rb->memcpy(&tmp_prefs, &prefs, sizeof(struct preferences)); result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB); if (!is_global && rb->memcmp(&tmp_prefs, &prefs, sizeof(struct preferences))) { /* Show-scrollbar mode for current view-width mode */ #ifdef HAVE_LCD_BITMAP init_need_scrollbar(); init_header_and_footer(); #endif calc_page(); } return result; } static void viewer_menu(void) { int result; MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL, "Return", "Viewer Options", "Show Playback Menu", "Select Bookmark", "Global Settings", "Quit"); result = rb->do_menu(&menu, NULL, NULL, false); switch (result) { case 0: /* return */ break; case 1: /* change settings */ done = viewer_options_menu(false); break; case 2: /* playback control */ playback_control(NULL); break; case 3: /* select bookmark */ viewer_select_bookmark(viewer_add_last_read_bookmark()); viewer_remove_last_read_bookmark(); fill_buffer(file_pos, buffer, buffer_size); if (prefs.scroll_mode == PAGE && cline > 1) viewer_scroll_to_top_line(); break; case 4: /* change global settings */ { struct preferences orig_prefs; rb->memcpy(&orig_prefs, &prefs, sizeof(struct preferences)); if (!viewer_load_global_settings()) viewer_default_preferences(); done = viewer_options_menu(true); viewer_save_global_settings(); rb->memcpy(&prefs, &orig_prefs, sizeof(struct preferences)); } break; case 5: /* quit */ viewer_exit(NULL); done = true; break; } viewer_draw(col); } enum plugin_status plugin_start(const void* file) { int button, i, ok; int lastbutton = BUTTON_NONE; bool autoscroll = false; long old_tick; old_tick = *rb->current_tick; /* get the plugin buffer */ buffer = rb->plugin_get_buffer((size_t *)&buffer_size); if (buffer_size == 0) { rb->splash(HZ, "buffer does not allocate !!"); return PLUGIN_ERROR; } block_size = buffer_size / 3; buffer_size = 3 * block_size; if (!file) return PLUGIN_ERROR; file_name = file; ok = viewer_init(); if (!ok) { rb->splash(HZ, "Error opening file."); return PLUGIN_ERROR; } if (!viewer_load_settings()) /* load the preferences and bookmark */ return PLUGIN_ERROR; #if LCD_DEPTH > 1 rb->lcd_set_backdrop(NULL); #endif viewer_draw(col); while (!done) { if(autoscroll) { if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10)) { viewer_scroll_down(true); viewer_draw(col); old_tick = *rb->current_tick; } } button = rb->button_get_w_tmo(HZ/10); if (prefs.view_mode != WIDE) { /* when not in wide view mode, the SCREEN_LEFT and SCREEN_RIGHT buttons jump to the beginning and end of the file. To stop users doing this by accident, replace non-held occurrences with page up/down instead. */ if (button == VIEWER_SCREEN_LEFT) button = VIEWER_PAGE_UP; else if (button == VIEWER_SCREEN_RIGHT) button = VIEWER_PAGE_DOWN; } switch (button) { case VIEWER_MENU: #ifdef VIEWER_MENU2 case VIEWER_MENU2: #endif viewer_menu(); break; case VIEWER_AUTOSCROLL: #ifdef VIEWER_AUTOSCROLL_PRE if (lastbutton != VIEWER_AUTOSCROLL_PRE) break; #endif autoscroll = !autoscroll; break; case VIEWER_PAGE_UP: case VIEWER_PAGE_UP | BUTTON_REPEAT: #ifdef VIEWER_PAGE_UP2 case VIEWER_PAGE_UP2: case VIEWER_PAGE_UP2 | BUTTON_REPEAT: #endif if (prefs.scroll_mode == PAGE) { /* Page up */ #ifdef HAVE_LCD_BITMAP for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++) #else for (i = 0; i < display_lines; i++) #endif viewer_scroll_up(); } else viewer_scroll_up(); old_tick = *rb->current_tick; viewer_draw(col); break; case VIEWER_PAGE_DOWN: case VIEWER_PAGE_DOWN | BUTTON_REPEAT: #ifdef VIEWER_PAGE_DOWN2 case VIEWER_PAGE_DOWN2: case VIEWER_PAGE_DOWN2 | BUTTON_REPEAT: #endif if (prefs.scroll_mode == PAGE) { /* Page down */ if (next_screen_ptr != NULL) { screen_top_ptr = next_screen_to_draw_ptr; if (cpage < MAX_PAGE) cpage++; } } else viewer_scroll_down(autoscroll); old_tick = *rb->current_tick; viewer_draw(col); break; case VIEWER_SCREEN_LEFT: case VIEWER_SCREEN_LEFT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Screen left */ col = col_limit(col - draw_columns); } else { /* prefs.view_mode == NARROW */ /* Top of file */ viewer_top(); } viewer_draw(col); break; case VIEWER_SCREEN_RIGHT: case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Screen right */ col = col_limit(col + draw_columns); } else { /* prefs.view_mode == NARROW */ /* Bottom of file */ viewer_bottom(); } viewer_draw(col); break; #ifdef VIEWER_LINE_UP case VIEWER_LINE_UP: case VIEWER_LINE_UP | BUTTON_REPEAT: /* Scroll up one line */ viewer_scroll_up(); old_tick = *rb->current_tick; viewer_draw(col); break; case VIEWER_LINE_DOWN: case VIEWER_LINE_DOWN | BUTTON_REPEAT: /* Scroll down one line */ viewer_scroll_down(autoscroll); increment_current_line(); old_tick = *rb->current_tick; viewer_draw(col); break; #endif #ifdef VIEWER_COLUMN_LEFT case VIEWER_COLUMN_LEFT: case VIEWER_COLUMN_LEFT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Scroll left one column */ col = col_limit(col - glyph_width('o')); viewer_draw(col); } break; case VIEWER_COLUMN_RIGHT: case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Scroll right one column */ col = col_limit(col + glyph_width('o')); viewer_draw(col); } break; #endif #ifdef VIEWER_RC_QUIT case VIEWER_RC_QUIT: #endif case VIEWER_QUIT: #ifdef VIEWER_QUIT2 case VIEWER_QUIT2: #endif viewer_exit(NULL); done = true; break; case VIEWER_BOOKMARK: { int idx = viewer_find_bookmark(cpage, cline); if (idx < 0) { if (bookmark_count >= MAX_BOOKMARKS-1) rb->splash(HZ/2, "No more bookmarks"); else { viewer_add_bookmark(); rb->splash(HZ/2, "Bookmark added"); } } else { viewer_remove_bookmark(idx); rb->splash(HZ/2, "Bookmark removed"); } viewer_draw(col); } break; default: if (rb->default_event_handler_ex(button, viewer_exit, NULL) == SYS_USB_CONNECTED) return PLUGIN_USB_CONNECTED; break; } if (button != BUTTON_NONE) { lastbutton = button; rb->yield(); } } return PLUGIN_OK; }