make the first bunch of plugins compile for the mrobe. an earlier commit broke the...
[Rockbox.git] / apps / plugins / viewer.c
blobf0b8e3ff365babdfe94dc93a9cfaac679f04d0c1
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 BUFFER_SIZE 0x3000 /* 12k: Mem reserved for buffered file data */
37 #define TOP_SECTOR buffer
38 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
39 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
40 #define SCROLLBAR_WIDTH 6
42 #define MAX_BOOKMARKED_FILES (((signed)BUFFER_SIZE/(signed)sizeof(struct bookmarked_file_info))-1)
44 /* Out-Of-Bounds test for any pointer to data in the buffer */
45 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
47 /* Does the buffer contain the beginning of the file? */
48 #define BUFFER_BOF() (file_pos==0)
50 /* Does the buffer contain the end of the file? */
51 #define BUFFER_EOF() (file_size-file_pos <= BUFFER_SIZE)
53 /* Formula for the endpoint address outside of buffer data */
54 #define BUFFER_END() \
55 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+BUFFER_SIZE))
57 /* Is the entire file being shown in one screen? */
58 #define ONE_SCREEN_FITS_ALL() \
59 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
61 /* Is a scrollbar called for on the current screen? */
62 #define NEED_SCROLLBAR() \
63 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
65 /* variable button definitions */
67 /* Recorder keys */
68 #if CONFIG_KEYPAD == RECORDER_PAD
69 #define VIEWER_QUIT BUTTON_OFF
70 #define VIEWER_PAGE_UP BUTTON_UP
71 #define VIEWER_PAGE_DOWN BUTTON_DOWN
72 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
73 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
74 #define VIEWER_MENU BUTTON_F1
75 #define VIEWER_AUTOSCROLL BUTTON_PLAY
76 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
77 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
78 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
79 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
81 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
82 #define VIEWER_QUIT BUTTON_OFF
83 #define VIEWER_PAGE_UP BUTTON_UP
84 #define VIEWER_PAGE_DOWN BUTTON_DOWN
85 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
86 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
87 #define VIEWER_MENU BUTTON_F1
88 #define VIEWER_AUTOSCROLL BUTTON_SELECT
89 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
90 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
91 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
92 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
94 /* Ondio keys */
95 #elif CONFIG_KEYPAD == ONDIO_PAD
96 #define VIEWER_QUIT BUTTON_OFF
97 #define VIEWER_PAGE_UP BUTTON_UP
98 #define VIEWER_PAGE_DOWN BUTTON_DOWN
99 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
100 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
101 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
102 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
103 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
105 /* Player keys */
106 #elif CONFIG_KEYPAD == PLAYER_PAD
107 #define VIEWER_QUIT BUTTON_STOP
108 #define VIEWER_PAGE_UP BUTTON_LEFT
109 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
110 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
111 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
112 #define VIEWER_MENU BUTTON_MENU
113 #define VIEWER_AUTOSCROLL BUTTON_PLAY
115 /* iRiver H1x0 && H3x0 keys */
116 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
117 (CONFIG_KEYPAD == IRIVER_H300_PAD)
118 #define VIEWER_QUIT BUTTON_OFF
119 #define VIEWER_PAGE_UP BUTTON_UP
120 #define VIEWER_PAGE_DOWN BUTTON_DOWN
121 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
122 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
123 #define VIEWER_MENU BUTTON_MODE
124 #define VIEWER_AUTOSCROLL BUTTON_SELECT
125 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
126 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
127 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
128 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
130 #define VIEWER_RC_QUIT BUTTON_RC_STOP
132 /* iPods */
133 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
135 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
136 #define VIEWER_QUIT_PRE BUTTON_SELECT
137 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
138 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
139 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
140 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
141 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
142 #define VIEWER_MENU BUTTON_MENU
143 #define VIEWER_AUTOSCROLL BUTTON_PLAY
145 /* iFP7xx keys */
146 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
147 #define VIEWER_QUIT BUTTON_PLAY
148 #define VIEWER_PAGE_UP BUTTON_UP
149 #define VIEWER_PAGE_DOWN BUTTON_DOWN
150 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
151 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
152 #define VIEWER_MENU BUTTON_MODE
153 #define VIEWER_AUTOSCROLL BUTTON_SELECT
155 /* iAudio X5 keys */
156 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
157 #define VIEWER_QUIT BUTTON_POWER
158 #define VIEWER_PAGE_UP BUTTON_UP
159 #define VIEWER_PAGE_DOWN BUTTON_DOWN
160 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
161 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
162 #define VIEWER_MENU BUTTON_SELECT
163 #define VIEWER_AUTOSCROLL BUTTON_PLAY
165 /* GIGABEAT keys */
166 #elif CONFIG_KEYPAD == GIGABEAT_PAD
167 #define VIEWER_QUIT BUTTON_POWER
168 #define VIEWER_PAGE_UP BUTTON_UP
169 #define VIEWER_PAGE_DOWN BUTTON_DOWN
170 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
171 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
172 #define VIEWER_MENU BUTTON_MENU
173 #define VIEWER_AUTOSCROLL BUTTON_A
175 /* Sansa E200 keys */
176 #elif CONFIG_KEYPAD == SANSA_E200_PAD
177 #define VIEWER_QUIT BUTTON_POWER
178 #define VIEWER_PAGE_UP BUTTON_UP
179 #define VIEWER_PAGE_DOWN BUTTON_DOWN
180 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
181 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
182 #define VIEWER_MENU BUTTON_SELECT
183 #define VIEWER_AUTOSCROLL BUTTON_REC
184 #define VIEWER_LINE_UP BUTTON_SCROLL_UP
185 #define VIEWER_LINE_DOWN BUTTON_SCROLL_DOWN
187 /* Sansa C200 keys */
188 #elif CONFIG_KEYPAD == SANSA_C200_PAD
189 #define VIEWER_QUIT BUTTON_POWER
190 #define VIEWER_PAGE_UP BUTTON_VOL_UP
191 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
192 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
193 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
194 #define VIEWER_MENU BUTTON_SELECT
195 #define VIEWER_AUTOSCROLL BUTTON_REC
196 #define VIEWER_LINE_UP BUTTON_UP
197 #define VIEWER_LINE_DOWN BUTTON_DOWN
199 /* iriver H10 keys */
200 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
201 #define VIEWER_QUIT BUTTON_POWER
202 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
203 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
204 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
205 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
206 #define VIEWER_MENU BUTTON_REW
207 #define VIEWER_AUTOSCROLL BUTTON_PLAY
209 #elif CONFIG_KEYPAD == MROBE500_PAD
210 #define VIEWER_QUIT BUTTON_POWER
211 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
212 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
213 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
214 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
215 #define VIEWER_MENU BUTTON_RC_HEART
216 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
218 #endif
220 /* stuff for the bookmarking */
221 struct bookmarked_file_info {
222 long file_position;
223 int top_ptr_pos;
224 char filename[MAX_PATH];
227 struct bookmark_file_data {
228 signed int bookmarked_files_count;
229 struct bookmarked_file_info bookmarks[];
232 struct preferences {
233 enum {
234 WRAP=0,
235 CHOP,
236 } word_mode;
238 enum {
239 NORMAL=0,
240 JOIN,
241 EXPAND,
242 REFLOW, /* won't be set on charcell LCD, must be last */
243 } line_mode;
245 enum {
246 NARROW=0,
247 WIDE,
248 } view_mode;
250 enum {
251 ISO_8859_1=0,
252 ISO_8859_7,
253 ISO_8859_8,
254 CP1251,
255 ISO_8859_11,
256 ISO_8859_6,
257 ISO_8859_9,
258 ISO_8859_2,
259 CP1250,
260 SJIS,
261 GB2312,
262 KSX1001,
263 BIG5,
264 UTF8,
265 ENCODINGS
266 } encoding; /* FIXME: What should default encoding be? */
268 #ifdef HAVE_LCD_BITMAP
269 enum {
270 SB_OFF=0,
271 SB_ON,
272 } scrollbar_mode;
273 bool need_scrollbar;
275 enum {
276 NO_OVERLAP=0,
277 OVERLAP,
278 } page_mode;
279 #endif /* HAVE_LCD_BITMAP */
281 enum {
282 PAGE=0,
283 LINE,
284 } scroll_mode;
286 int autoscroll_speed;
288 } prefs;
290 static unsigned char buffer[BUFFER_SIZE + 1];
291 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
292 static int display_columns; /* number of (pixel) columns on the display */
293 static int display_lines; /* number of lines on the display */
294 static int draw_columns; /* number of (pixel) columns available for text */
295 static int par_indent_spaces; /* number of spaces to indent first paragraph */
296 static int fd;
297 static char *file_name;
298 static long file_size;
299 static bool mac_text;
300 static long file_pos; /* Position of the top of the buffer in the file */
301 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
302 static int max_line_len;
303 static unsigned char *screen_top_ptr;
304 static unsigned char *next_screen_ptr;
305 static unsigned char *next_screen_to_draw_ptr;
306 static unsigned char *next_line_ptr;
307 static struct plugin_api* rb;
308 #ifdef HAVE_LCD_BITMAP
309 static struct font *pf;
310 #endif
313 int glyph_width(int ch)
315 if (ch == 0)
316 ch = ' ';
318 #ifdef HAVE_LCD_BITMAP
319 return rb->font_get_width(pf, ch);
320 #else
321 return 1;
322 #endif
325 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
327 unsigned char utf8_tmp[6];
328 int count;
330 if (prefs.encoding == UTF8)
331 return (unsigned char*)rb->utf8decode(str, ch);
333 count = BUFFER_OOB(str+2)? 1:2;
334 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
335 rb->utf8decode(utf8_tmp, ch);
337 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
338 return (unsigned char*)str+1;
339 else
340 return (unsigned char*)str+2;
343 bool done = false;
344 int col = 0;
346 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
347 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
348 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
349 static unsigned char* crop_at_width(const unsigned char* p)
351 int k,width;
352 unsigned short ch;
353 const unsigned char *oldp = p;
355 k=width=0;
357 while (LINE_IS_NOT_FULL) {
358 oldp = p;
359 p = get_ucs(p, &ch);
360 ADVANCE_COUNTERS(ch);
363 return (unsigned char*)oldp;
366 static unsigned char* find_first_feed(const unsigned char* p, int size)
368 int i;
370 for (i=0; i < size; i++)
371 if (p[i] == 0)
372 return (unsigned char*) p+i;
374 return NULL;
377 static unsigned char* find_last_feed(const unsigned char* p, int size)
379 int i;
381 for (i=size-1; i>=0; i--)
382 if (p[i] == 0)
383 return (unsigned char*) p+i;
385 return NULL;
388 static unsigned char* find_last_space(const unsigned char* p, int size)
390 int i, j, k;
392 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
394 if (!BUFFER_OOB(&p[size]))
395 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
396 if (p[size] == line_break[j])
397 return (unsigned char*) p+size;
399 for (i=size-1; i>=0; i--)
400 for (j=k; j < (int) sizeof(line_break); j++)
402 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
403 if (p[i] == line_break[j])
404 return (unsigned char*) p+i;
407 return NULL;
410 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
412 const unsigned char *next_line = NULL;
413 int size, i, j, k, width, search_len, spaces, newlines;
414 bool first_chars;
415 unsigned char c;
417 if (is_short != NULL)
418 *is_short = true;
420 if BUFFER_OOB(cur_line)
421 return NULL;
423 if (prefs.view_mode == WIDE) {
424 search_len = MAX_WIDTH;
426 else { /* prefs.view_mode == NARROW */
427 search_len = crop_at_width(cur_line) - cur_line;
430 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
432 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
433 /* Need to scan ahead and possibly increase search_len and size,
434 or possibly set next_line at second hard return in a row. */
435 next_line = NULL;
436 first_chars=true;
437 for (j=k=width=spaces=newlines=0; ; j++) {
438 if (BUFFER_OOB(cur_line+j))
439 return NULL;
440 if (LINE_IS_FULL) {
441 size = search_len = j;
442 break;
445 c = cur_line[j];
446 switch (c) {
447 case ' ':
448 if (prefs.line_mode == REFLOW) {
449 if (newlines > 0) {
450 size = j;
451 next_line = cur_line + size;
452 return (unsigned char*) next_line;
454 if (j==0) /* i=1 is intentional */
455 for (i=0; i<par_indent_spaces; i++)
456 ADVANCE_COUNTERS(' ');
458 if (!first_chars) spaces++;
459 break;
461 case 0:
462 if (newlines > 0) {
463 size = j;
464 next_line = cur_line + size - spaces;
465 if (next_line != cur_line)
466 return (unsigned char*) next_line;
467 break;
470 newlines++;
471 size += spaces -1;
472 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
473 return NULL;
474 search_len = size;
475 spaces = first_chars? 0:1;
476 break;
478 default:
479 if (prefs.line_mode==JOIN || newlines>0) {
480 while (spaces) {
481 spaces--;
482 ADVANCE_COUNTERS(' ');
483 if (LINE_IS_FULL) {
484 size = search_len = j;
485 break;
488 newlines=0;
489 } else if (spaces) {
490 /* REFLOW, multiple spaces between words: count only
491 * one. If more are needed, they will be added
492 * while drawing. */
493 search_len = size;
494 spaces=0;
495 ADVANCE_COUNTERS(' ');
496 if (LINE_IS_FULL) {
497 size = search_len = j;
498 break;
501 first_chars = false;
502 ADVANCE_COUNTERS(c);
503 break;
507 else {
508 /* find first hard return */
509 next_line = find_first_feed(cur_line, size);
512 if (next_line == NULL)
513 if (size == search_len) {
514 if (prefs.word_mode == WRAP) /* Find last space */
515 next_line = find_last_space(cur_line, size);
517 if (next_line == NULL)
518 next_line = crop_at_width(cur_line);
519 else
520 if (prefs.word_mode == WRAP)
521 for (i=0;
522 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
523 i++)
524 next_line++;
527 if (prefs.line_mode == EXPAND)
528 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
529 if (next_line[0] == 0)
530 if (next_line != cur_line)
531 return (unsigned char*) next_line;
533 /* If next_line is pointing to a zero, increment it; i.e.,
534 leave the terminator at the end of cur_line. If pointing
535 to a hyphen, increment only if there is room to display
536 the hyphen on current line (won't apply in WIDE mode,
537 since it's guarenteed there won't be room). */
538 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
539 if (next_line[0] == 0)/* ||
540 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
541 next_line++;
543 if (BUFFER_OOB(next_line))
544 return NULL;
546 if (is_short)
547 *is_short = false;
549 return (unsigned char*) next_line;
552 static unsigned char* find_prev_line(const unsigned char* cur_line)
554 const unsigned char *prev_line = NULL;
555 const unsigned char *p;
557 if BUFFER_OOB(cur_line)
558 return NULL;
560 /* To wrap consistently at the same places, we must
561 start with a known hard return, then work downwards.
562 We can either search backwards for a hard return,
563 or simply start wrapping downwards from top of buffer.
564 If current line is not near top of buffer, this is
565 a file with long lines (paragraphs). We would need to
566 read earlier sectors before we could decide how to
567 properly wrap the lines above the current line, but
568 it probably is not worth the disk access. Instead,
569 start with top of buffer and wrap down from there.
570 This may result in some lines wrapping at different
571 points from where they wrap when scrolling down.
572 If buffer is at top of file, start at top of buffer. */
574 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
575 prev_line = p = NULL;
576 else
577 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
578 /* Null means no line feeds in buffer above current line. */
580 if (prev_line == NULL)
581 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
582 prev_line = p = buffer;
583 /* (else return NULL and read previous block) */
585 /* Wrap downwards until too far, then use the one before. */
586 while (p < cur_line && p != NULL) {
587 prev_line = p;
588 p = find_next_line(prev_line, NULL);
591 if (BUFFER_OOB(prev_line))
592 return NULL;
594 return (unsigned char*) prev_line;
597 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
599 /* Read from file and preprocess the data */
600 /* To minimize disk access, always read on sector boundaries */
601 unsigned numread, i;
602 bool found_CR = false;
604 rb->lseek(fd, pos, SEEK_SET);
605 numread = rb->read(fd, buf, size);
606 rb->button_clear_queue(); /* clear button queue */
608 for(i = 0; i < numread; i++) {
609 switch(buf[i]) {
610 case '\r':
611 if (mac_text) {
612 buf[i] = 0;
614 else {
615 buf[i] = ' ';
616 found_CR = true;
618 break;
620 case '\n':
621 buf[i] = 0;
622 found_CR = false;
623 break;
625 case 0: /* No break between case 0 and default, intentionally */
626 buf[i] = ' ';
627 default:
628 if (found_CR) {
629 buf[i - 1] = 0;
630 found_CR = false;
631 mac_text = true;
633 break;
638 static int read_and_synch(int direction)
640 /* Read next (or prev) block, and reposition global pointers. */
641 /* direction: 1 for down (i.e., further into file), -1 for up */
642 int move_size, move_vector, offset;
643 unsigned char *fill_buf;
645 if (direction == -1) /* up */ {
646 move_size = SMALL_BLOCK_SIZE;
647 offset = 0;
648 fill_buf = TOP_SECTOR;
649 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
650 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
652 else /* down */ {
653 if (prefs.view_mode == WIDE) {
654 /* WIDE mode needs more buffer so we have to read smaller blocks */
655 move_size = SMALL_BLOCK_SIZE;
656 offset = LARGE_BLOCK_SIZE;
657 fill_buf = BOTTOM_SECTOR;
658 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
659 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
661 else {
662 move_size = LARGE_BLOCK_SIZE;
663 offset = SMALL_BLOCK_SIZE;
664 fill_buf = MID_SECTOR;
665 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
668 move_vector = direction * move_size;
669 screen_top_ptr -= move_vector;
670 file_pos += move_vector;
671 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
672 fill_buffer(file_pos + offset, fill_buf, move_size);
673 return move_vector;
676 static void viewer_scroll_up(void)
678 unsigned char *p;
680 p = find_prev_line(screen_top_ptr);
681 if (p == NULL && !BUFFER_BOF()) {
682 read_and_synch(-1);
683 p = find_prev_line(screen_top_ptr);
685 if (p != NULL)
686 screen_top_ptr = p;
689 static void viewer_scroll_down(void)
691 if (next_screen_ptr != NULL)
692 screen_top_ptr = next_line_ptr;
695 #ifdef HAVE_LCD_BITMAP
696 static void viewer_scrollbar(void) {
697 int items, min_shown, max_shown;
699 items = (int) file_size; /* (SH1 int is same as long) */
700 min_shown = (int) file_pos + (screen_top_ptr - buffer);
702 if (next_screen_ptr == NULL)
703 max_shown = items;
704 else
705 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
707 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
708 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
710 #endif
712 static void viewer_draw(int col)
714 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
715 int width, extra_spaces, indent_spaces, spaces_per_word;
716 bool multiple_spacing, line_is_short;
717 unsigned short ch;
718 unsigned char *str, *oldstr;
719 unsigned char *line_begin;
720 unsigned char *line_end;
721 unsigned char c;
722 unsigned char scratch_buffer[MAX_COLUMNS + 1];
723 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
724 unsigned char *endptr;
726 /* If col==-1 do all calculations but don't display */
727 if (col != -1) {
728 #ifdef HAVE_LCD_BITMAP
729 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
730 #else
731 left_col = 0;
732 #endif
733 rb->lcd_clear_display();
735 max_line_len = 0;
736 line_begin = line_end = screen_top_ptr;
738 for (i = 0; i < display_lines; i++) {
739 if (BUFFER_OOB(line_end))
740 break; /* Happens after display last line at BUFFER_EOF() */
742 line_begin = line_end;
743 line_end = find_next_line(line_begin, &line_is_short);
745 if (line_end == NULL) {
746 if (BUFFER_EOF()) {
747 if (i < display_lines - 1 && !BUFFER_BOF()) {
748 if (col != -1)
749 rb->lcd_clear_display();
751 for (; i < display_lines - 1; i++)
752 viewer_scroll_up();
754 line_begin = line_end = screen_top_ptr;
755 i = -1;
756 continue;
758 else {
759 line_end = buffer_end;
762 else {
763 resynch_move = read_and_synch(1); /* Read block & move ptrs */
764 line_begin -= resynch_move;
765 if (i > 0)
766 next_line_ptr -= resynch_move;
768 line_end = find_next_line(line_begin, NULL);
769 if (line_end == NULL) /* Should not really happen */
770 break;
773 line_len = line_end - line_begin;
775 /* calculate line_len */
776 str = oldstr = line_begin;
777 j = -1;
778 while (str < line_end) {
779 oldstr = str;
780 str = crop_at_width(str);
781 j++;
783 line_width = j*draw_columns;
784 while (oldstr < line_end) {
785 oldstr = get_ucs(oldstr, &ch);
786 line_width += glyph_width(ch);
789 if (prefs.line_mode == JOIN) {
790 if (line_begin[0] == 0) {
791 line_begin++;
792 if (prefs.word_mode == CHOP)
793 line_end++;
794 else
795 line_len--;
797 for (j=k=spaces=0; j < line_len; j++) {
798 if (k == MAX_COLUMNS)
799 break;
801 c = line_begin[j];
802 switch (c) {
803 case ' ':
804 spaces++;
805 break;
806 case 0:
807 spaces = 0;
808 scratch_buffer[k++] = ' ';
809 break;
810 default:
811 while (spaces) {
812 spaces--;
813 scratch_buffer[k++] = ' ';
814 if (k == MAX_COLUMNS - 1)
815 break;
817 scratch_buffer[k++] = c;
818 break;
821 if (col != -1) {
822 scratch_buffer[k] = 0;
823 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
824 prefs.encoding, draw_columns/glyph_width('i'));
825 *endptr = 0;
828 else if (prefs.line_mode == REFLOW) {
829 if (line_begin[0] == 0) {
830 line_begin++;
831 if (prefs.word_mode == CHOP)
832 line_end++;
833 else
834 line_len--;
837 indent_spaces = 0;
838 if (!line_is_short) {
839 multiple_spacing = false;
840 width=spaces=0;
841 for (str = line_begin; str < line_end; ) {
842 str = get_ucs(str, &ch);
843 switch (ch) {
844 case ' ':
845 case 0:
846 if ((str == line_begin) && (prefs.word_mode==WRAP))
847 /* special case: indent the paragraph,
848 * don't count spaces */
849 indent_spaces = par_indent_spaces;
850 else if (!multiple_spacing)
851 spaces++;
852 multiple_spacing = true;
853 break;
854 default:
855 multiple_spacing = false;
856 width += glyph_width(ch);
857 break;
860 if (multiple_spacing) spaces--;
862 if (spaces) {
863 /* total number of spaces to insert between words */
864 extra_spaces = (draw_columns-width)/glyph_width(' ')
865 - indent_spaces;
866 /* number of spaces between each word*/
867 spaces_per_word = extra_spaces / spaces;
868 /* number of words with n+1 spaces (to fill up) */
869 extra_spaces = extra_spaces % spaces;
870 if (spaces_per_word > 2) { /* too much spacing is awful */
871 spaces_per_word = 3;
872 extra_spaces = 0;
874 } else { /* this doesn't matter much... no spaces anyway */
875 spaces_per_word = extra_spaces = 0;
877 } else { /* end of a paragraph: don't fill line */
878 spaces_per_word = 1;
879 extra_spaces = 0;
882 multiple_spacing = false;
883 for (j=k=spaces=0; j < line_len; j++) {
884 if (k == MAX_COLUMNS)
885 break;
887 c = line_begin[j];
888 switch (c) {
889 case ' ':
890 case 0:
891 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
892 for (j=0; j<par_indent_spaces; j++)
893 scratch_buffer[k++] = ' ';
894 j=0;
896 else if (!multiple_spacing) {
897 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
898 scratch_buffer[k++] = ' ';
899 spaces++;
901 multiple_spacing = true;
902 break;
903 default:
904 scratch_buffer[k++] = c;
905 multiple_spacing = false;
906 break;
910 if (col != -1) {
911 scratch_buffer[k] = 0;
912 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
913 prefs.encoding, k-col);
914 *endptr = 0;
917 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
918 if (col != -1)
919 if (line_width > col) {
920 str = oldstr = line_begin;
921 k = col;
922 width = 0;
923 while( (width<draw_columns) && (oldstr<line_end) )
925 oldstr = get_ucs(oldstr, &ch);
926 if (k > 0) {
927 k -= glyph_width(ch);
928 line_begin = oldstr;
929 } else {
930 width += glyph_width(ch);
934 if(prefs.view_mode==WIDE)
935 endptr = rb->iso_decode(line_begin, utf8_buffer,
936 prefs.encoding, oldstr-line_begin);
937 else
938 endptr = rb->iso_decode(line_begin, utf8_buffer,
939 prefs.encoding, line_end-line_begin);
940 *endptr = 0;
943 if (col != -1 && line_width > col)
944 #ifdef HAVE_LCD_BITMAP
945 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
946 #else
947 rb->lcd_puts(left_col, i, utf8_buffer);
948 #endif
949 if (line_width > max_line_len)
950 max_line_len = line_width;
952 if (i == 0)
953 next_line_ptr = line_end;
955 next_screen_ptr = line_end;
956 if (BUFFER_OOB(next_screen_ptr))
957 next_screen_ptr = NULL;
959 #ifdef HAVE_LCD_BITMAP
960 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
962 if (prefs.need_scrollbar)
963 viewer_scrollbar();
964 #else
965 next_screen_to_draw_ptr = next_screen_ptr;
966 #endif
968 if (col != -1)
969 rb->lcd_update();
972 static void viewer_top(void)
974 /* Read top of file into buffer
975 and point screen pointer to top */
976 file_pos = 0;
977 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
978 screen_top_ptr = buffer;
979 fill_buffer(0, buffer, BUFFER_SIZE);
982 static void viewer_bottom(void)
984 /* Read bottom of file into buffer
985 and point screen pointer to bottom */
986 long last_sectors;
988 if (file_size > BUFFER_SIZE) {
989 /* Find last buffer in file, round up to next sector boundary */
990 last_sectors = file_size - BUFFER_SIZE + SMALL_BLOCK_SIZE;
991 last_sectors /= SMALL_BLOCK_SIZE;
992 last_sectors *= SMALL_BLOCK_SIZE;
994 else {
995 last_sectors = 0;
997 file_pos = last_sectors;
998 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
999 screen_top_ptr = buffer_end-1;
1000 fill_buffer(last_sectors, buffer, BUFFER_SIZE);
1003 #ifdef HAVE_LCD_BITMAP
1004 static void init_need_scrollbar(void) {
1005 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1006 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1007 viewer_draw(-1);
1008 prefs.need_scrollbar = NEED_SCROLLBAR();
1009 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1010 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1012 #else
1013 #define init_need_scrollbar()
1014 #endif
1016 static bool viewer_init(void)
1018 #ifdef HAVE_LCD_BITMAP
1020 pf = rb->font_get(FONT_UI);
1022 display_lines = LCD_HEIGHT / pf->height;
1023 draw_columns = display_columns = LCD_WIDTH;
1024 #else
1025 /* REAL fixed pitch :) all chars use up 1 cell */
1026 display_lines = 2;
1027 draw_columns = display_columns = 11;
1028 par_indent_spaces = 2;
1029 #endif
1031 fd = rb->open(file_name, O_RDONLY);
1032 if (fd==-1)
1033 return false;
1035 file_size = rb->filesize(fd);
1036 if (file_size==-1)
1037 return false;
1039 /* Init mac_text value used in processing buffer */
1040 mac_text = false;
1042 /* Set codepage to system default */
1043 prefs.encoding = rb->global_settings->default_codepage;
1045 /* Read top of file into buffer;
1046 init file_pos, buffer_end, screen_top_ptr */
1047 viewer_top();
1049 /* Init prefs.need_scrollbar value */
1050 init_need_scrollbar();
1052 return true;
1055 static void viewer_reset_settings(void)
1057 prefs.word_mode = WRAP;
1058 prefs.line_mode = NORMAL;
1059 prefs.view_mode = NARROW;
1060 prefs.scroll_mode = PAGE;
1061 #ifdef HAVE_LCD_BITMAP
1062 prefs.page_mode = NO_OVERLAP;
1063 prefs.scrollbar_mode = SB_OFF;
1064 #endif
1065 prefs.autoscroll_speed = 1;
1068 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1070 int settings_fd, i;
1071 struct bookmark_file_data *data;
1073 /* read settings file */
1074 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1075 if ((settings_fd < 0) || (rb->filesize(settings_fd) != sizeof(struct preferences)))
1077 rb->splash(HZ*2, "No Valid Settings File");
1079 else
1081 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1082 rb->close(settings_fd);
1085 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1086 data->bookmarked_files_count = 0;
1088 /* read bookmarks if file exists */
1089 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1090 if (settings_fd >= 0)
1092 /* figure out how many items to read */
1093 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1094 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1095 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1096 rb->read(settings_fd, data->bookmarks,
1097 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1098 rb->close(settings_fd);
1101 /* check if current file is in list */
1102 for (i=0; i < data->bookmarked_files_count; i++)
1104 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1106 file_pos = data->bookmarks[i].file_position;
1107 screen_top_ptr = buffer + data->bookmarks[i].top_ptr_pos;
1108 break;
1112 /* prevent potential slot overflow */
1113 if (i >= data->bookmarked_files_count)
1115 if (i < MAX_BOOKMARKED_FILES)
1116 data->bookmarked_files_count++;
1117 else
1118 i = MAX_BOOKMARKED_FILES-1;
1121 /* write bookmark file with spare slot in first position
1122 to be filled in by viewer_save_settings */
1123 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1124 if (settings_fd >=0 )
1126 /* write count and skip first slot */
1127 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1128 rb->PREFIX(lseek)(settings_fd,sizeof(struct bookmarked_file_info),SEEK_CUR);
1130 /* shuffle up bookmarks */
1131 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1132 rb->close(settings_fd);
1135 init_need_scrollbar();
1137 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1139 if (BUFFER_OOB(screen_top_ptr))
1141 screen_top_ptr = buffer;
1144 fill_buffer(file_pos, buffer, BUFFER_SIZE);
1147 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1149 int settings_fd;
1151 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1153 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1154 rb->close(settings_fd);
1156 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1157 if (settings_fd >= 0 )
1159 struct bookmarked_file_info b;
1160 b.file_position = file_pos;
1161 b.top_ptr_pos = screen_top_ptr - buffer;
1162 rb->memset(&b.filename[0],0,MAX_PATH);
1163 rb->strcpy(b.filename,file_name);
1164 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1165 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1166 rb->close(settings_fd);
1170 static void viewer_exit(void *parameter)
1172 (void)parameter;
1174 viewer_save_settings();
1175 rb->close(fd);
1178 static int col_limit(int col)
1180 if (col < 0)
1181 col = 0;
1182 else
1183 if (col > max_line_len - 2*glyph_width('o'))
1184 col = max_line_len - 2*glyph_width('o');
1186 return col;
1189 /* settings helper functions */
1191 static bool encoding_setting(void)
1193 static const struct opt_items names[] = {
1194 {"ISO-8859-1", -1},
1195 {"ISO-8859-7", -1},
1196 {"ISO-8859-8", -1},
1197 {"CP1251", -1},
1198 {"ISO-8859-11", -1},
1199 {"ISO-8859-6", -1},
1200 {"ISO-8859-9", -1},
1201 {"ISO-8859-2", -1},
1202 {"CP1250", -1},
1203 {"SJIS", -1},
1204 {"GB-2312", -1},
1205 {"KSX-1001", -1},
1206 {"BIG5", -1},
1207 {"UTF-8", -1},
1210 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1211 sizeof(names) / sizeof(names[0]), NULL);
1214 static bool word_wrap_setting(void)
1216 static const struct opt_items names[] = {
1217 {"On", -1},
1218 {"Off (Chop Words)", -1},
1221 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1222 names, 2, NULL);
1225 static bool line_mode_setting(void)
1227 static const struct opt_items names[] = {
1228 {"Normal", -1},
1229 {"Join Lines", -1},
1230 {"Expand Lines", -1},
1231 #ifdef HAVE_LCD_BITMAP
1232 {"Reflow Lines", -1},
1233 #endif
1236 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1237 sizeof(names) / sizeof(names[0]), NULL);
1240 static bool view_mode_setting(void)
1242 static const struct opt_items names[] = {
1243 {"No (Narrow)", -1},
1244 {"Yes", -1},
1246 bool ret;
1247 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1248 names , 2, NULL);
1249 if (prefs.view_mode == NARROW)
1250 col = 0;
1251 return ret;
1254 static bool scroll_mode_setting(void)
1256 static const struct opt_items names[] = {
1257 {"Scroll by Page", -1},
1258 {"Scroll by Line", -1},
1261 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1262 names, 2, NULL);
1265 #ifdef HAVE_LCD_BITMAP
1266 static bool page_mode_setting(void)
1268 static const struct opt_items names[] = {
1269 {"No", -1},
1270 {"Yes", -1},
1273 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1274 names, 2, NULL);
1277 static bool scrollbar_setting(void)
1279 static const struct opt_items names[] = {
1280 {"Off", -1},
1281 {"On", -1}
1284 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1285 names, 2, NULL);
1287 #endif
1289 static bool autoscroll_speed_setting(void)
1291 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1292 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1295 static bool viewer_options_menu(void)
1297 int m;
1298 bool result;
1300 static const struct menu_item items[] = {
1301 {"Encoding", encoding_setting },
1302 {"Word Wrap", word_wrap_setting },
1303 {"Line Mode", line_mode_setting },
1304 {"Wide View", view_mode_setting },
1305 #ifdef HAVE_LCD_BITMAP
1306 {"Show Scrollbar", scrollbar_setting },
1307 {"Overlap Pages", page_mode_setting },
1308 #endif
1309 {"Scroll Mode", scroll_mode_setting},
1310 {"Auto-Scroll Speed", autoscroll_speed_setting },
1312 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1313 NULL, NULL, NULL, NULL);
1315 result = menu_run(m);
1316 menu_exit(m);
1317 #ifdef HAVE_LCD_BITMAP
1318 rb->lcd_setmargins(0,0);
1320 /* Show-scrollbar mode for current view-width mode */
1321 if (!ONE_SCREEN_FITS_ALL())
1322 if (prefs.scrollbar_mode == true)
1323 init_need_scrollbar();
1324 #endif
1325 return result;
1328 static void viewer_menu(void)
1330 int m;
1331 int result;
1332 static const struct menu_item items[] = {
1333 {"Quit", NULL },
1334 {"Viewer Options", NULL },
1335 {"Show Playback Menu", NULL },
1336 {"Return", NULL },
1339 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1340 result=menu_show(m);
1341 switch (result)
1343 case 0: /* quit */
1344 menu_exit(m);
1345 viewer_exit(NULL);
1346 done = true;
1347 break;
1348 case 1: /* change settings */
1349 done = viewer_options_menu();
1350 break;
1351 case 2: /* playback control */
1352 playback_control(rb);
1353 break;
1354 case 3: /* return */
1355 break;
1357 menu_exit(m);
1358 #ifdef HAVE_LCD_BITMAP
1359 rb->lcd_setmargins(0,0);
1360 #endif
1361 viewer_draw(col);
1364 enum plugin_status plugin_start(struct plugin_api* api, void* file)
1366 int button, i, ok;
1367 int lastbutton = BUTTON_NONE;
1368 bool autoscroll = false;
1369 long old_tick;
1371 rb = api;
1372 old_tick = *rb->current_tick;
1374 if (!file)
1375 return PLUGIN_ERROR;
1377 file_name = file;
1378 ok = viewer_init();
1379 if (!ok) {
1380 rb->splash(HZ, "Error opening file.");
1381 return PLUGIN_ERROR;
1384 viewer_reset_settings(); /* load defaults first */
1385 viewer_load_settings(); /* .. then try to load from disk */
1387 #if LCD_DEPTH > 1
1388 rb->lcd_set_backdrop(NULL);
1389 #endif
1391 viewer_draw(col);
1393 while (!done) {
1395 if(autoscroll)
1397 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1399 viewer_scroll_down();
1400 viewer_draw(col);
1401 old_tick = *rb->current_tick;
1405 button = rb->button_get_w_tmo(HZ/10);
1406 switch (button) {
1407 case VIEWER_MENU:
1408 viewer_menu();
1409 break;
1411 case VIEWER_AUTOSCROLL:
1412 #ifdef VIEWER_AUTOSCROLL_PRE
1413 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1414 break;
1415 #endif
1416 autoscroll = !autoscroll;
1417 break;
1419 case VIEWER_PAGE_UP:
1420 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1421 if (prefs.scroll_mode == PAGE)
1423 /* Page up */
1424 #ifdef HAVE_LCD_BITMAP
1425 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1426 #else
1427 for (i = 0; i < display_lines; i++)
1428 #endif
1429 viewer_scroll_up();
1431 else
1432 viewer_scroll_up();
1433 old_tick = *rb->current_tick;
1434 viewer_draw(col);
1435 break;
1437 case VIEWER_PAGE_DOWN:
1438 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1439 if (prefs.scroll_mode == PAGE)
1441 /* Page down */
1442 if (next_screen_ptr != NULL)
1443 screen_top_ptr = next_screen_to_draw_ptr;
1445 else
1446 viewer_scroll_down();
1447 old_tick = *rb->current_tick;
1448 viewer_draw(col);
1449 break;
1451 case VIEWER_SCREEN_LEFT:
1452 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1453 if (prefs.view_mode == WIDE) {
1454 /* Screen left */
1455 col -= draw_columns;
1456 col = col_limit(col);
1458 else { /* prefs.view_mode == NARROW */
1459 /* Top of file */
1460 viewer_top();
1463 viewer_draw(col);
1464 break;
1466 case VIEWER_SCREEN_RIGHT:
1467 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1468 if (prefs.view_mode == WIDE) {
1469 /* Screen right */
1470 col += draw_columns;
1471 col = col_limit(col);
1473 else { /* prefs.view_mode == NARROW */
1474 /* Bottom of file */
1475 viewer_bottom();
1478 viewer_draw(col);
1479 break;
1481 #ifdef VIEWER_LINE_UP
1482 case VIEWER_LINE_UP:
1483 case VIEWER_LINE_UP | BUTTON_REPEAT:
1484 /* Scroll up one line */
1485 viewer_scroll_up();
1486 old_tick = *rb->current_tick;
1487 viewer_draw(col);
1488 break;
1490 case VIEWER_LINE_DOWN:
1491 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1492 /* Scroll down one line */
1493 if (next_screen_ptr != NULL)
1494 screen_top_ptr = next_line_ptr;
1495 old_tick = *rb->current_tick;
1496 viewer_draw(col);
1497 break;
1498 #endif
1499 #ifdef VIEWER_COLUMN_LEFT
1500 case VIEWER_COLUMN_LEFT:
1501 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1502 if (prefs.view_mode == WIDE) {
1503 /* Scroll left one column */
1504 col -= glyph_width('o');
1505 col = col_limit(col);
1506 viewer_draw(col);
1508 break;
1510 case VIEWER_COLUMN_RIGHT:
1511 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1512 if (prefs.view_mode == WIDE) {
1513 /* Scroll right one column */
1514 col += glyph_width('o');
1515 col = col_limit(col);
1516 viewer_draw(col);
1518 break;
1519 #endif
1521 #ifdef VIEWER_RC_QUIT
1522 case VIEWER_RC_QUIT:
1523 #endif
1524 case VIEWER_QUIT:
1525 viewer_exit(NULL);
1526 done = true;
1527 break;
1529 default:
1530 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1531 == SYS_USB_CONNECTED)
1532 return PLUGIN_USB_CONNECTED;
1533 break;
1535 if (button != BUTTON_NONE)
1537 lastbutton = button;
1538 rb->yield();
1541 return PLUGIN_OK;