diff --git a/apps/plugins/viewer.c b/apps/plugins/viewer.c old mode 100644 new mode 100755 index f01afbb..5ae4943 --- a/apps/plugins/viewer.c +++ b/apps/plugins/viewer.c @@ -26,21 +26,97 @@ PLUGIN_HEADER -#define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */ -#define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat" +/* viewer plugin global setting file + * binary file, so dont use .cfg + */ +#define GLOBAL_SETTINGS_FILE VIEWERS_DIR "/viewer.dat" + +/* 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 + * page_number_mode 1 + * title_mode 1 + * scroll_mode 1 + * autoscroll_speed 1 + * font file path 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 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 /* 4k: Smallest file chunk we will read */ +#define LARGE_BLOCK_SIZE (block_size << 1) /* 8k: 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)) #define SCROLLBAR_WIDTH 6 +#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 + +#ifdef HAVE_LCD_BITMAP +#ifdef HAVE_LCD_COLOR +#define BOOKMARK_COLOR LCD_RGBPACK(255, 255, 255) +#elif LCD_DEPTH > 1 +#define BOOKMARK_COLOR LCD_WHITE +#endif +#endif + +#ifdef HAVE_LCD_BITMAP +#ifdef HAVE_LCD_COLOR +#define TEXT_COLOR LCD_RGBPACK(255, 255, 255) +#elif LCD_DEPTH > 1 +#define TEXT_COLOR LCD_WHITE +#endif +#endif -#define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1) +#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) @@ -78,6 +154,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 @@ -91,6 +168,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 @@ -102,6 +180,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 @@ -112,6 +191,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) || \ @@ -127,6 +207,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 @@ -142,6 +223,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 @@ -152,6 +234,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 @@ -162,6 +245,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 @@ -172,6 +256,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 @@ -184,6 +269,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_REC|BUTTON_SELECT) /* Sansa Fuze keys */ #elif CONFIG_KEYPAD == SANSA_FUZE_PAD @@ -196,6 +282,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 @@ -208,6 +295,7 @@ PLUGIN_HEADER #define VIEWER_AUTOSCROLL BUTTON_REC #define VIEWER_LINE_UP BUTTON_UP #define VIEWER_LINE_DOWN BUTTON_DOWN +#define VIEWER_BOOKMARK (BUTTON_REC | BUTTON_SELECT) /* Sansa Clip keys */ #elif CONFIG_KEYPAD == SANSA_CLIP_PAD @@ -220,6 +308,7 @@ PLUGIN_HEADER #define VIEWER_AUTOSCROLL BUTTON_HOME #define VIEWER_LINE_UP BUTTON_UP #define VIEWER_LINE_DOWN BUTTON_DOWN +#define VIEWER_BOOKMARK (BUTTON_HOME|BUTTON_SELECT) /* Sansa M200 keys */ #elif CONFIG_KEYPAD == SANSA_M200_PAD @@ -232,6 +321,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 @@ -242,6 +332,7 @@ PLUGIN_HEADER #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_REW #define VIEWER_AUTOSCROLL BUTTON_PLAY +#define VIEWER_BOOKMARK BUTTON_SELECT /*M-Robe 500 keys */ #elif CONFIG_KEYPAD == MROBE500_PAD @@ -252,6 +343,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 @@ -267,6 +359,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 @@ -277,6 +370,7 @@ 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 @@ -288,11 +382,13 @@ PLUGIN_HEADER #define VIEWER_MENU BUTTON_RC_MENU #define VIEWER_AUTOSCROLL BUTTON_RC_MODE #define VIEWER_RC_QUIT BUTTON_REC +#define VIEWER_BOOKMARK BUTTON_PLAY /* Cowon D2 keys */ #elif CONFIG_KEYPAD == COWOND2_PAD #define VIEWER_QUIT BUTTON_POWER #define VIEWER_MENU BUTTON_MENU +#define VIEWER_BOOKMARK BUTTON_PLUS #elif CONFIG_KEYPAD == IAUDIO67_PAD #define VIEWER_QUIT BUTTON_POWER @@ -303,6 +399,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 @@ -313,6 +410,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 @@ -323,6 +421,7 @@ PLUGIN_HEADER #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT #define VIEWER_MENU BUTTON_MENU #define VIEWER_AUTOSCROLL BUTTON_VIEW +#define VIEWER_BOOKMARK BUTTON_SELECT #else #error No keymap defined! @@ -353,15 +452,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 { @@ -384,7 +479,6 @@ struct preferences { enum codepages encoding; -#ifdef HAVE_LCD_BITMAP enum { SB_OFF=0, SB_ON, @@ -395,7 +489,18 @@ struct preferences { NO_OVERLAP=0, OVERLAP, } page_mode; -#endif /* HAVE_LCD_BITMAP */ + + enum { + HD_NONE = 0, + HD_SB, + HD_FP, + HD_BOTH, + } header_mode; + + enum { + FT_OFF = 0, + FT_ON + } footer_mode; enum { PAGE=0, @@ -403,7 +508,8 @@ struct preferences { } scroll_mode; int autoscroll_speed; - + + unsigned char font[MAX_PATH]; }; struct preferences prefs; @@ -411,6 +517,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 */ @@ -422,18 +529,32 @@ 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 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 unsigned char sys_font[MAX_PATH]; #endif +struct viewport vp; +struct bookmark_info bookmarks[MAX_BOOKMARKS]; +static int bookmark_count; +/* UTF-8 BOM */ +#define BOM "\xef\xbb\xbf" +#define BOM_SIZE 3 -int glyph_width(int ch) +static bool is_bom = false; + +static int glyph_width(int ch) { if (ch == 0) ch = ' '; @@ -445,7 +566,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; @@ -458,15 +579,18 @@ unsigned char* get_ucs(const unsigned char* str, unsigned short* ch) rb->utf8decode(utf8_tmp, ch); #ifdef HAVE_LCD_BITMAP - if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS) + if (*str < 0x80 || prefs.encoding < SJIS + || (prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0)) return (unsigned char*)str+1; - else + + return (unsigned char*)str+2; +#else + return (unsigned char*)str+1; #endif - return (unsigned char*)str+2; } -bool done = false; -int col = 0; +static bool done = false; +static int cols = 0; #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; } #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns)) @@ -481,6 +605,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); } @@ -666,7 +792,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; @@ -708,7 +838,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); } @@ -719,15 +849,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++) { @@ -760,6 +909,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. */ @@ -798,6 +959,47 @@ 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; @@ -809,17 +1011,25 @@ 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(); } #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); @@ -829,15 +1039,54 @@ 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 = vp.y; + if (prefs.header_mode == HD_FP || prefs.header_mode == HD_BOTH) + sb_begin_y += pf->height; + + sb_height = LCD_HEIGHT - sb_begin_y + - ((prefs.footer_mode)?pf->height:0); + + 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_NONE) + return; + + if (prefs.header_mode == HD_SB || prefs.header_mode == HD_BOTH) + rb->viewportmanager_set_statusbar(VP_SB_ALLSCREENS); + else + rb->viewportmanager_set_statusbar(VP_SB_HIDE_ALL); + + if (prefs.header_mode != HD_SB) + rb->lcd_putsxy(0, vp.y, file_name); +} + +static void viewer_show_footer(void) +{ + unsigned char buf[12]; + + if (prefs.footer_mode == FT_OFF) + return; + + 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 - pf->height, buf); } #endif static void viewer_draw(int col) { - int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0; - int width, extra_spaces, indent_spaces, spaces_per_word; + int i, j, k, line_len, line_width, spaces, left_col=0; + int width, extra_spaces, indent_spaces, spaces_per_word, nc; bool multiple_spacing, line_is_short; unsigned short ch; unsigned char *str, *oldstr; @@ -862,39 +1111,25 @@ 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 */ @@ -911,6 +1146,9 @@ static void viewer_draw(int col) line_width += glyph_width(ch); } + endptr = utf8_buffer; + nc = draw_columns/glyph_width('i'); + if (prefs.line_mode == JOIN) { if (line_begin[0] == 0) { line_begin++; @@ -945,8 +1183,12 @@ 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')); + if (col > 0 && k >= nc) + endptr = rb->iso_decode(scratch_buffer + nc, utf8_buffer, + prefs.encoding, k - nc); + else if (col == 0) + endptr = rb->iso_decode(scratch_buffer, utf8_buffer, + prefs.encoding, nc); *endptr = 0; } } @@ -1034,8 +1276,12 @@ static void viewer_draw(int col) if (col != -1) { scratch_buffer[k] = 0; - endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer, - prefs.encoding, k-col); + if (col > 0 && k >= nc) + endptr = rb->iso_decode(scratch_buffer + nc, utf8_buffer, + prefs.encoding, k - nc); + else if (col == 0) + endptr = rb->iso_decode(scratch_buffer, utf8_buffer, + prefs.encoding, nc); *endptr = 0; } } @@ -1065,12 +1311,38 @@ static void viewer_draw(int col) *endptr = 0; } } - if (col != -1 && line_width > col) + if (col != -1) + { + 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 + vp.y; + if (prefs.header_mode == HD_FP || prefs.header_mode == HD_BOTH) + dy += pf->height; +#endif + if (bflag) #ifdef HAVE_LCD_BITMAP - rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer); + { +#if LCD_DEPTH > 1 + rb->lcd_set_foreground(BOOKMARK_COLOR); +#endif + rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG); + rb->lcd_fillrect(left_col, dy, LCD_WIDTH, pf->height); +#if LCD_DEPTH > 1 + rb->lcd_set_foreground(TEXT_COLOR); +#endif + 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; @@ -1090,6 +1362,16 @@ static void viewer_draw(int col) next_screen_to_draw_ptr = next_screen_ptr; #endif +#ifdef HAVE_LCD_BITMAP + /* show header */ + if (prefs.header_mode) + viewer_show_header(); + + /* show footer */ + if (prefs.footer_mode) + viewer_show_footer(); +#endif + if (col != -1) rb->lcd_update(); } @@ -1100,38 +1382,52 @@ 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; - - 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; - } + unsigned char *line_begin; + unsigned char *line_end; - if (file_pos != last_sectors) + rb->splash(0, "Loading..."); + + 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; } #ifdef HAVE_LCD_BITMAP @@ -1143,17 +1439,48 @@ static void init_need_scrollbar(void) { draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns; par_indent_spaces = draw_columns/(5*glyph_width(' ')); } + +static void init_header_and_footer(void) +{ + if (prefs.header_mode == HD_SB || prefs.header_mode == HD_BOTH) + rb->viewportmanager_set_statusbar(VP_SB_ALLSCREENS); + else + rb->viewportmanager_set_statusbar(VP_SB_HIDE_ALL); + rb->viewport_set_defaults(&vp, 0); + + display_lines = (LCD_HEIGHT - vp.y)/pf->height - ((prefs.footer_mode)?1:0); + if (prefs.header_mode == HD_FP || prefs.header_mode == HD_BOTH) + display_lines--; + + lpage = 0; + last_file_pos = 0; + last_screen_top_ptr = NULL; +} + +static void set_font(unsigned char *font) +{ + unsigned char buf[MAX_PATH]; + + if (font == NULL || *font == '\0') + rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, sys_font); + else + rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font); + + pf = rb->font_load(buf); +} + #else #define init_need_scrollbar() +#define init_header_and_footer() +#define set_font() #endif static bool viewer_init(void) { #ifdef HAVE_LCD_BITMAP - + rb->snprintf(sys_font, MAX_PATH, "%s", rb->global_settings->font_file); 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 */ @@ -1166,172 +1493,649 @@ static bool viewer_init(void) if (fd==-1) 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) { - 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; -#endif - prefs.autoscroll_speed = 1; - /* Set codepage to system default */ - prefs.encoding = rb->global_settings->default_codepage; + file_size = rb->filesize(fd); + if (file_size==-1) + return; + + if (prefs.encoding == UTF_8 && is_bom) + file_size -= BOM_SIZE; } -static void viewer_load_settings(void) /* same name as global, but not the same file.. */ +static int bm_comp(const void *a, const void *b) { - int settings_fd, i; - struct bookmark_file_data *data; - struct bookmarked_file_info this_bookmark; - + 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 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_bookmark(void) +{ + int i, j; + + for (i = 0; i < bookmark_count; i++) + { + 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--; + break; + } + bookmarks[i].flag = BOOKMARK_USER; + } +} + +static void viewer_select_bookmark(void) +{ + int i; + int screen_pos; + int screen_top; + int selected = -1; + + struct opt_items items[bookmark_count]; + unsigned char names[bookmark_count][38]; + + 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]), + "Page: %d Line: %d %s", + bookmarks[i].page, + bookmarks[i].line, + (bookmarks[i].flag&BOOKMARK_LAST)? + "(last read page)":""); + items[i].string = names[i]; + items[i].voice_id = -1; + } + + rb->set_option("Select bookmark", &selected, INT, items, + sizeof(items) / sizeof(items[0]), NULL); + + if (selected < 0 || selected >= bookmark_count) + { + rb->splash(HZ, "Start the first page."); + file_pos = 0; + screen_top_ptr = buffer; + cline = 1; + cpage = 1; + } + else + { + 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; + } +} + +static void viewer_default_preferences(void) +{ + int sfd; + /* read settings file */ - settings_fd=rb->open(SETTINGS_FILE, O_RDONLY); - if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences))) + sfd=rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY); + if ((sfd >= 0) && + (rb->filesize(sfd) == sizeof(struct preferences))) { - rb->read(settings_fd, &prefs, sizeof(struct preferences)); - rb->close(settings_fd); + rb->read(sfd, &prefs, sizeof(struct preferences)); + rb->close(sfd); } else { - /* load default settings if there is no settings file */ - viewer_default_settings(); + prefs.word_mode = WRAP; + prefs.line_mode = NORMAL; + prefs.view_mode = NARROW; + 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_ON; + rb->snprintf(prefs.font, MAX_PATH, "%s", sys_font); +#else + prefs.header_mode = HD_OFF; + prefs.footer_mode = FT_OFF; +#endif + prefs.autoscroll_speed = 1; + /* Set codepage to system default */ + prefs.encoding = rb->global_settings->default_codepage; } +} - rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences)); +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]; - data = (struct bookmark_file_data*)buffer; /* grab the text buffer */ - data->bookmarked_files_count = 0; + return true; +} + +static bool viewer_read_bookmark_infos(int bfd) +{ + unsigned char c; + int i; - /* read bookmarks if file exists */ - settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY); - if (settings_fd >= 0) + if (rb->read(bfd, &c, 1) != 1) { - /* 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); + bookmark_count = 0; + return false; } - file_pos = 0; - screen_top_ptr = buffer; + bookmark_count = c; + if (bookmark_count > MAX_BOOKMARKS) + bookmark_count = MAX_BOOKMARKS; - /* check if current file is in list */ - for (i=0; i < data->bookmarked_files_count; i++) + for (i = 0; i < bookmark_count; i++) { - if (!rb->strcmp(file_name, data->bookmarks[i].filename)) + if (!viewer_read_bookmark_info(bfd, &bookmarks[i])) { - 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; - } + 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; - this_bookmark.file_position = file_pos; - this_bookmark.top_ptr_pos = screen_top_ptr - buffer; + ul = b->page; + *p++ = ul >> 8; + *p++ = ul; - rb->memset(&this_bookmark.filename[0],0,MAX_PATH); - rb->strcpy(this_bookmark.filename,file_name); + *p++ = b->line; + *p = b->flag; + + return (rb->write(bfd, buf, sizeof(buf)) == sizeof(buf)); +} - /* prevent potential slot overflow */ - if (i >= data->bookmarked_files_count) +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 (i < MAX_BOOKMARKED_FILES) - data->bookmarked_files_count++; - else - i = MAX_BOOKMARKED_FILES-1; - } - - /* 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 ) - { - /* write count */ - rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int)); - - /* write the current bookmark */ - rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info)); - - /* write everything that was before this bookmark */ - rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i); - - rb->close(settings_fd); + if (!viewer_write_bookmark_info(bfd, &bookmarks[i])) + return false; } - buffer_end = BUFFER_END(); /* Update whenever file_pos changes */ + return true; +} + +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)) + { + /* illegal setting file */ + rb->close(sfd); + + if (rb->file_exists(SETTINGS_FILE)) + rb->remove(SETTINGS_FILE); + + goto read_end; + } - if (BUFFER_OOB(screen_top_ptr)) + 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]; + if (rb->strcmp(buf, file_name)) + { + if (rb->lseek(sfd, size, SEEK_CUR) < 0) + break; + continue; + } + if (!viewer_read_preferences(sfd)) + break; + + res = viewer_read_bookmark_infos(sfd); + break; + } + + rb->close(sfd); + +read_end: + if (!res) + { + /* set default preference */ + 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)); + + if (bookmark_count > 1) + viewer_select_bookmark(); + 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_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); /* remember the current position */ start_position = file_pos + screen_top_ptr - buffer; + set_font(prefs.font); init_need_scrollbar(); + init_header_and_footer(); } -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; - /* save the viewer settings if they have been changed */ - if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences))) + if (rb->lseek(sfd, start, SEEK_SET) < 0) + return false; + + 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 (rb->read(sfd, buffer, rsize) != rsize || + rb->write(dfd, buffer, rsize) != rsize) + return false; + } + return true; +} - if (settings_fd >= 0 ) - { - rb->write (settings_fd, &prefs, sizeof(struct preferences)); - rb->close(settings_fd); - } +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; } - - /* save the bookmark if the position has changed */ - if (file_pos + screen_top_ptr - buffer != start_position) + + 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) { - 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)) + { + rb->close(ofd); + goto save_err; + } + if (second_copy_start_pos > 0) { - 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); + 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 the bookmark */ + if (!viewer_save_settings()) + rb->splash(HZ, "Can't save preference and bookmarks."); + rb->close(fd); + set_font(sys_font); +} + +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 */ + i = viewer_find_bookmark(cpage, cline); + if (i >= 0) + bookmarks[i].flag |= BOOKMARK_LAST; + else + { + viewer_add_bookmark(); + bookmarks[bookmark_count-1].flag = BOOKMARK_LAST; + } + + 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) + { + int line = cline; + for (i = 1; i < line; i++) + viewer_scroll_up(); + } + break; + } + } } static int col_limit(int col) @@ -1351,6 +2155,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++) { @@ -1358,8 +2164,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) @@ -1398,7 +2217,7 @@ static bool view_mode_setting(void) ret = rb->set_option("Wide View", &prefs.view_mode, INT, names , 2, NULL); if (prefs.view_mode == NARROW) - col = 0; + cols = 0; return ret; } @@ -1435,6 +2254,118 @@ static bool scrollbar_setting(void) return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT, names, 2, NULL); } + +static bool header_setting(void) +{ + static const struct opt_items names[] = { + {"None", -1}, + {"Status bar", -1}, + {"File path", -1}, + {"Both", -1} + }; + + return rb->set_option("Show Header", &prefs.header_mode, INT, + names, 4, NULL); +} + +static bool footer_setting(void) +{ + static const struct opt_items names[] = { + {"No", -1}, + {"Yes", -1} + }; + + return rb->set_option("Show Footer", &prefs.footer_mode, INT, + names, 2, 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 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; + 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; + if (!rb->strcmp(p, prefs.font)) + new_font = i; + + p += len-3; + i++; + } + rb->closedir(dir); + + rb->qsort(names, count, sizeof(struct opt_items), font_comp); + + res = rb->set_option("Select Font", &new_font, INT, + names, count, NULL); + + rb->memset(prefs.font, 0, MAX_PATH); + rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string); + + return res; +} #endif static bool autoscroll_speed_setting(void) @@ -1447,6 +2378,7 @@ static bool viewer_options_menu(void) { int m; bool result; + struct preferences tmp_prefs; static const struct menu_item items[] = { {"Encoding", encoding_setting }, @@ -1456,20 +2388,34 @@ static bool viewer_options_menu(void) #ifdef HAVE_LCD_BITMAP {"Show Scrollbar", scrollbar_setting }, {"Overlap Pages", page_mode_setting }, + {"Show Header", header_setting }, + {"Show Footer", footer_setting }, + {"Font", font_setting }, #endif {"Scroll Mode", scroll_mode_setting}, {"Auto-Scroll Speed", autoscroll_speed_setting }, }; + + rb->memcpy(&tmp_prefs, &prefs, sizeof(struct preferences)); + m = menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL); result = menu_run(m); menu_exit(m); -#ifdef HAVE_LCD_BITMAP - /* Show-scrollbar mode for current view-width mode */ - init_need_scrollbar(); -#endif + if (rb->memcmp(&tmp_prefs, &prefs, sizeof(struct preferences))) + { + set_font(prefs.font); + + /* Show-scrollbar mode for current view-width mode */ + init_need_scrollbar(); + init_header_and_footer(); + calc_page(); + + set_font(sys_font); + } + return result; } @@ -1484,6 +2430,7 @@ static void viewer_menu(void) {"Return", NULL }, }; + set_font(sys_font); m = menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL); result=menu_show(m); switch (result) @@ -1503,7 +2450,9 @@ static void viewer_menu(void) break; } menu_exit(m); - viewer_draw(col); + if (!done) + set_font(prefs.font); + viewer_draw(cols); } enum plugin_status plugin_start(const void* file) @@ -1517,6 +2466,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; @@ -1534,7 +2490,7 @@ enum plugin_status plugin_start(const void* file) rb->lcd_set_backdrop(NULL); #endif - viewer_draw(col); + viewer_draw(cols); while (!done) { @@ -1542,13 +2498,17 @@ enum plugin_status plugin_start(const void* file) { if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10)) { - viewer_scroll_down(); - viewer_draw(col); + viewer_scroll_down(true); + viewer_draw(cols); old_tick = *rb->current_tick; } } button = rb->button_get_w_tmo(HZ/10); + + if (prefs.header_mode == HD_SB || prefs.header_mode == HD_BOTH) + rb->viewportmanager_set_statusbar(VP_SB_ALLSCREENS); + switch (button) { case VIEWER_MENU: viewer_menu(); @@ -1577,7 +2537,7 @@ enum plugin_status plugin_start(const void* file) else viewer_scroll_up(); old_tick = *rb->current_tick; - viewer_draw(col); + viewer_draw(cols); break; case VIEWER_PAGE_DOWN: @@ -1586,42 +2546,46 @@ 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); + viewer_draw(cols); break; case VIEWER_SCREEN_LEFT: case VIEWER_SCREEN_LEFT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Screen left */ - col -= draw_columns; - col = col_limit(col); + cols -= draw_columns; + cols = col_limit(cols); } else { /* prefs.view_mode == NARROW */ /* Top of file */ viewer_top(); } - viewer_draw(col); + viewer_draw(cols); break; case VIEWER_SCREEN_RIGHT: case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Screen right */ - col += draw_columns; - col = col_limit(col); + cols += draw_columns; + cols = col_limit(cols); } else { /* prefs.view_mode == NARROW */ /* Bottom of file */ viewer_bottom(); } - viewer_draw(col); + viewer_draw(cols); break; #ifdef VIEWER_LINE_UP @@ -1630,7 +2594,7 @@ enum plugin_status plugin_start(const void* file) /* Scroll up one line */ viewer_scroll_up(); old_tick = *rb->current_tick; - viewer_draw(col); + viewer_draw(cols); break; case VIEWER_LINE_DOWN: @@ -1639,7 +2603,7 @@ enum plugin_status plugin_start(const void* file) if (next_screen_ptr != NULL) screen_top_ptr = next_line_ptr; old_tick = *rb->current_tick; - viewer_draw(col); + viewer_draw(cols); break; #endif #ifdef VIEWER_COLUMN_LEFT @@ -1647,9 +2611,9 @@ enum plugin_status plugin_start(const void* file) case VIEWER_COLUMN_LEFT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Scroll left one column */ - col -= glyph_width('o'); - col = col_limit(col); - viewer_draw(col); + cols -= glyph_width('o'); + cols = col_limit(cols); + viewer_draw(cols); } break; @@ -1657,9 +2621,9 @@ enum plugin_status plugin_start(const void* file) case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT: if (prefs.view_mode == WIDE) { /* Scroll right one column */ - col += glyph_width('o'); - col = col_limit(col); - viewer_draw(col); + cols += glyph_width('o'); + cols = col_limit(cols); + viewer_draw(cols); } break; #endif @@ -1672,6 +2636,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(cols); + } + break; + default: if (rb->default_event_handler_ex(button, viewer_exit, NULL) == SYS_USB_CONNECTED) @@ -1686,5 +2673,3 @@ enum plugin_status plugin_start(const void* file) } return PLUGIN_OK; } - -