diff options
author | Yoshihisa Uchida <uchida@rockbox.org> | 2010-03-17 11:17:56 +0000 |
---|---|---|
committer | Yoshihisa Uchida <uchida@rockbox.org> | 2010-03-17 11:17:56 +0000 |
commit | 4e8f200db93d9516d3ada7b4701488fe2621b0c7 (patch) | |
tree | 0e241db4052332d4bab198aca925b2028d850ca2 | |
parent | eec03f5f29e9d1d584420cdbcf5224c578b8366d (diff) |
text viewer plugin applies patches FS#8445, FS#9546, FS#9853, FS#9855, FS#9892, FS#9893, FS#9898, FS#9902, and FS#9990.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25233 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/plugins/viewer.c | 1601 |
1 files changed, 1398 insertions, 203 deletions
diff --git a/apps/plugins/viewer.c b/apps/plugins/viewer.c index 8dae52780e..0901be427b 100644 --- a/apps/plugins/viewer.c +++ b/apps/plugins/viewer.c @@ -24,22 +24,108 @@ PLUGIN_HEADER -#define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */ -#define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat" +/* 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 + * 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\x31" /* header="TVGS" version=1 */ +#define GLOBAL_SETTINGS_H_SIZE 5 + +/* 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 + * 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\x32" /* header="TVS" version=2 */ +#define SETTINGS_H_SIZE 4 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */ -#define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */ +#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 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */ -#define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */ -#define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */ +#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 + 2*(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 MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1) +#define BOOKMARK_LAST 1 +#define BOOKMARK_USER 2 + +#ifndef HAVE_LCD_BITMAP +#define BOOKMARK_ICON "\xee\x84\x81\x00" +#endif + +#define PREFERENCES_SIZE (11 + MAX_PATH) /* Out-Of-Bounds test for any pointer to data in the buffer */ #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end) @@ -77,6 +163,7 @@ PLUGIN_HEADER #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 @@ -90,6 +177,7 @@ PLUGIN_HEADER #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 @@ -101,6 +189,7 @@ PLUGIN_HEADER #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 @@ -111,6 +200,7 @@ PLUGIN_HEADER #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) || \ @@ -126,6 +216,7 @@ PLUGIN_HEADER #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 @@ -141,6 +232,7 @@ PLUGIN_HEADER #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 @@ -151,6 +243,7 @@ PLUGIN_HEADER #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 @@ -161,6 +254,7 @@ PLUGIN_HEADER #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 @@ -171,6 +265,7 @@ PLUGIN_HEADER #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 @@ -183,6 +278,7 @@ PLUGIN_HEADER #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 @@ -195,6 +291,7 @@ PLUGIN_HEADER #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 @@ -207,6 +304,7 @@ PLUGIN_HEADER #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 @@ -219,6 +317,7 @@ PLUGIN_HEADER #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 @@ -231,6 +330,7 @@ PLUGIN_HEADER #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 @@ -241,6 +341,7 @@ PLUGIN_HEADER #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 @@ -251,6 +352,7 @@ PLUGIN_HEADER #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 @@ -266,6 +368,7 @@ PLUGIN_HEADER #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 @@ -276,17 +379,19 @@ PLUGIN_HEADER #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_RC_REC +#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_REC +#define VIEWER_RC_QUIT BUTTON_RC_REC +#define VIEWER_BOOKMARK BUTTON_RC_PLAY /* Cowon D2 keys */ #elif CONFIG_KEYPAD == COWON_D2_PAD @@ -294,6 +399,7 @@ PLUGIN_HEADER #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 @@ -304,6 +410,7 @@ PLUGIN_HEADER #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 @@ -314,6 +421,7 @@ PLUGIN_HEADER #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 @@ -324,6 +432,7 @@ PLUGIN_HEADER #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 @@ -334,15 +443,18 @@ PLUGIN_HEADER #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 /* Onda VX777 keys */ #elif CONFIG_KEYPAD == ONDAVX777_PAD #define VIEWER_QUIT BUTTON_POWER +#define VIEWER_BOOKMARK BUTTON_RIGHT /* SAMSUNG YH-820 / YH-920 / YH-925 keys */ #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD @@ -353,6 +465,7 @@ PLUGIN_HEADER #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 @@ -365,6 +478,7 @@ PLUGIN_HEADER #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! @@ -403,15 +517,11 @@ PLUGIN_HEADER #endif /* stuff for the bookmarking */ -struct bookmarked_file_info { +struct bookmark_info { long file_position; - int top_ptr_pos; - char filename[MAX_PATH]; -}; - -struct bookmark_file_data { - signed int bookmarked_files_count; - struct bookmarked_file_info bookmarks[]; + int page; + int line; + unsigned char flag; }; struct preferences { @@ -434,7 +544,6 @@ struct preferences { enum codepages encoding; -#ifdef HAVE_LCD_BITMAP enum { SB_OFF=0, SB_ON, @@ -445,7 +554,20 @@ struct preferences { NO_OVERLAP=0, OVERLAP, } page_mode; -#endif /* HAVE_LCD_BITMAP */ + + 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, @@ -453,6 +575,13 @@ struct preferences { } scroll_mode; int autoscroll_speed; + + unsigned char font[MAX_PATH]; +}; + +enum { + VIEWER_FONT_MENU = 0, + VIEWER_FONT_TEXT, }; struct preferences prefs; @@ -460,6 +589,7 @@ 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 */ @@ -471,18 +601,34 @@ 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_line_len; +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; -int glyph_width(int ch) +static int glyph_width(int ch) { if (ch == 0) ch = ' '; @@ -494,7 +640,7 @@ int glyph_width(int ch) #endif } -unsigned char* get_ucs(const unsigned char* str, unsigned short* ch) +static unsigned char* get_ucs(const unsigned char* str, unsigned short* ch) { unsigned char utf8_tmp[6]; int count; @@ -515,12 +661,60 @@ unsigned char* get_ucs(const unsigned char* str, unsigned short* ch) return (unsigned char*)str+1; } +static unsigned char *decode2utf8(const unsigned char *src, unsigned char *dst, + int skip_width, int disp_width) +{ + unsigned short ch; + const unsigned char *oldstr; + const unsigned char *str = src; + unsigned char *utf8 = dst; + int width = 0; + + while (*str != '\0') + { + oldstr = str; + str = get_ucs(oldstr, &ch); + width += glyph_width(ch); + if (width > skip_width) + { + str = oldstr; + break; + } + } + width = 0; + while(*str != '\0') + { + str = get_ucs(str, &ch); + width += glyph_width(ch); + if (width > disp_width) + break; + + utf8 = rb->utf8encode(ch, utf8); + } + + return utf8; +} + +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; + } +} + bool done = false; int col = 0; #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; } -#define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns)) -#define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns)) +#define LINE_IS_FULL ((k>=max_columns-1) ||( width >= max_width)) +#define LINE_IS_NOT_FULL ((k<max_columns-1) &&( width < max_width)) static unsigned char* crop_at_width(const unsigned char* p) { int k,width; @@ -531,6 +725,8 @@ static unsigned char* crop_at_width(const unsigned char* p) while (LINE_IS_NOT_FULL) { oldp = p; + if (BUFFER_OOB(p)) + break; p = get_ucs(p, &ch); ADVANCE_COUNTERS(ch); } @@ -716,7 +912,11 @@ static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_sho next_line++; if (BUFFER_OOB(next_line)) + { + if (BUFFER_EOF() && next_line != cur_line) + return (unsigned char*) next_line; return NULL; + } if (is_short) *is_short = false; @@ -758,7 +958,7 @@ static unsigned char* find_prev_line(const unsigned char* cur_line) /* (else return NULL and read previous block) */ /* Wrap downwards until too far, then use the one before. */ - while (p < cur_line && p != NULL) { + while (p != NULL && p < cur_line) { prev_line = p; p = find_next_line(prev_line, NULL); } @@ -769,15 +969,34 @@ static unsigned char* find_prev_line(const unsigned char* cur_line) 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); - rb->lseek(fd, pos, SEEK_SET); numread = rb->read(fd, buf, size); + buf[numread] = 0; rb->button_clear_queue(); /* clear button queue */ for(i = 0; i < numread; i++) { @@ -810,6 +1029,18 @@ static void fill_buffer(long pos, unsigned char* buf, unsigned size) } } +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. */ @@ -848,6 +1079,48 @@ static int read_and_synch(int direction) 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; @@ -859,17 +1132,33 @@ static void viewer_scroll_up(void) } if (p != NULL) screen_top_ptr = p; + + decrement_current_line(); } -static void viewer_scroll_down(void) +static void viewer_scroll_down(bool autoscroll) { - if (next_screen_ptr != NULL) + if (cpage == lpage) + return; + + if (next_line_ptr != NULL) screen_top_ptr = next_line_ptr; + + if (prefs.scroll_mode == LINE || autoscroll) + increment_current_line(); +} + +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; + 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); @@ -879,14 +1168,47 @@ static void viewer_scrollbar(void) { else max_shown = min_shown + (next_screen_ptr - screen_top_ptr); - rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1, - LCD_HEIGHT, items, min_shown, max_shown, VERTICAL); + 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, resynch_move, spaces, left_col=0; + int i, j, k, line_len, line_width, spaces, left_col=0; int width, extra_spaces, indent_spaces, spaces_per_word; bool multiple_spacing, line_is_short; unsigned short ch; @@ -894,8 +1216,8 @@ static void viewer_draw(int col) unsigned char *line_begin; unsigned char *line_end; unsigned char c; - unsigned char scratch_buffer[MAX_COLUMNS + 1]; - unsigned char utf8_buffer[MAX_COLUMNS*4 + 1]; + unsigned char scratch_buffer[max_columns + 1]; + unsigned char utf8_buffer[max_columns*4 + 1]; unsigned char *endptr; /* If col==-1 do all calculations but don't display */ @@ -912,39 +1234,26 @@ static void viewer_draw(int col) for (i = 0; i < display_lines; i++) { if (BUFFER_OOB(line_end)) - break; /* Happens after display last line at BUFFER_EOF() */ - - line_begin = line_end; - line_end = find_next_line(line_begin, &line_is_short); - - if (line_end == NULL) { - if (BUFFER_EOF()) { - if (i < display_lines - 1 && !BUFFER_BOF()) { - if (col != -1) - rb->lcd_clear_display(); - - for (; i < display_lines - 1; i++) - viewer_scroll_up(); + { + if (lpage == cpage) + break; /* Happens after display last line at BUFFER_EOF() */ - line_begin = line_end = screen_top_ptr; - i = -1; - continue; - } - else { - line_end = buffer_end; - } - } - else { - resynch_move = read_and_synch(1); /* Read block & move ptrs */ - line_begin -= resynch_move; - if (i > 0) - next_line_ptr -= resynch_move; - - line_end = find_next_line(line_begin, NULL); - if (line_end == NULL) /* Should not really happen */ - break; + 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 */ @@ -970,7 +1279,7 @@ static void viewer_draw(int col) line_len--; } for (j=k=spaces=0; j < line_len; j++) { - if (k == MAX_COLUMNS) + if (k == max_columns) break; c = line_begin[j]; @@ -986,7 +1295,7 @@ static void viewer_draw(int col) while (spaces) { spaces--; scratch_buffer[k++] = ' '; - if (k == MAX_COLUMNS - 1) + if (k == max_columns - 1) break; } scratch_buffer[k++] = c; @@ -995,8 +1304,7 @@ static void viewer_draw(int col) } if (col != -1) { scratch_buffer[k] = 0; - endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer, - prefs.encoding, draw_columns/glyph_width('i')); + endptr = decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns); *endptr = 0; } } @@ -1036,7 +1344,7 @@ static void viewer_draw(int col) if (spaces) { /* total number of spaces to insert between words */ - extra_spaces = (draw_columns-width)/glyph_width(' ') + extra_spaces = (max_width-width)/glyph_width(' ') - indent_spaces; /* number of spaces between each word*/ spaces_per_word = extra_spaces / spaces; @@ -1056,7 +1364,7 @@ static void viewer_draw(int col) multiple_spacing = false; for (j=k=spaces=0; j < line_len; j++) { - if (k == MAX_COLUMNS) + if (k == max_columns) break; c = line_begin[j]; @@ -1081,11 +1389,9 @@ static void viewer_draw(int col) break; } } - if (col != -1) { scratch_buffer[k] = 0; - endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer, - prefs.encoding, k-col); + endptr = decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns); *endptr = 0; } } @@ -1116,11 +1422,29 @@ static void viewer_draw(int col) } } if (col != -1 && line_width > col) + { + int dpage = (cline+i <= display_lines)?cpage:cpage+1; + int dline = cline+i - ((cline+i <= display_lines)?0:display_lines); + bool bflag = (viewer_find_bookmark(dpage, dline) >= 0); +#ifdef HAVE_LCD_BITMAP + int dy = i * pf->height + header_height; +#endif + if (bflag) #ifdef HAVE_LCD_BITMAP - rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer); + { + rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG); + rb->lcd_fillrect(left_col, dy, LCD_WIDTH, pf->height); + rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); + } + rb->lcd_putsxy(left_col, dy, utf8_buffer); + rb->lcd_set_drawmode(DRMODE_SOLID); #else - rb->lcd_puts(left_col, i, utf8_buffer); + { + rb->lcd_puts(left_col, i, BOOKMARK_ICON); + } + rb->lcd_puts(left_col+1, i, utf8_buffer); #endif + } if (line_width > max_line_len) max_line_len = line_width; @@ -1140,6 +1464,14 @@ static void viewer_draw(int col) 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(); } @@ -1150,38 +1482,53 @@ static void viewer_top(void) 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) { - /* Read bottom of file into buffer - and point screen pointer to bottom */ - long last_sectors; + unsigned char *line_begin; + unsigned char *line_end; - if (file_size > buffer_size) { - /* Find last buffer in file, round up to next sector boundary */ - last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE; - last_sectors /= SMALL_BLOCK_SIZE; - last_sectors *= SMALL_BLOCK_SIZE; - } - else { - last_sectors = 0; - } + rb->splash(0, "Loading..."); - if (file_pos != last_sectors) + if (last_screen_top_ptr) { - file_pos = last_sectors; - buffer_end = BUFFER_END(); /* Update whenever file_pos changes */ - fill_buffer(last_sectors, buffer, buffer_size); + 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; } - screen_top_ptr = buffer_end-1; + 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 @@ -1192,18 +1539,80 @@ static void init_need_scrollbar(void) { 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 void change_font(unsigned char *font) +{ + unsigned char buf[MAX_PATH]; + + if (font == NULL || *font == '\0') + return; + + 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."); } -#else -#define init_need_scrollbar() #endif static bool viewer_init(void) { #ifdef HAVE_LCD_BITMAP - + /* initialize fonts */ pf = rb->font_get(FONT_UI); - - display_lines = LCD_HEIGHT / pf->height; draw_columns = display_columns = LCD_WIDTH; #else /* REAL fixed pitch :) all chars use up 1 cell */ @@ -1216,172 +1625,727 @@ static bool viewer_init(void) if (fd < 0) return false; - file_size = rb->filesize(fd); - if (file_size==-1) - return false; - /* Init mac_text value used in processing buffer */ mac_text = false; return true; } -static void viewer_default_settings(void) +/* 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.scroll_mode = PAGE; -#ifdef HAVE_LCD_BITMAP 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 void viewer_load_settings(void) /* same name as global, but not the same file.. */ +static bool viewer_read_preferences(int pfd) +{ + unsigned char buf[PREFERENCES_SIZE]; + unsigned char *p = buf; + + if (rb->read(pfd, buf, sizeof(buf)) != sizeof(buf)) + return false; + + prefs.word_mode = *p++; + prefs.line_mode = *p++; + prefs.view_mode = *p++; + prefs.encoding = *p++; + prefs.scrollbar_mode = *p++; + prefs.need_scrollbar = *p++; + prefs.page_mode = *p++; + prefs.header_mode = *p++; + prefs.footer_mode = *p++; + prefs.scroll_mode = *p++; + prefs.autoscroll_speed = *p++; + rb->memcpy(prefs.font, p, MAX_PATH); + + return true; +} + +static bool viewer_write_preferences(int pfd) +{ + unsigned char buf[PREFERENCES_SIZE]; + unsigned char *p = buf; + + *p++ = prefs.word_mode; + *p++ = prefs.line_mode; + *p++ = prefs.view_mode; + *p++ = prefs.encoding; + *p++ = prefs.scrollbar_mode; + *p++ = prefs.need_scrollbar; + *p++ = prefs.page_mode; + *p++ = prefs.header_mode; + *p++ = prefs.footer_mode; + *p++ = prefs.scroll_mode; + *p++ = prefs.autoscroll_speed; + rb->memcpy(p, prefs.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) { - int settings_fd, i; - struct bookmark_file_data *data; - struct bookmarked_file_info this_bookmark; + unsigned char c; + int i; - /* read settings file */ - settings_fd=rb->open(SETTINGS_FILE, O_RDONLY); - if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences))) + if (rb->read(bfd, &c, 1) != 1) { - rb->read(settings_fd, &prefs, sizeof(struct preferences)); - rb->close(settings_fd); + bookmark_count = 0; + return false; } - else + + bookmark_count = c; + if (bookmark_count > MAX_BOOKMARKS) + bookmark_count = MAX_BOOKMARKS; + + for (i = 0; i < bookmark_count; i++) { - /* load default settings if there is no settings file */ - viewer_default_settings(); + if (!viewer_read_bookmark_info(bfd, &bookmarks[i])) + { + bookmark_count = i; + return false; + } } + return true; +} - rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences)); +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; - data = (struct bookmark_file_data*)buffer; /* grab the text buffer */ - data->bookmarked_files_count = 0; + ul = b->page; + *p++ = ul >> 8; + *p++ = ul; - /* read bookmarks if file exists */ - settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY); - if (settings_fd >= 0) + *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++) { - /* figure out how many items to read */ - rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int)); - if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES) - data->bookmarked_files_count = MAX_BOOKMARKED_FILES; - rb->read(settings_fd, data->bookmarks, - sizeof(struct bookmarked_file_info) * data->bookmarked_files_count); - rb->close(settings_fd); + if (!viewer_write_bookmark_info(bfd, &bookmarks[i])) + return false; } - file_pos = 0; - screen_top_ptr = buffer; + return true; +} - /* check if current file is in list */ - for (i=0; i < data->bookmarked_files_count; i++) - { - if (!rb->strcmp(file_name, data->bookmarks[i].filename)) - { - int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos; - int screen_top = screen_pos % buffer_size; - file_pos = screen_pos - screen_top; - screen_top_ptr = buffer + screen_top; - break; - } +static bool viewer_load_global_settings(void) +{ + unsigned buf[GLOBAL_SETTINGS_H_SIZE]; + int sfd = rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY); + + 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) || + !viewer_read_preferences(sfd)) + { + rb->close(sfd); + return false; } + rb->close(sfd); + return true; +} - this_bookmark.file_position = file_pos; - this_bookmark.top_ptr_pos = screen_top_ptr - buffer; +static bool viewer_save_global_settings(void) +{ + int sfd = rb->open(GLOBAL_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC); - rb->memset(&this_bookmark.filename[0],0,MAX_PATH); - rb->strcpy(this_bookmark.filename,file_name); + if (sfd < 0) + return false; - /* prevent potential slot overflow */ - if (i >= data->bookmarked_files_count) + if (rb->write(sfd, &GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE) + != GLOBAL_SETTINGS_H_SIZE || + !viewer_write_preferences(sfd)) { - if (i < MAX_BOOKMARKED_FILES) - data->bookmarked_files_count++; - else - i = MAX_BOOKMARKED_FILES-1; + 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; +} - /* write bookmark file with spare slot in first position - to be filled in by viewer_save_settings */ - settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT); - if (settings_fd >=0 ) +static void viewer_load_settings(void) +{ + unsigned char buf[MAX_PATH+2]; + unsigned int fcount; + unsigned int i; + bool res = false; + int sfd; + unsigned int size; + + 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)) { - /* write count */ - rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int)); + /* illegal setting file */ + rb->close(sfd); + + if (rb->file_exists(SETTINGS_FILE)) + rb->remove(SETTINGS_FILE); - /* write the current bookmark */ - rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info)); + goto read_end; + } + + 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; - /* write everything that was before this bookmark */ - rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i); + size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1]; + if (rb->strcmp(buf, file_name)) + { + if (rb->lseek(sfd, size, SEEK_CUR) < 0) + break; + continue; + } + if (!viewer_read_preferences(sfd)) + break; - rb->close(settings_fd); + res = viewer_read_bookmark_infos(sfd); + break; } - buffer_end = BUFFER_END(); /* Update whenever file_pos changes */ + rb->close(sfd); - if (BUFFER_OOB(screen_top_ptr)) +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 + if (strcmp(prefs.font, rb->global_settings->font_file)) + change_font(prefs.font); + init_need_scrollbar(); + init_header_and_footer(); +#endif } -static void viewer_save_settings(void)/* same name as global, but not the same file.. */ +static bool copy_bookmark_file(int sfd, int dfd, off_t start, off_t size) { - int settings_fd; + off_t rsize; + + if (rb->lseek(sfd, start, SEEK_SET) < 0) + return false; - /* save the viewer settings if they have been changed */ - if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences))) + while (size > 0) { - settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */ + if (size > buffer_size) + rsize = buffer_size; + else + rsize = size; + size -= rsize; - if (settings_fd >= 0 ) - { - rb->write (settings_fd, &prefs, sizeof(struct preferences)); - rb->close(settings_fd); - } + 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; - /* save the bookmark if the position has changed */ - if (file_pos + screen_top_ptr - buffer != start_position) + ofd = rb->open(SETTINGS_FILE, O_RDWR); + if (ofd >= 0) { - settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT); + if ((rb->read(ofd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) || + rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE)) + { + rb->close(ofd); + goto save_err; + } + 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 (settings_fd >= 0 ) + if (!copy_bookmark_file(ofd, tfd, 0, first_copy_size)) { - struct bookmarked_file_info b; - b.file_position = file_pos + screen_top_ptr - buffer; - b.top_ptr_pos = 0; /* this is only kept for legassy reasons */ - rb->memset(&b.filename[0],0,MAX_PATH); - rb->strcpy(b.filename,file_name); - rb->lseek(settings_fd,sizeof(signed int),SEEK_SET); - rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info)); - rb->close(settings_fd); + 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); + 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)) + 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; - viewer_save_settings(); + /* 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 (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; + + 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) @@ -1389,8 +2353,8 @@ static int col_limit(int col) if (col < 0) col = 0; else - if (col > max_line_len - 2*glyph_width('o')) - col = max_line_len - 2*glyph_width('o'); + if (col >= max_width) + col = max_width - draw_columns; return col; } @@ -1401,6 +2365,8 @@ 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++) { @@ -1408,8 +2374,21 @@ static bool encoding_setting(void) names[idx].voice_id = -1; } - return rb->set_option("Encoding", &prefs.encoding, INT, names, + 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) @@ -1449,6 +2428,7 @@ static bool view_mode_setting(void) names , 2, NULL); if (prefs.view_mode == NARROW) col = 0; + calc_max_width(); return ret; } @@ -1485,6 +2465,152 @@ static bool scrollbar_setting(void) 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 does not access."); + 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 does not access."); + 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) + { + rb->memset(prefs.font, 0, MAX_PATH); + rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string); + change_font(prefs.font); + } + + return res; +} #endif static bool autoscroll_speed_setting(void) @@ -1506,6 +2632,12 @@ 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); @@ -1514,28 +2646,39 @@ MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed", MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON, &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item, #ifdef HAVE_LCD_BITMAP - &scrollbar_item, &page_mode_item, + &scrollbar_item, &page_mode_item, &header_item, &footer_item, &font_item, #endif &scroll_mode_item, &autoscroll_speed_item); -static bool viewer_options_menu(void) +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 - /* Show-scrollbar mode for current view-width mode */ - init_need_scrollbar(); + 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", "Quit"); + "Show Playback Menu", "Select Bookmark", + "Global Settings", "Quit"); result = rb->do_menu(&menu, NULL, NULL, false); switch (result) @@ -1543,12 +2686,31 @@ static void viewer_menu(void) case 0: /* return */ break; case 1: /* change settings */ - done = viewer_options_menu(); + done = viewer_options_menu(false); break; case 2: /* playback control */ playback_control(NULL); break; - case 3: /* quit */ + 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; @@ -1567,6 +2729,13 @@ enum plugin_status plugin_start(const void* file) /* 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; @@ -1592,13 +2761,14 @@ enum plugin_status plugin_start(const void* file) { if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10)) { - viewer_scroll_down(); + viewer_scroll_down(true); viewer_draw(col); old_tick = *rb->current_tick; } } button = rb->button_get_w_tmo(HZ/10); + switch (button) { case VIEWER_MENU: #ifdef VIEWER_MENU2 @@ -1647,10 +2817,14 @@ enum plugin_status plugin_start(const void* file) { /* Page down */ if (next_screen_ptr != NULL) + { screen_top_ptr = next_screen_to_draw_ptr; + if (cpage < MAX_PAGE) + cpage++; + } } else - viewer_scroll_down(); + viewer_scroll_down(autoscroll); old_tick = *rb->current_tick; viewer_draw(col); break; @@ -1697,8 +2871,8 @@ enum plugin_status plugin_start(const void* file) case VIEWER_LINE_DOWN: case VIEWER_LINE_DOWN | BUTTON_REPEAT: /* Scroll down one line */ - if (next_screen_ptr != NULL) - screen_top_ptr = next_line_ptr; + viewer_scroll_down(autoscroll); + increment_current_line(); old_tick = *rb->current_tick; viewer_draw(col); break; @@ -1736,6 +2910,29 @@ enum plugin_status plugin_start(const void* file) 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 add bookmark."); + else + { + viewer_add_bookmark(); + rb->splash(HZ/2, "Bookmark add."); + } + } + else + { + viewer_remove_bookmark(idx); + rb->splash(HZ/2, "Bookmark remove."); + } + viewer_draw(col); + } + break; + default: if (rb->default_event_handler_ex(button, viewer_exit, NULL) == SYS_USB_CONNECTED) @@ -1750,5 +2947,3 @@ enum plugin_status plugin_start(const void* file) } return PLUGIN_OK; } - - |