Add 2008 to the copyright notice.
[Rockbox.git] / apps / plugins / viewer.c
blobda288f1d6f552c974637a4f3af7ecc2f0d7705a1
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include "plugin.h"
21 #include <ctype.h>
22 #include "playback_control.h"
23 #include "oldmenuapi.h"
25 PLUGIN_HEADER
27 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
28 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
30 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
31 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
32 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
33 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
34 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
35 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
36 #define TOP_SECTOR buffer
37 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
38 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
39 #define SCROLLBAR_WIDTH 6
41 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
43 /* Out-Of-Bounds test for any pointer to data in the buffer */
44 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
46 /* Does the buffer contain the beginning of the file? */
47 #define BUFFER_BOF() (file_pos==0)
49 /* Does the buffer contain the end of the file? */
50 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
52 /* Formula for the endpoint address outside of buffer data */
53 #define BUFFER_END() \
54 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
56 /* Is the entire file being shown in one screen? */
57 #define ONE_SCREEN_FITS_ALL() \
58 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
60 /* Is a scrollbar called for on the current screen? */
61 #define NEED_SCROLLBAR() \
62 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
64 /* variable button definitions */
66 /* Recorder keys */
67 #if CONFIG_KEYPAD == RECORDER_PAD
68 #define VIEWER_QUIT BUTTON_OFF
69 #define VIEWER_PAGE_UP BUTTON_UP
70 #define VIEWER_PAGE_DOWN BUTTON_DOWN
71 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
72 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
73 #define VIEWER_MENU BUTTON_F1
74 #define VIEWER_AUTOSCROLL BUTTON_PLAY
75 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
76 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
77 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
78 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
80 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
81 #define VIEWER_QUIT BUTTON_OFF
82 #define VIEWER_PAGE_UP BUTTON_UP
83 #define VIEWER_PAGE_DOWN BUTTON_DOWN
84 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
85 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
86 #define VIEWER_MENU BUTTON_F1
87 #define VIEWER_AUTOSCROLL BUTTON_SELECT
88 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
89 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
90 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
91 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
93 /* Ondio keys */
94 #elif CONFIG_KEYPAD == ONDIO_PAD
95 #define VIEWER_QUIT BUTTON_OFF
96 #define VIEWER_PAGE_UP BUTTON_UP
97 #define VIEWER_PAGE_DOWN BUTTON_DOWN
98 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
99 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
100 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
101 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
102 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
104 /* Player keys */
105 #elif CONFIG_KEYPAD == PLAYER_PAD
106 #define VIEWER_QUIT BUTTON_STOP
107 #define VIEWER_PAGE_UP BUTTON_LEFT
108 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
109 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
110 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
111 #define VIEWER_MENU BUTTON_MENU
112 #define VIEWER_AUTOSCROLL BUTTON_PLAY
114 /* iRiver H1x0 && H3x0 keys */
115 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
116 (CONFIG_KEYPAD == IRIVER_H300_PAD)
117 #define VIEWER_QUIT BUTTON_OFF
118 #define VIEWER_PAGE_UP BUTTON_UP
119 #define VIEWER_PAGE_DOWN BUTTON_DOWN
120 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
121 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
122 #define VIEWER_MENU BUTTON_MODE
123 #define VIEWER_AUTOSCROLL BUTTON_SELECT
124 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
125 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
126 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
127 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
129 #define VIEWER_RC_QUIT BUTTON_RC_STOP
131 /* iPods */
132 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
133 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
135 #define VIEWER_QUIT_PRE BUTTON_SELECT
136 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
137 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
138 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
139 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
140 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
141 #define VIEWER_MENU BUTTON_MENU
142 #define VIEWER_AUTOSCROLL BUTTON_PLAY
144 /* iFP7xx keys */
145 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
146 #define VIEWER_QUIT BUTTON_PLAY
147 #define VIEWER_PAGE_UP BUTTON_UP
148 #define VIEWER_PAGE_DOWN BUTTON_DOWN
149 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
150 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
151 #define VIEWER_MENU BUTTON_MODE
152 #define VIEWER_AUTOSCROLL BUTTON_SELECT
154 /* iAudio X5 keys */
155 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
156 #define VIEWER_QUIT BUTTON_POWER
157 #define VIEWER_PAGE_UP BUTTON_UP
158 #define VIEWER_PAGE_DOWN BUTTON_DOWN
159 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
160 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
161 #define VIEWER_MENU BUTTON_SELECT
162 #define VIEWER_AUTOSCROLL BUTTON_PLAY
164 /* GIGABEAT keys */
165 #elif CONFIG_KEYPAD == GIGABEAT_PAD
166 #define VIEWER_QUIT BUTTON_POWER
167 #define VIEWER_PAGE_UP BUTTON_UP
168 #define VIEWER_PAGE_DOWN BUTTON_DOWN
169 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
170 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
171 #define VIEWER_MENU BUTTON_MENU
172 #define VIEWER_AUTOSCROLL BUTTON_A
174 /* Sansa E200 keys */
175 #elif CONFIG_KEYPAD == SANSA_E200_PAD
176 #define VIEWER_QUIT BUTTON_POWER
177 #define VIEWER_PAGE_UP BUTTON_UP
178 #define VIEWER_PAGE_DOWN BUTTON_DOWN
179 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
180 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
181 #define VIEWER_MENU BUTTON_SELECT
182 #define VIEWER_AUTOSCROLL BUTTON_REC
183 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
184 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
186 /* Sansa C200 keys */
187 #elif CONFIG_KEYPAD == SANSA_C200_PAD
188 #define VIEWER_QUIT BUTTON_POWER
189 #define VIEWER_PAGE_UP BUTTON_VOL_UP
190 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
191 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
192 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
193 #define VIEWER_MENU BUTTON_SELECT
194 #define VIEWER_AUTOSCROLL BUTTON_REC
195 #define VIEWER_LINE_UP BUTTON_UP
196 #define VIEWER_LINE_DOWN BUTTON_DOWN
198 /* iriver H10 keys */
199 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
200 #define VIEWER_QUIT BUTTON_POWER
201 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
202 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
203 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
204 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
205 #define VIEWER_MENU BUTTON_REW
206 #define VIEWER_AUTOSCROLL BUTTON_PLAY
208 #elif CONFIG_KEYPAD == MROBE500_PAD
209 #define VIEWER_QUIT BUTTON_POWER
210 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
211 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
212 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
213 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
214 #define VIEWER_MENU BUTTON_RC_HEART
215 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
217 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
218 #define VIEWER_QUIT BUTTON_BACK
219 #define VIEWER_PAGE_UP BUTTON_VOL_UP
220 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
221 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
222 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
223 #define VIEWER_MENU BUTTON_MENU
224 #define VIEWER_AUTOSCROLL BUTTON_PLAY
226 #endif
228 /* stuff for the bookmarking */
229 struct bookmarked_file_info {
230 long file_position;
231 int top_ptr_pos;
232 char filename[MAX_PATH];
235 struct bookmark_file_data {
236 signed int bookmarked_files_count;
237 struct bookmarked_file_info bookmarks[];
240 struct preferences {
241 enum {
242 WRAP=0,
243 CHOP,
244 } word_mode;
246 enum {
247 NORMAL=0,
248 JOIN,
249 EXPAND,
250 REFLOW, /* won't be set on charcell LCD, must be last */
251 } line_mode;
253 enum {
254 NARROW=0,
255 WIDE,
256 } view_mode;
258 enum {
259 ISO_8859_1=0,
260 ISO_8859_7,
261 ISO_8859_8,
262 CP1251,
263 ISO_8859_11,
264 ISO_8859_6,
265 ISO_8859_9,
266 ISO_8859_2,
267 CP1250,
268 SJIS,
269 GB2312,
270 KSX1001,
271 BIG5,
272 UTF8,
273 ENCODINGS
274 } encoding; /* FIXME: What should default encoding be? */
276 #ifdef HAVE_LCD_BITMAP
277 enum {
278 SB_OFF=0,
279 SB_ON,
280 } scrollbar_mode;
281 bool need_scrollbar;
283 enum {
284 NO_OVERLAP=0,
285 OVERLAP,
286 } page_mode;
287 #endif /* HAVE_LCD_BITMAP */
289 enum {
290 PAGE=0,
291 LINE,
292 } scroll_mode;
294 int autoscroll_speed;
298 struct preferences prefs;
299 struct preferences old_prefs;
301 static unsigned char *buffer;
302 static long buffer_size;
303 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
304 static int display_columns; /* number of (pixel) columns on the display */
305 static int display_lines; /* number of lines on the display */
306 static int draw_columns; /* number of (pixel) columns available for text */
307 static int par_indent_spaces; /* number of spaces to indent first paragraph */
308 static int fd;
309 static char *file_name;
310 static long file_size;
311 static long start_position; /* position in the file after the viewer is started */
312 static bool mac_text;
313 static long file_pos; /* Position of the top of the buffer in the file */
314 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
315 static int max_line_len;
316 static unsigned char *screen_top_ptr;
317 static unsigned char *next_screen_ptr;
318 static unsigned char *next_screen_to_draw_ptr;
319 static unsigned char *next_line_ptr;
320 static struct plugin_api* rb;
321 #ifdef HAVE_LCD_BITMAP
322 static struct font *pf;
323 #endif
326 int glyph_width(int ch)
328 if (ch == 0)
329 ch = ' ';
331 #ifdef HAVE_LCD_BITMAP
332 return rb->font_get_width(pf, ch);
333 #else
334 return 1;
335 #endif
338 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
340 unsigned char utf8_tmp[6];
341 int count;
343 if (prefs.encoding == UTF8)
344 return (unsigned char*)rb->utf8decode(str, ch);
346 count = BUFFER_OOB(str+2)? 1:2;
347 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
348 rb->utf8decode(utf8_tmp, ch);
350 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
351 return (unsigned char*)str+1;
352 else
353 return (unsigned char*)str+2;
356 bool done = false;
357 int col = 0;
359 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
360 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
361 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
362 static unsigned char* crop_at_width(const unsigned char* p)
364 int k,width;
365 unsigned short ch;
366 const unsigned char *oldp = p;
368 k=width=0;
370 while (LINE_IS_NOT_FULL) {
371 oldp = p;
372 p = get_ucs(p, &ch);
373 ADVANCE_COUNTERS(ch);
376 return (unsigned char*)oldp;
379 static unsigned char* find_first_feed(const unsigned char* p, int size)
381 int i;
383 for (i=0; i < size; i++)
384 if (p[i] == 0)
385 return (unsigned char*) p+i;
387 return NULL;
390 static unsigned char* find_last_feed(const unsigned char* p, int size)
392 int i;
394 for (i=size-1; i>=0; i--)
395 if (p[i] == 0)
396 return (unsigned char*) p+i;
398 return NULL;
401 static unsigned char* find_last_space(const unsigned char* p, int size)
403 int i, j, k;
405 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
407 if (!BUFFER_OOB(&p[size]))
408 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
409 if (p[size] == line_break[j])
410 return (unsigned char*) p+size;
412 for (i=size-1; i>=0; i--)
413 for (j=k; j < (int) sizeof(line_break); j++)
415 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
416 if (p[i] == line_break[j])
417 return (unsigned char*) p+i;
420 return NULL;
423 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
425 const unsigned char *next_line = NULL;
426 int size, i, j, k, width, search_len, spaces, newlines;
427 bool first_chars;
428 unsigned char c;
430 if (is_short != NULL)
431 *is_short = true;
433 if BUFFER_OOB(cur_line)
434 return NULL;
436 if (prefs.view_mode == WIDE) {
437 search_len = MAX_WIDTH;
439 else { /* prefs.view_mode == NARROW */
440 search_len = crop_at_width(cur_line) - cur_line;
443 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
445 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
446 /* Need to scan ahead and possibly increase search_len and size,
447 or possibly set next_line at second hard return in a row. */
448 next_line = NULL;
449 first_chars=true;
450 for (j=k=width=spaces=newlines=0; ; j++) {
451 if (BUFFER_OOB(cur_line+j))
452 return NULL;
453 if (LINE_IS_FULL) {
454 size = search_len = j;
455 break;
458 c = cur_line[j];
459 switch (c) {
460 case ' ':
461 if (prefs.line_mode == REFLOW) {
462 if (newlines > 0) {
463 size = j;
464 next_line = cur_line + size;
465 return (unsigned char*) next_line;
467 if (j==0) /* i=1 is intentional */
468 for (i=0; i<par_indent_spaces; i++)
469 ADVANCE_COUNTERS(' ');
471 if (!first_chars) spaces++;
472 break;
474 case 0:
475 if (newlines > 0) {
476 size = j;
477 next_line = cur_line + size - spaces;
478 if (next_line != cur_line)
479 return (unsigned char*) next_line;
480 break;
483 newlines++;
484 size += spaces -1;
485 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
486 return NULL;
487 search_len = size;
488 spaces = first_chars? 0:1;
489 break;
491 default:
492 if (prefs.line_mode==JOIN || newlines>0) {
493 while (spaces) {
494 spaces--;
495 ADVANCE_COUNTERS(' ');
496 if (LINE_IS_FULL) {
497 size = search_len = j;
498 break;
501 newlines=0;
502 } else if (spaces) {
503 /* REFLOW, multiple spaces between words: count only
504 * one. If more are needed, they will be added
505 * while drawing. */
506 search_len = size;
507 spaces=0;
508 ADVANCE_COUNTERS(' ');
509 if (LINE_IS_FULL) {
510 size = search_len = j;
511 break;
514 first_chars = false;
515 ADVANCE_COUNTERS(c);
516 break;
520 else {
521 /* find first hard return */
522 next_line = find_first_feed(cur_line, size);
525 if (next_line == NULL)
526 if (size == search_len) {
527 if (prefs.word_mode == WRAP) /* Find last space */
528 next_line = find_last_space(cur_line, size);
530 if (next_line == NULL)
531 next_line = crop_at_width(cur_line);
532 else
533 if (prefs.word_mode == WRAP)
534 for (i=0;
535 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
536 i++)
537 next_line++;
540 if (prefs.line_mode == EXPAND)
541 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
542 if (next_line[0] == 0)
543 if (next_line != cur_line)
544 return (unsigned char*) next_line;
546 /* If next_line is pointing to a zero, increment it; i.e.,
547 leave the terminator at the end of cur_line. If pointing
548 to a hyphen, increment only if there is room to display
549 the hyphen on current line (won't apply in WIDE mode,
550 since it's guarenteed there won't be room). */
551 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
552 if (next_line[0] == 0)/* ||
553 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
554 next_line++;
556 if (BUFFER_OOB(next_line))
557 return NULL;
559 if (is_short)
560 *is_short = false;
562 return (unsigned char*) next_line;
565 static unsigned char* find_prev_line(const unsigned char* cur_line)
567 const unsigned char *prev_line = NULL;
568 const unsigned char *p;
570 if BUFFER_OOB(cur_line)
571 return NULL;
573 /* To wrap consistently at the same places, we must
574 start with a known hard return, then work downwards.
575 We can either search backwards for a hard return,
576 or simply start wrapping downwards from top of buffer.
577 If current line is not near top of buffer, this is
578 a file with long lines (paragraphs). We would need to
579 read earlier sectors before we could decide how to
580 properly wrap the lines above the current line, but
581 it probably is not worth the disk access. Instead,
582 start with top of buffer and wrap down from there.
583 This may result in some lines wrapping at different
584 points from where they wrap when scrolling down.
585 If buffer is at top of file, start at top of buffer. */
587 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
588 prev_line = p = NULL;
589 else
590 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
591 /* Null means no line feeds in buffer above current line. */
593 if (prev_line == NULL)
594 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
595 prev_line = p = buffer;
596 /* (else return NULL and read previous block) */
598 /* Wrap downwards until too far, then use the one before. */
599 while (p < cur_line && p != NULL) {
600 prev_line = p;
601 p = find_next_line(prev_line, NULL);
604 if (BUFFER_OOB(prev_line))
605 return NULL;
607 return (unsigned char*) prev_line;
610 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
612 /* Read from file and preprocess the data */
613 /* To minimize disk access, always read on sector boundaries */
614 unsigned numread, i;
615 bool found_CR = false;
617 rb->lseek(fd, pos, SEEK_SET);
618 numread = rb->read(fd, buf, size);
619 rb->button_clear_queue(); /* clear button queue */
621 for(i = 0; i < numread; i++) {
622 switch(buf[i]) {
623 case '\r':
624 if (mac_text) {
625 buf[i] = 0;
627 else {
628 buf[i] = ' ';
629 found_CR = true;
631 break;
633 case '\n':
634 buf[i] = 0;
635 found_CR = false;
636 break;
638 case 0: /* No break between case 0 and default, intentionally */
639 buf[i] = ' ';
640 default:
641 if (found_CR) {
642 buf[i - 1] = 0;
643 found_CR = false;
644 mac_text = true;
646 break;
651 static int read_and_synch(int direction)
653 /* Read next (or prev) block, and reposition global pointers. */
654 /* direction: 1 for down (i.e., further into file), -1 for up */
655 int move_size, move_vector, offset;
656 unsigned char *fill_buf;
658 if (direction == -1) /* up */ {
659 move_size = SMALL_BLOCK_SIZE;
660 offset = 0;
661 fill_buf = TOP_SECTOR;
662 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
663 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
665 else /* down */ {
666 if (prefs.view_mode == WIDE) {
667 /* WIDE mode needs more buffer so we have to read smaller blocks */
668 move_size = SMALL_BLOCK_SIZE;
669 offset = LARGE_BLOCK_SIZE;
670 fill_buf = BOTTOM_SECTOR;
671 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
672 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
674 else {
675 move_size = LARGE_BLOCK_SIZE;
676 offset = SMALL_BLOCK_SIZE;
677 fill_buf = MID_SECTOR;
678 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
681 move_vector = direction * move_size;
682 screen_top_ptr -= move_vector;
683 file_pos += move_vector;
684 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
685 fill_buffer(file_pos + offset, fill_buf, move_size);
686 return move_vector;
689 static void viewer_scroll_up(void)
691 unsigned char *p;
693 p = find_prev_line(screen_top_ptr);
694 if (p == NULL && !BUFFER_BOF()) {
695 read_and_synch(-1);
696 p = find_prev_line(screen_top_ptr);
698 if (p != NULL)
699 screen_top_ptr = p;
702 static void viewer_scroll_down(void)
704 if (next_screen_ptr != NULL)
705 screen_top_ptr = next_line_ptr;
708 #ifdef HAVE_LCD_BITMAP
709 static void viewer_scrollbar(void) {
710 int items, min_shown, max_shown;
712 items = (int) file_size; /* (SH1 int is same as long) */
713 min_shown = (int) file_pos + (screen_top_ptr - buffer);
715 if (next_screen_ptr == NULL)
716 max_shown = items;
717 else
718 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
720 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
721 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
723 #endif
725 static void viewer_draw(int col)
727 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
728 int width, extra_spaces, indent_spaces, spaces_per_word;
729 bool multiple_spacing, line_is_short;
730 unsigned short ch;
731 unsigned char *str, *oldstr;
732 unsigned char *line_begin;
733 unsigned char *line_end;
734 unsigned char c;
735 unsigned char scratch_buffer[MAX_COLUMNS + 1];
736 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
737 unsigned char *endptr;
739 /* If col==-1 do all calculations but don't display */
740 if (col != -1) {
741 #ifdef HAVE_LCD_BITMAP
742 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
743 #else
744 left_col = 0;
745 #endif
746 rb->lcd_clear_display();
748 max_line_len = 0;
749 line_begin = line_end = screen_top_ptr;
751 for (i = 0; i < display_lines; i++) {
752 if (BUFFER_OOB(line_end))
753 break; /* Happens after display last line at BUFFER_EOF() */
755 line_begin = line_end;
756 line_end = find_next_line(line_begin, &line_is_short);
758 if (line_end == NULL) {
759 if (BUFFER_EOF()) {
760 if (i < display_lines - 1 && !BUFFER_BOF()) {
761 if (col != -1)
762 rb->lcd_clear_display();
764 for (; i < display_lines - 1; i++)
765 viewer_scroll_up();
767 line_begin = line_end = screen_top_ptr;
768 i = -1;
769 continue;
771 else {
772 line_end = buffer_end;
775 else {
776 resynch_move = read_and_synch(1); /* Read block & move ptrs */
777 line_begin -= resynch_move;
778 if (i > 0)
779 next_line_ptr -= resynch_move;
781 line_end = find_next_line(line_begin, NULL);
782 if (line_end == NULL) /* Should not really happen */
783 break;
786 line_len = line_end - line_begin;
788 /* calculate line_len */
789 str = oldstr = line_begin;
790 j = -1;
791 while (str < line_end) {
792 oldstr = str;
793 str = crop_at_width(str);
794 j++;
796 line_width = j*draw_columns;
797 while (oldstr < line_end) {
798 oldstr = get_ucs(oldstr, &ch);
799 line_width += glyph_width(ch);
802 if (prefs.line_mode == JOIN) {
803 if (line_begin[0] == 0) {
804 line_begin++;
805 if (prefs.word_mode == CHOP)
806 line_end++;
807 else
808 line_len--;
810 for (j=k=spaces=0; j < line_len; j++) {
811 if (k == MAX_COLUMNS)
812 break;
814 c = line_begin[j];
815 switch (c) {
816 case ' ':
817 spaces++;
818 break;
819 case 0:
820 spaces = 0;
821 scratch_buffer[k++] = ' ';
822 break;
823 default:
824 while (spaces) {
825 spaces--;
826 scratch_buffer[k++] = ' ';
827 if (k == MAX_COLUMNS - 1)
828 break;
830 scratch_buffer[k++] = c;
831 break;
834 if (col != -1) {
835 scratch_buffer[k] = 0;
836 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
837 prefs.encoding, draw_columns/glyph_width('i'));
838 *endptr = 0;
841 else if (prefs.line_mode == REFLOW) {
842 if (line_begin[0] == 0) {
843 line_begin++;
844 if (prefs.word_mode == CHOP)
845 line_end++;
846 else
847 line_len--;
850 indent_spaces = 0;
851 if (!line_is_short) {
852 multiple_spacing = false;
853 width=spaces=0;
854 for (str = line_begin; str < line_end; ) {
855 str = get_ucs(str, &ch);
856 switch (ch) {
857 case ' ':
858 case 0:
859 if ((str == line_begin) && (prefs.word_mode==WRAP))
860 /* special case: indent the paragraph,
861 * don't count spaces */
862 indent_spaces = par_indent_spaces;
863 else if (!multiple_spacing)
864 spaces++;
865 multiple_spacing = true;
866 break;
867 default:
868 multiple_spacing = false;
869 width += glyph_width(ch);
870 break;
873 if (multiple_spacing) spaces--;
875 if (spaces) {
876 /* total number of spaces to insert between words */
877 extra_spaces = (draw_columns-width)/glyph_width(' ')
878 - indent_spaces;
879 /* number of spaces between each word*/
880 spaces_per_word = extra_spaces / spaces;
881 /* number of words with n+1 spaces (to fill up) */
882 extra_spaces = extra_spaces % spaces;
883 if (spaces_per_word > 2) { /* too much spacing is awful */
884 spaces_per_word = 3;
885 extra_spaces = 0;
887 } else { /* this doesn't matter much... no spaces anyway */
888 spaces_per_word = extra_spaces = 0;
890 } else { /* end of a paragraph: don't fill line */
891 spaces_per_word = 1;
892 extra_spaces = 0;
895 multiple_spacing = false;
896 for (j=k=spaces=0; j < line_len; j++) {
897 if (k == MAX_COLUMNS)
898 break;
900 c = line_begin[j];
901 switch (c) {
902 case ' ':
903 case 0:
904 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
905 for (j=0; j<par_indent_spaces; j++)
906 scratch_buffer[k++] = ' ';
907 j=0;
909 else if (!multiple_spacing) {
910 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
911 scratch_buffer[k++] = ' ';
912 spaces++;
914 multiple_spacing = true;
915 break;
916 default:
917 scratch_buffer[k++] = c;
918 multiple_spacing = false;
919 break;
923 if (col != -1) {
924 scratch_buffer[k] = 0;
925 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
926 prefs.encoding, k-col);
927 *endptr = 0;
930 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
931 if (col != -1)
932 if (line_width > col) {
933 str = oldstr = line_begin;
934 k = col;
935 width = 0;
936 while( (width<draw_columns) && (oldstr<line_end) )
938 oldstr = get_ucs(oldstr, &ch);
939 if (k > 0) {
940 k -= glyph_width(ch);
941 line_begin = oldstr;
942 } else {
943 width += glyph_width(ch);
947 if(prefs.view_mode==WIDE)
948 endptr = rb->iso_decode(line_begin, utf8_buffer,
949 prefs.encoding, oldstr-line_begin);
950 else
951 endptr = rb->iso_decode(line_begin, utf8_buffer,
952 prefs.encoding, line_end-line_begin);
953 *endptr = 0;
956 if (col != -1 && line_width > col)
957 #ifdef HAVE_LCD_BITMAP
958 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
959 #else
960 rb->lcd_puts(left_col, i, utf8_buffer);
961 #endif
962 if (line_width > max_line_len)
963 max_line_len = line_width;
965 if (i == 0)
966 next_line_ptr = line_end;
968 next_screen_ptr = line_end;
969 if (BUFFER_OOB(next_screen_ptr))
970 next_screen_ptr = NULL;
972 #ifdef HAVE_LCD_BITMAP
973 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
975 if (prefs.need_scrollbar)
976 viewer_scrollbar();
977 #else
978 next_screen_to_draw_ptr = next_screen_ptr;
979 #endif
981 if (col != -1)
982 rb->lcd_update();
985 static void viewer_top(void)
987 /* Read top of file into buffer
988 and point screen pointer to top */
989 if (file_pos != 0)
991 file_pos = 0;
992 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
993 fill_buffer(0, buffer, buffer_size);
996 screen_top_ptr = buffer;
999 static void viewer_bottom(void)
1001 /* Read bottom of file into buffer
1002 and point screen pointer to bottom */
1003 long last_sectors;
1005 if (file_size > buffer_size) {
1006 /* Find last buffer in file, round up to next sector boundary */
1007 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1008 last_sectors /= SMALL_BLOCK_SIZE;
1009 last_sectors *= SMALL_BLOCK_SIZE;
1011 else {
1012 last_sectors = 0;
1015 if (file_pos != last_sectors)
1017 file_pos = last_sectors;
1018 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1019 fill_buffer(last_sectors, buffer, buffer_size);
1022 screen_top_ptr = buffer_end-1;
1025 #ifdef HAVE_LCD_BITMAP
1026 static void init_need_scrollbar(void) {
1027 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1028 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1029 viewer_draw(-1);
1030 prefs.need_scrollbar = NEED_SCROLLBAR();
1031 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1032 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1034 #else
1035 #define init_need_scrollbar()
1036 #endif
1038 static bool viewer_init(void)
1040 #ifdef HAVE_LCD_BITMAP
1042 pf = rb->font_get(FONT_UI);
1044 display_lines = LCD_HEIGHT / pf->height;
1045 draw_columns = display_columns = LCD_WIDTH;
1046 #else
1047 /* REAL fixed pitch :) all chars use up 1 cell */
1048 display_lines = 2;
1049 draw_columns = display_columns = 11;
1050 par_indent_spaces = 2;
1051 #endif
1053 fd = rb->open(file_name, O_RDONLY);
1054 if (fd==-1)
1055 return false;
1057 file_size = rb->filesize(fd);
1058 if (file_size==-1)
1059 return false;
1061 /* Init mac_text value used in processing buffer */
1062 mac_text = false;
1064 return true;
1067 static void viewer_default_settings(void)
1069 prefs.word_mode = WRAP;
1070 prefs.line_mode = NORMAL;
1071 prefs.view_mode = NARROW;
1072 prefs.scroll_mode = PAGE;
1073 #ifdef HAVE_LCD_BITMAP
1074 prefs.page_mode = NO_OVERLAP;
1075 prefs.scrollbar_mode = SB_OFF;
1076 #endif
1077 prefs.autoscroll_speed = 1;
1078 /* Set codepage to system default */
1079 prefs.encoding = rb->global_settings->default_codepage;
1082 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1084 int settings_fd, i;
1085 struct bookmark_file_data *data;
1086 struct bookmarked_file_info this_bookmark;
1088 /* read settings file */
1089 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1090 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1092 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1093 rb->close(settings_fd);
1095 else
1097 /* load default settings if there is no settings file */
1098 viewer_default_settings();
1101 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1103 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1104 data->bookmarked_files_count = 0;
1106 /* read bookmarks if file exists */
1107 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1108 if (settings_fd >= 0)
1110 /* figure out how many items to read */
1111 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1112 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1113 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1114 rb->read(settings_fd, data->bookmarks,
1115 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1116 rb->close(settings_fd);
1119 file_pos = 0;
1120 screen_top_ptr = buffer;
1122 /* check if current file is in list */
1123 for (i=0; i < data->bookmarked_files_count; i++)
1125 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1127 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1128 int screen_top = screen_pos % buffer_size;
1129 file_pos = screen_pos - screen_top;
1130 screen_top_ptr = buffer + screen_top;
1131 break;
1135 this_bookmark.file_position = file_pos;
1136 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1138 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1139 rb->strcpy(this_bookmark.filename,file_name);
1141 /* prevent potential slot overflow */
1142 if (i >= data->bookmarked_files_count)
1144 if (i < MAX_BOOKMARKED_FILES)
1145 data->bookmarked_files_count++;
1146 else
1147 i = MAX_BOOKMARKED_FILES-1;
1150 /* write bookmark file with spare slot in first position
1151 to be filled in by viewer_save_settings */
1152 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1153 if (settings_fd >=0 )
1155 /* write count */
1156 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1158 /* write the current bookmark */
1159 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1161 /* write everything that was before this bookmark */
1162 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1164 rb->close(settings_fd);
1167 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1169 if (BUFFER_OOB(screen_top_ptr))
1171 screen_top_ptr = buffer;
1174 fill_buffer(file_pos, buffer, buffer_size);
1176 /* remember the current position */
1177 start_position = file_pos + screen_top_ptr - buffer;
1179 init_need_scrollbar();
1182 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1184 int settings_fd;
1186 /* save the viewer settings if they have been changed */
1187 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1189 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1191 if (settings_fd >= 0 )
1193 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1194 rb->close(settings_fd);
1198 /* save the bookmark if the position has changed */
1199 if (file_pos + screen_top_ptr - buffer != start_position)
1201 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1203 if (settings_fd >= 0 )
1205 struct bookmarked_file_info b;
1206 b.file_position = file_pos + screen_top_ptr - buffer;
1207 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1208 rb->memset(&b.filename[0],0,MAX_PATH);
1209 rb->strcpy(b.filename,file_name);
1210 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1211 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1212 rb->close(settings_fd);
1217 static void viewer_exit(void *parameter)
1219 (void)parameter;
1221 viewer_save_settings();
1222 rb->close(fd);
1225 static int col_limit(int col)
1227 if (col < 0)
1228 col = 0;
1229 else
1230 if (col > max_line_len - 2*glyph_width('o'))
1231 col = max_line_len - 2*glyph_width('o');
1233 return col;
1236 /* settings helper functions */
1238 static bool encoding_setting(void)
1240 static const struct opt_items names[] = {
1241 {"ISO-8859-1", -1},
1242 {"ISO-8859-7", -1},
1243 {"ISO-8859-8", -1},
1244 {"CP1251", -1},
1245 {"ISO-8859-11", -1},
1246 {"ISO-8859-6", -1},
1247 {"ISO-8859-9", -1},
1248 {"ISO-8859-2", -1},
1249 {"CP1250", -1},
1250 {"SJIS", -1},
1251 {"GB-2312", -1},
1252 {"KSX-1001", -1},
1253 {"BIG5", -1},
1254 {"UTF-8", -1},
1257 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1258 sizeof(names) / sizeof(names[0]), NULL);
1261 static bool word_wrap_setting(void)
1263 static const struct opt_items names[] = {
1264 {"On", -1},
1265 {"Off (Chop Words)", -1},
1268 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1269 names, 2, NULL);
1272 static bool line_mode_setting(void)
1274 static const struct opt_items names[] = {
1275 {"Normal", -1},
1276 {"Join Lines", -1},
1277 {"Expand Lines", -1},
1278 #ifdef HAVE_LCD_BITMAP
1279 {"Reflow Lines", -1},
1280 #endif
1283 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1284 sizeof(names) / sizeof(names[0]), NULL);
1287 static bool view_mode_setting(void)
1289 static const struct opt_items names[] = {
1290 {"No (Narrow)", -1},
1291 {"Yes", -1},
1293 bool ret;
1294 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1295 names , 2, NULL);
1296 if (prefs.view_mode == NARROW)
1297 col = 0;
1298 return ret;
1301 static bool scroll_mode_setting(void)
1303 static const struct opt_items names[] = {
1304 {"Scroll by Page", -1},
1305 {"Scroll by Line", -1},
1308 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1309 names, 2, NULL);
1312 #ifdef HAVE_LCD_BITMAP
1313 static bool page_mode_setting(void)
1315 static const struct opt_items names[] = {
1316 {"No", -1},
1317 {"Yes", -1},
1320 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1321 names, 2, NULL);
1324 static bool scrollbar_setting(void)
1326 static const struct opt_items names[] = {
1327 {"Off", -1},
1328 {"On", -1}
1331 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1332 names, 2, NULL);
1334 #endif
1336 static bool autoscroll_speed_setting(void)
1338 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1339 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1342 static bool viewer_options_menu(void)
1344 int m;
1345 bool result;
1347 static const struct menu_item items[] = {
1348 {"Encoding", encoding_setting },
1349 {"Word Wrap", word_wrap_setting },
1350 {"Line Mode", line_mode_setting },
1351 {"Wide View", view_mode_setting },
1352 #ifdef HAVE_LCD_BITMAP
1353 {"Show Scrollbar", scrollbar_setting },
1354 {"Overlap Pages", page_mode_setting },
1355 #endif
1356 {"Scroll Mode", scroll_mode_setting},
1357 {"Auto-Scroll Speed", autoscroll_speed_setting },
1359 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1360 NULL, NULL, NULL, NULL);
1362 result = menu_run(m);
1363 menu_exit(m);
1364 #ifdef HAVE_LCD_BITMAP
1365 rb->lcd_setmargins(0,0);
1367 /* Show-scrollbar mode for current view-width mode */
1368 init_need_scrollbar();
1369 #endif
1370 return result;
1373 static void viewer_menu(void)
1375 int m;
1376 int result;
1377 static const struct menu_item items[] = {
1378 {"Quit", NULL },
1379 {"Viewer Options", NULL },
1380 {"Show Playback Menu", NULL },
1381 {"Return", NULL },
1384 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1385 result=menu_show(m);
1386 switch (result)
1388 case 0: /* quit */
1389 menu_exit(m);
1390 viewer_exit(NULL);
1391 done = true;
1392 break;
1393 case 1: /* change settings */
1394 done = viewer_options_menu();
1395 break;
1396 case 2: /* playback control */
1397 playback_control(rb);
1398 break;
1399 case 3: /* return */
1400 break;
1402 menu_exit(m);
1403 #ifdef HAVE_LCD_BITMAP
1404 rb->lcd_setmargins(0,0);
1405 #endif
1406 viewer_draw(col);
1409 enum plugin_status plugin_start(struct plugin_api* api, void* file)
1411 int button, i, ok;
1412 int lastbutton = BUTTON_NONE;
1413 bool autoscroll = false;
1414 long old_tick;
1416 rb = api;
1417 old_tick = *rb->current_tick;
1419 /* get the plugin buffer */
1420 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1422 if (!file)
1423 return PLUGIN_ERROR;
1425 file_name = file;
1426 ok = viewer_init();
1427 if (!ok) {
1428 rb->splash(HZ, "Error opening file.");
1429 return PLUGIN_ERROR;
1432 viewer_load_settings(); /* load the preferences and bookmark */
1434 #if LCD_DEPTH > 1
1435 rb->lcd_set_backdrop(NULL);
1436 #endif
1438 viewer_draw(col);
1440 while (!done) {
1442 if(autoscroll)
1444 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1446 viewer_scroll_down();
1447 viewer_draw(col);
1448 old_tick = *rb->current_tick;
1452 button = rb->button_get_w_tmo(HZ/10);
1453 switch (button) {
1454 case VIEWER_MENU:
1455 viewer_menu();
1456 break;
1458 case VIEWER_AUTOSCROLL:
1459 #ifdef VIEWER_AUTOSCROLL_PRE
1460 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1461 break;
1462 #endif
1463 autoscroll = !autoscroll;
1464 break;
1466 case VIEWER_PAGE_UP:
1467 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1468 if (prefs.scroll_mode == PAGE)
1470 /* Page up */
1471 #ifdef HAVE_LCD_BITMAP
1472 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1473 #else
1474 for (i = 0; i < display_lines; i++)
1475 #endif
1476 viewer_scroll_up();
1478 else
1479 viewer_scroll_up();
1480 old_tick = *rb->current_tick;
1481 viewer_draw(col);
1482 break;
1484 case VIEWER_PAGE_DOWN:
1485 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1486 if (prefs.scroll_mode == PAGE)
1488 /* Page down */
1489 if (next_screen_ptr != NULL)
1490 screen_top_ptr = next_screen_to_draw_ptr;
1492 else
1493 viewer_scroll_down();
1494 old_tick = *rb->current_tick;
1495 viewer_draw(col);
1496 break;
1498 case VIEWER_SCREEN_LEFT:
1499 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1500 if (prefs.view_mode == WIDE) {
1501 /* Screen left */
1502 col -= draw_columns;
1503 col = col_limit(col);
1505 else { /* prefs.view_mode == NARROW */
1506 /* Top of file */
1507 viewer_top();
1510 viewer_draw(col);
1511 break;
1513 case VIEWER_SCREEN_RIGHT:
1514 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1515 if (prefs.view_mode == WIDE) {
1516 /* Screen right */
1517 col += draw_columns;
1518 col = col_limit(col);
1520 else { /* prefs.view_mode == NARROW */
1521 /* Bottom of file */
1522 viewer_bottom();
1525 viewer_draw(col);
1526 break;
1528 #ifdef VIEWER_LINE_UP
1529 case VIEWER_LINE_UP:
1530 case VIEWER_LINE_UP | BUTTON_REPEAT:
1531 /* Scroll up one line */
1532 viewer_scroll_up();
1533 old_tick = *rb->current_tick;
1534 viewer_draw(col);
1535 break;
1537 case VIEWER_LINE_DOWN:
1538 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1539 /* Scroll down one line */
1540 if (next_screen_ptr != NULL)
1541 screen_top_ptr = next_line_ptr;
1542 old_tick = *rb->current_tick;
1543 viewer_draw(col);
1544 break;
1545 #endif
1546 #ifdef VIEWER_COLUMN_LEFT
1547 case VIEWER_COLUMN_LEFT:
1548 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1549 if (prefs.view_mode == WIDE) {
1550 /* Scroll left one column */
1551 col -= glyph_width('o');
1552 col = col_limit(col);
1553 viewer_draw(col);
1555 break;
1557 case VIEWER_COLUMN_RIGHT:
1558 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1559 if (prefs.view_mode == WIDE) {
1560 /* Scroll right one column */
1561 col += glyph_width('o');
1562 col = col_limit(col);
1563 viewer_draw(col);
1565 break;
1566 #endif
1568 #ifdef VIEWER_RC_QUIT
1569 case VIEWER_RC_QUIT:
1570 #endif
1571 case VIEWER_QUIT:
1572 viewer_exit(NULL);
1573 done = true;
1574 break;
1576 default:
1577 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1578 == SYS_USB_CONNECTED)
1579 return PLUGIN_USB_CONNECTED;
1580 break;
1582 if (button != BUTTON_NONE)
1584 lastbutton = button;
1585 rb->yield();
1588 return PLUGIN_OK;