Do not display the backdrop in text viewer and editor plugins
[kugel-rb.git] / apps / plugins / viewer.c
blob37b7b492907e5ce10741608bc2cb2385a7febabe
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 #endif
211 /* stuff for the bookmarking */
212 struct bookmarked_file_info {
213 long file_position;
214 int top_ptr_pos;
215 char filename[MAX_PATH];
218 struct bookmark_file_data {
219 signed int bookmarked_files_count;
220 struct bookmarked_file_info bookmarks[];
223 struct preferences {
224 enum {
225 WRAP=0,
226 CHOP,
227 } word_mode;
229 enum {
230 NORMAL=0,
231 JOIN,
232 EXPAND,
233 REFLOW, /* won't be set on charcell LCD, must be last */
234 } line_mode;
236 enum {
237 NARROW=0,
238 WIDE,
239 } view_mode;
241 enum {
242 ISO_8859_1=0,
243 ISO_8859_7,
244 ISO_8859_8,
245 CP1251,
246 ISO_8859_11,
247 ISO_8859_6,
248 ISO_8859_9,
249 ISO_8859_2,
250 SJIS,
251 GB2312,
252 KSX1001,
253 BIG5,
254 UTF8,
255 ENCODINGS
256 } encoding; /* FIXME: What should default encoding be? */
258 #ifdef HAVE_LCD_BITMAP
259 enum {
260 SB_OFF=0,
261 SB_ON,
262 } scrollbar_mode;
263 bool need_scrollbar;
265 enum {
266 NO_OVERLAP=0,
267 OVERLAP,
268 } page_mode;
269 #endif /* HAVE_LCD_BITMAP */
271 enum {
272 PAGE=0,
273 LINE,
274 } scroll_mode;
276 int autoscroll_speed;
278 } prefs;
280 static unsigned char buffer[BUFFER_SIZE + 1];
281 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
282 static int display_columns; /* number of (pixel) columns on the display */
283 static int display_lines; /* number of lines on the display */
284 static int draw_columns; /* number of (pixel) columns available for text */
285 static int par_indent_spaces; /* number of spaces to indent first paragraph */
286 static int fd;
287 static char *file_name;
288 static long file_size;
289 static bool mac_text;
290 static long file_pos; /* Position of the top of the buffer in the file */
291 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
292 static int max_line_len;
293 static unsigned char *screen_top_ptr;
294 static unsigned char *next_screen_ptr;
295 static unsigned char *next_screen_to_draw_ptr;
296 static unsigned char *next_line_ptr;
297 static struct plugin_api* rb;
298 #ifdef HAVE_LCD_BITMAP
299 static struct font *pf;
300 #endif
303 int glyph_width(int ch)
305 if (ch == 0)
306 ch = ' ';
308 #ifdef HAVE_LCD_BITMAP
309 return rb->font_get_width(pf, ch);
310 #else
311 return 1;
312 #endif
315 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
317 unsigned char utf8_tmp[6];
318 int count;
320 if (prefs.encoding == UTF8)
321 return (unsigned char*)rb->utf8decode(str, ch);
323 count = BUFFER_OOB(str+2)? 1:2;
324 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
325 rb->utf8decode(utf8_tmp, ch);
327 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
328 return (unsigned char*)str+1;
329 else
330 return (unsigned char*)str+2;
333 bool done = false;
334 int col = 0;
336 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
337 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
338 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
339 static unsigned char* crop_at_width(const unsigned char* p)
341 int k,width;
342 unsigned short ch;
343 const unsigned char *oldp = p;
345 k=width=0;
347 while (LINE_IS_NOT_FULL) {
348 oldp = p;
349 p = get_ucs(p, &ch);
350 ADVANCE_COUNTERS(ch);
353 return (unsigned char*)oldp;
356 static unsigned char* find_first_feed(const unsigned char* p, int size)
358 int i;
360 for (i=0; i < size; i++)
361 if (p[i] == 0)
362 return (unsigned char*) p+i;
364 return NULL;
367 static unsigned char* find_last_feed(const unsigned char* p, int size)
369 int i;
371 for (i=size-1; i>=0; i--)
372 if (p[i] == 0)
373 return (unsigned char*) p+i;
375 return NULL;
378 static unsigned char* find_last_space(const unsigned char* p, int size)
380 int i, j, k;
382 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
384 if (!BUFFER_OOB(&p[size]))
385 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
386 if (p[size] == line_break[j])
387 return (unsigned char*) p+size;
389 for (i=size-1; i>=0; i--)
390 for (j=k; j < (int) sizeof(line_break); j++)
392 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
393 if (p[i] == line_break[j])
394 return (unsigned char*) p+i;
397 return NULL;
400 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
402 const unsigned char *next_line = NULL;
403 int size, i, j, k, width, search_len, spaces, newlines;
404 bool first_chars;
405 unsigned char c;
407 if (is_short != NULL)
408 *is_short = true;
410 if BUFFER_OOB(cur_line)
411 return NULL;
413 if (prefs.view_mode == WIDE) {
414 search_len = MAX_WIDTH;
416 else { /* prefs.view_mode == NARROW */
417 search_len = crop_at_width(cur_line) - cur_line;
420 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
422 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
423 /* Need to scan ahead and possibly increase search_len and size,
424 or possibly set next_line at second hard return in a row. */
425 next_line = NULL;
426 first_chars=true;
427 for (j=k=width=spaces=newlines=0; ; j++) {
428 if (BUFFER_OOB(cur_line+j))
429 return NULL;
430 if (LINE_IS_FULL) {
431 size = search_len = j;
432 break;
435 c = cur_line[j];
436 switch (c) {
437 case ' ':
438 if (prefs.line_mode == REFLOW) {
439 if (newlines > 0) {
440 size = j;
441 next_line = cur_line + size;
442 return (unsigned char*) next_line;
444 if (j==0) /* i=1 is intentional */
445 for (i=0; i<par_indent_spaces; i++)
446 ADVANCE_COUNTERS(' ');
448 if (!first_chars) spaces++;
449 break;
451 case 0:
452 if (newlines > 0) {
453 size = j;
454 next_line = cur_line + size - spaces;
455 if (next_line != cur_line)
456 return (unsigned char*) next_line;
457 break;
460 newlines++;
461 size += spaces -1;
462 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
463 return NULL;
464 search_len = size;
465 spaces = first_chars? 0:1;
466 break;
468 default:
469 if (prefs.line_mode==JOIN || newlines>0) {
470 while (spaces) {
471 spaces--;
472 ADVANCE_COUNTERS(' ');
473 if (LINE_IS_FULL) {
474 size = search_len = j;
475 break;
478 newlines=0;
479 } else if (spaces) {
480 /* REFLOW, multiple spaces between words: count only
481 * one. If more are needed, they will be added
482 * while drawing. */
483 search_len = size;
484 spaces=0;
485 ADVANCE_COUNTERS(' ');
486 if (LINE_IS_FULL) {
487 size = search_len = j;
488 break;
491 first_chars = false;
492 ADVANCE_COUNTERS(c);
493 break;
497 else {
498 /* find first hard return */
499 next_line = find_first_feed(cur_line, size);
502 if (next_line == NULL)
503 if (size == search_len) {
504 if (prefs.word_mode == WRAP) /* Find last space */
505 next_line = find_last_space(cur_line, size);
507 if (next_line == NULL)
508 next_line = crop_at_width(cur_line);
509 else
510 if (prefs.word_mode == WRAP)
511 for (i=0;
512 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
513 i++)
514 next_line++;
517 if (prefs.line_mode == EXPAND)
518 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
519 if (next_line[0] == 0)
520 if (next_line != cur_line)
521 return (unsigned char*) next_line;
523 /* If next_line is pointing to a zero, increment it; i.e.,
524 leave the terminator at the end of cur_line. If pointing
525 to a hyphen, increment only if there is room to display
526 the hyphen on current line (won't apply in WIDE mode,
527 since it's guarenteed there won't be room). */
528 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
529 if (next_line[0] == 0)/* ||
530 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
531 next_line++;
533 if (BUFFER_OOB(next_line))
534 return NULL;
536 if (is_short)
537 *is_short = false;
539 return (unsigned char*) next_line;
542 static unsigned char* find_prev_line(const unsigned char* cur_line)
544 const unsigned char *prev_line = NULL;
545 const unsigned char *p;
547 if BUFFER_OOB(cur_line)
548 return NULL;
550 /* To wrap consistently at the same places, we must
551 start with a known hard return, then work downwards.
552 We can either search backwards for a hard return,
553 or simply start wrapping downwards from top of buffer.
554 If current line is not near top of buffer, this is
555 a file with long lines (paragraphs). We would need to
556 read earlier sectors before we could decide how to
557 properly wrap the lines above the current line, but
558 it probably is not worth the disk access. Instead,
559 start with top of buffer and wrap down from there.
560 This may result in some lines wrapping at different
561 points from where they wrap when scrolling down.
562 If buffer is at top of file, start at top of buffer. */
564 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
565 prev_line = p = NULL;
566 else
567 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
568 /* Null means no line feeds in buffer above current line. */
570 if (prev_line == NULL)
571 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
572 prev_line = p = buffer;
573 /* (else return NULL and read previous block) */
575 /* Wrap downwards until too far, then use the one before. */
576 while (p < cur_line && p != NULL) {
577 prev_line = p;
578 p = find_next_line(prev_line, NULL);
581 if (BUFFER_OOB(prev_line))
582 return NULL;
584 return (unsigned char*) prev_line;
587 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
589 /* Read from file and preprocess the data */
590 /* To minimize disk access, always read on sector boundaries */
591 unsigned numread, i;
592 bool found_CR = false;
594 rb->lseek(fd, pos, SEEK_SET);
595 numread = rb->read(fd, buf, size);
596 rb->button_clear_queue(); /* clear button queue */
598 for(i = 0; i < numread; i++) {
599 switch(buf[i]) {
600 case '\r':
601 if (mac_text) {
602 buf[i] = 0;
604 else {
605 buf[i] = ' ';
606 found_CR = true;
608 break;
610 case '\n':
611 buf[i] = 0;
612 found_CR = false;
613 break;
615 case 0: /* No break between case 0 and default, intentionally */
616 buf[i] = ' ';
617 default:
618 if (found_CR) {
619 buf[i - 1] = 0;
620 found_CR = false;
621 mac_text = true;
623 break;
628 static int read_and_synch(int direction)
630 /* Read next (or prev) block, and reposition global pointers. */
631 /* direction: 1 for down (i.e., further into file), -1 for up */
632 int move_size, move_vector, offset;
633 unsigned char *fill_buf;
635 if (direction == -1) /* up */ {
636 move_size = SMALL_BLOCK_SIZE;
637 offset = 0;
638 fill_buf = TOP_SECTOR;
639 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
640 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
642 else /* down */ {
643 if (prefs.view_mode == WIDE) {
644 /* WIDE mode needs more buffer so we have to read smaller blocks */
645 move_size = SMALL_BLOCK_SIZE;
646 offset = LARGE_BLOCK_SIZE;
647 fill_buf = BOTTOM_SECTOR;
648 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
649 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
651 else {
652 move_size = LARGE_BLOCK_SIZE;
653 offset = SMALL_BLOCK_SIZE;
654 fill_buf = MID_SECTOR;
655 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
658 move_vector = direction * move_size;
659 screen_top_ptr -= move_vector;
660 file_pos += move_vector;
661 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
662 fill_buffer(file_pos + offset, fill_buf, move_size);
663 return move_vector;
666 static void viewer_scroll_up(void)
668 unsigned char *p;
670 p = find_prev_line(screen_top_ptr);
671 if (p == NULL && !BUFFER_BOF()) {
672 read_and_synch(-1);
673 p = find_prev_line(screen_top_ptr);
675 if (p != NULL)
676 screen_top_ptr = p;
679 static void viewer_scroll_down(void)
681 if (next_screen_ptr != NULL)
682 screen_top_ptr = next_line_ptr;
685 #ifdef HAVE_LCD_BITMAP
686 static void viewer_scrollbar(void) {
687 int items, min_shown, max_shown;
689 items = (int) file_size; /* (SH1 int is same as long) */
690 min_shown = (int) file_pos + (screen_top_ptr - buffer);
692 if (next_screen_ptr == NULL)
693 max_shown = items;
694 else
695 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
697 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
698 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
700 #endif
702 static void viewer_draw(int col)
704 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
705 int width, extra_spaces, indent_spaces, spaces_per_word;
706 bool multiple_spacing, line_is_short;
707 unsigned short ch;
708 unsigned char *str, *oldstr;
709 unsigned char *line_begin;
710 unsigned char *line_end;
711 unsigned char c;
712 unsigned char scratch_buffer[MAX_COLUMNS + 1];
713 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
714 unsigned char *endptr;
716 /* If col==-1 do all calculations but don't display */
717 if (col != -1) {
718 #ifdef HAVE_LCD_BITMAP
719 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
720 #else
721 left_col = 0;
722 #endif
723 rb->lcd_clear_display();
725 max_line_len = 0;
726 line_begin = line_end = screen_top_ptr;
728 for (i = 0; i < display_lines; i++) {
729 if (BUFFER_OOB(line_end))
730 break; /* Happens after display last line at BUFFER_EOF() */
732 line_begin = line_end;
733 line_end = find_next_line(line_begin, &line_is_short);
735 if (line_end == NULL) {
736 if (BUFFER_EOF()) {
737 if (i < display_lines - 1 && !BUFFER_BOF()) {
738 if (col != -1)
739 rb->lcd_clear_display();
741 for (; i < display_lines - 1; i++)
742 viewer_scroll_up();
744 line_begin = line_end = screen_top_ptr;
745 i = -1;
746 continue;
748 else {
749 line_end = buffer_end;
752 else {
753 resynch_move = read_and_synch(1); /* Read block & move ptrs */
754 line_begin -= resynch_move;
755 if (i > 0)
756 next_line_ptr -= resynch_move;
758 line_end = find_next_line(line_begin, NULL);
759 if (line_end == NULL) /* Should not really happen */
760 break;
763 line_len = line_end - line_begin;
765 /* calculate line_len */
766 str = oldstr = line_begin;
767 j = -1;
768 while (str < line_end) {
769 oldstr = str;
770 str = crop_at_width(str);
771 j++;
773 line_width = j*draw_columns;
774 while (oldstr < line_end) {
775 oldstr = get_ucs(oldstr, &ch);
776 line_width += glyph_width(ch);
779 if (prefs.line_mode == JOIN) {
780 if (line_begin[0] == 0) {
781 line_begin++;
782 if (prefs.word_mode == CHOP)
783 line_end++;
784 else
785 line_len--;
787 for (j=k=spaces=0; j < line_len; j++) {
788 if (k == MAX_COLUMNS)
789 break;
791 c = line_begin[j];
792 switch (c) {
793 case ' ':
794 spaces++;
795 break;
796 case 0:
797 spaces = 0;
798 scratch_buffer[k++] = ' ';
799 break;
800 default:
801 while (spaces) {
802 spaces--;
803 scratch_buffer[k++] = ' ';
804 if (k == MAX_COLUMNS - 1)
805 break;
807 scratch_buffer[k++] = c;
808 break;
811 if (col != -1) {
812 scratch_buffer[k] = 0;
813 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
814 prefs.encoding, draw_columns/glyph_width('i'));
815 *endptr = 0;
818 else if (prefs.line_mode == REFLOW) {
819 if (line_begin[0] == 0) {
820 line_begin++;
821 if (prefs.word_mode == CHOP)
822 line_end++;
823 else
824 line_len--;
827 indent_spaces = 0;
828 if (!line_is_short) {
829 multiple_spacing = false;
830 width=spaces=0;
831 for (str = line_begin; str < line_end; ) {
832 str = get_ucs(str, &ch);
833 switch (ch) {
834 case ' ':
835 case 0:
836 if ((str == line_begin) && (prefs.word_mode==WRAP))
837 /* special case: indent the paragraph,
838 * don't count spaces */
839 indent_spaces = par_indent_spaces;
840 else if (!multiple_spacing)
841 spaces++;
842 multiple_spacing = true;
843 break;
844 default:
845 multiple_spacing = false;
846 width += glyph_width(ch);
847 break;
850 if (multiple_spacing) spaces--;
852 if (spaces) {
853 /* total number of spaces to insert between words */
854 extra_spaces = (draw_columns-width)/glyph_width(' ')
855 - indent_spaces;
856 /* number of spaces between each word*/
857 spaces_per_word = extra_spaces / spaces;
858 /* number of words with n+1 spaces (to fill up) */
859 extra_spaces = extra_spaces % spaces;
860 if (spaces_per_word > 2) { /* too much spacing is awful */
861 spaces_per_word = 3;
862 extra_spaces = 0;
864 } else { /* this doesn't matter much... no spaces anyway */
865 spaces_per_word = extra_spaces = 0;
867 } else { /* end of a paragraph: don't fill line */
868 spaces_per_word = 1;
869 extra_spaces = 0;
872 multiple_spacing = false;
873 for (j=k=spaces=0; j < line_len; j++) {
874 if (k == MAX_COLUMNS)
875 break;
877 c = line_begin[j];
878 switch (c) {
879 case ' ':
880 case 0:
881 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
882 for (j=0; j<par_indent_spaces; j++)
883 scratch_buffer[k++] = ' ';
884 j=0;
886 else if (!multiple_spacing) {
887 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
888 scratch_buffer[k++] = ' ';
889 spaces++;
891 multiple_spacing = true;
892 break;
893 default:
894 scratch_buffer[k++] = c;
895 multiple_spacing = false;
896 break;
900 if (col != -1) {
901 scratch_buffer[k] = 0;
902 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
903 prefs.encoding, k-col);
904 *endptr = 0;
907 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
908 if (col != -1)
909 if (line_width > col) {
910 str = oldstr = line_begin;
911 k = col;
912 width = 0;
913 while( (width<draw_columns) && (oldstr<line_end) )
915 oldstr = get_ucs(oldstr, &ch);
916 if (k > 0) {
917 k -= glyph_width(ch);
918 line_begin = oldstr;
919 } else {
920 width += glyph_width(ch);
924 if(prefs.view_mode==WIDE)
925 endptr = rb->iso_decode(line_begin, utf8_buffer,
926 prefs.encoding, oldstr-line_begin);
927 else
928 endptr = rb->iso_decode(line_begin, utf8_buffer,
929 prefs.encoding, line_end-line_begin);
930 *endptr = 0;
933 if (col != -1 && line_width > col)
934 #ifdef HAVE_LCD_BITMAP
935 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
936 #else
937 rb->lcd_puts(left_col, i, utf8_buffer);
938 #endif
939 if (line_width > max_line_len)
940 max_line_len = line_width;
942 if (i == 0)
943 next_line_ptr = line_end;
945 next_screen_ptr = line_end;
946 if (BUFFER_OOB(next_screen_ptr))
947 next_screen_ptr = NULL;
949 #ifdef HAVE_LCD_BITMAP
950 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
952 if (prefs.need_scrollbar)
953 viewer_scrollbar();
954 #else
955 next_screen_to_draw_ptr = next_screen_ptr;
956 #endif
958 if (col != -1)
959 rb->lcd_update();
962 static void viewer_top(void)
964 /* Read top of file into buffer
965 and point screen pointer to top */
966 file_pos = 0;
967 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
968 screen_top_ptr = buffer;
969 fill_buffer(0, buffer, BUFFER_SIZE);
972 static void viewer_bottom(void)
974 /* Read bottom of file into buffer
975 and point screen pointer to bottom */
976 long last_sectors;
978 if (file_size > BUFFER_SIZE) {
979 /* Find last buffer in file, round up to next sector boundary */
980 last_sectors = file_size - BUFFER_SIZE + SMALL_BLOCK_SIZE;
981 last_sectors /= SMALL_BLOCK_SIZE;
982 last_sectors *= SMALL_BLOCK_SIZE;
984 else {
985 last_sectors = 0;
987 file_pos = last_sectors;
988 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
989 screen_top_ptr = buffer_end-1;
990 fill_buffer(last_sectors, buffer, BUFFER_SIZE);
993 #ifdef HAVE_LCD_BITMAP
994 static void init_need_scrollbar(void) {
995 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
996 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
997 viewer_draw(-1);
998 prefs.need_scrollbar = NEED_SCROLLBAR();
999 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1000 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1002 #else
1003 #define init_need_scrollbar()
1004 #endif
1006 static bool viewer_init(void)
1008 #ifdef HAVE_LCD_BITMAP
1010 pf = rb->font_get(FONT_UI);
1012 display_lines = LCD_HEIGHT / pf->height;
1013 draw_columns = display_columns = LCD_WIDTH;
1014 #else
1015 /* REAL fixed pitch :) all chars use up 1 cell */
1016 display_lines = 2;
1017 draw_columns = display_columns = 11;
1018 par_indent_spaces = 2;
1019 #endif
1021 fd = rb->open(file_name, O_RDONLY);
1022 if (fd==-1)
1023 return false;
1025 file_size = rb->filesize(fd);
1026 if (file_size==-1)
1027 return false;
1029 /* Init mac_text value used in processing buffer */
1030 mac_text = false;
1032 /* Set codepage to system default */
1033 prefs.encoding = rb->global_settings->default_codepage;
1035 /* Read top of file into buffer;
1036 init file_pos, buffer_end, screen_top_ptr */
1037 viewer_top();
1039 /* Init prefs.need_scrollbar value */
1040 init_need_scrollbar();
1042 return true;
1045 static void viewer_reset_settings(void)
1047 prefs.word_mode = WRAP;
1048 prefs.line_mode = NORMAL;
1049 prefs.view_mode = NARROW;
1050 prefs.scroll_mode = PAGE;
1051 #ifdef HAVE_LCD_BITMAP
1052 prefs.page_mode = NO_OVERLAP;
1053 prefs.scrollbar_mode = SB_OFF;
1054 #endif
1055 prefs.autoscroll_speed = 1;
1058 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1060 int settings_fd, i;
1061 struct bookmark_file_data *data;
1063 /* read settings file */
1064 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1065 if ((settings_fd < 0) || (rb->filesize(settings_fd) != sizeof(struct preferences)))
1067 rb->splash(HZ*2, "No Valid Settings File");
1069 else
1071 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1072 rb->close(settings_fd);
1075 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1076 data->bookmarked_files_count = 0;
1078 /* read bookmarks if file exists */
1079 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1080 if (settings_fd >= 0)
1082 /* figure out how many items to read */
1083 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1084 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1085 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1086 rb->read(settings_fd, data->bookmarks,
1087 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1088 rb->close(settings_fd);
1091 /* check if current file is in list */
1092 for (i=0; i < data->bookmarked_files_count; i++)
1094 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1096 file_pos = data->bookmarks[i].file_position;
1097 screen_top_ptr = buffer + data->bookmarks[i].top_ptr_pos;
1098 break;
1102 /* prevent potential slot overflow */
1103 if (i >= data->bookmarked_files_count)
1105 if (i < MAX_BOOKMARKED_FILES)
1106 data->bookmarked_files_count++;
1107 else
1108 i = MAX_BOOKMARKED_FILES-1;
1111 /* write bookmark file with spare slot in first position
1112 to be filled in by viewer_save_settings */
1113 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1114 if (settings_fd >=0 )
1116 /* write count and skip first slot */
1117 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1118 rb->PREFIX(lseek)(settings_fd,sizeof(struct bookmarked_file_info),SEEK_CUR);
1120 /* shuffle up bookmarks */
1121 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1122 rb->close(settings_fd);
1125 init_need_scrollbar();
1127 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1129 if (BUFFER_OOB(screen_top_ptr))
1131 screen_top_ptr = buffer;
1134 fill_buffer(file_pos, buffer, BUFFER_SIZE);
1137 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1139 int settings_fd;
1141 rb->splash(1, "Saving Settings");
1143 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1145 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1146 rb->close(settings_fd);
1148 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1149 if (settings_fd >= 0 )
1151 struct bookmarked_file_info b;
1152 b.file_position = file_pos;
1153 b.top_ptr_pos = screen_top_ptr - buffer;
1154 rb->memset(&b.filename[0],0,MAX_PATH);
1155 rb->strcpy(b.filename,file_name);
1156 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1157 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1158 rb->close(settings_fd);
1162 static void viewer_exit(void *parameter)
1164 (void)parameter;
1166 viewer_save_settings();
1167 rb->close(fd);
1170 static int col_limit(int col)
1172 if (col < 0)
1173 col = 0;
1174 else
1175 if (col > max_line_len - 2*glyph_width('o'))
1176 col = max_line_len - 2*glyph_width('o');
1178 return col;
1181 /* settings helper functions */
1183 static bool encoding_setting(void)
1185 static const struct opt_items names[] = {
1186 {"ISO-8859-1", -1},
1187 {"ISO-8859-7", -1},
1188 {"ISO-8859-8", -1},
1189 {"CP1251", -1},
1190 {"ISO-8859-11", -1},
1191 {"ISO-8859-6", -1},
1192 {"ISO-8859-9", -1},
1193 {"ISO-8859-2", -1},
1194 {"SJIS", -1},
1195 {"GB-2312", -1},
1196 {"KSX-1001", -1},
1197 {"BIG5", -1},
1198 {"UTF-8", -1},
1201 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1202 sizeof(names) / sizeof(names[0]), NULL);
1205 static bool word_wrap_setting(void)
1207 static const struct opt_items names[] = {
1208 {"On", -1},
1209 {"Off (Chop Words)", -1},
1212 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1213 names, 2, NULL);
1216 static bool line_mode_setting(void)
1218 static const struct opt_items names[] = {
1219 {"Normal", -1},
1220 {"Join Lines", -1},
1221 {"Expand Lines", -1},
1222 #ifdef HAVE_LCD_BITMAP
1223 {"Reflow Lines", -1},
1224 #endif
1227 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1228 sizeof(names) / sizeof(names[0]), NULL);
1231 static bool view_mode_setting(void)
1233 static const struct opt_items names[] = {
1234 {"No (Narrow)", -1},
1235 {"Yes", -1},
1237 bool ret;
1238 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1239 names , 2, NULL);
1240 if (prefs.view_mode == NARROW)
1241 col = 0;
1242 return ret;
1245 static bool scroll_mode_setting(void)
1247 static const struct opt_items names[] = {
1248 {"Scroll by Page", -1},
1249 {"Scroll by Line", -1},
1252 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1253 names, 2, NULL);
1256 #ifdef HAVE_LCD_BITMAP
1257 static bool page_mode_setting(void)
1259 static const struct opt_items names[] = {
1260 {"No", -1},
1261 {"Yes", -1},
1264 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1265 names, 2, NULL);
1268 static bool scrollbar_setting(void)
1270 static const struct opt_items names[] = {
1271 {"Off", -1},
1272 {"On", -1}
1275 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1276 names, 2, NULL);
1278 #endif
1280 static bool autoscroll_speed_setting(void)
1282 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1283 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1286 static bool viewer_options_menu(void)
1288 int m;
1289 bool result;
1291 static const struct menu_item items[] = {
1292 {"Encoding", encoding_setting },
1293 {"Word Wrap", word_wrap_setting },
1294 {"Line Mode", line_mode_setting },
1295 {"Wide View", view_mode_setting },
1296 #ifdef HAVE_LCD_BITMAP
1297 {"Show Scrollbar", scrollbar_setting },
1298 {"Overlap Pages", page_mode_setting },
1299 #endif
1300 {"Scroll Mode", scroll_mode_setting},
1301 {"Auto-Scroll Speed", autoscroll_speed_setting },
1303 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1304 NULL, NULL, NULL, NULL);
1306 result = menu_run(m);
1307 menu_exit(m);
1308 #ifdef HAVE_LCD_BITMAP
1309 rb->lcd_setmargins(0,0);
1311 /* Show-scrollbar mode for current view-width mode */
1312 if (!ONE_SCREEN_FITS_ALL())
1313 if (prefs.scrollbar_mode == true)
1314 init_need_scrollbar();
1315 #endif
1316 return result;
1319 static void viewer_menu(void)
1321 int m;
1322 int result;
1323 static const struct menu_item items[] = {
1324 {"Quit", NULL },
1325 {"Viewer Options", NULL },
1326 {"Show Playback Menu", NULL },
1327 {"Return", NULL },
1330 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1331 result=menu_show(m);
1332 switch (result)
1334 case 0: /* quit */
1335 menu_exit(m);
1336 viewer_exit(NULL);
1337 done = true;
1338 break;
1339 case 1: /* change settings */
1340 done = viewer_options_menu();
1341 break;
1342 case 2: /* playback control */
1343 playback_control(rb);
1344 break;
1345 case 3: /* return */
1346 break;
1348 menu_exit(m);
1349 #ifdef HAVE_LCD_BITMAP
1350 rb->lcd_setmargins(0,0);
1351 #endif
1352 viewer_draw(col);
1355 enum plugin_status plugin_start(struct plugin_api* api, void* file)
1357 int button, i, ok;
1358 int lastbutton = BUTTON_NONE;
1359 bool autoscroll = false;
1360 long old_tick;
1362 rb = api;
1363 old_tick = *rb->current_tick;
1365 if (!file)
1366 return PLUGIN_ERROR;
1368 file_name = file;
1369 ok = viewer_init();
1370 if (!ok) {
1371 rb->splash(HZ, "Error opening file.");
1372 return PLUGIN_ERROR;
1375 viewer_reset_settings(); /* load defaults first */
1376 viewer_load_settings(); /* .. then try to load from disk */
1378 #if LCD_DEPTH > 1
1379 rb->lcd_set_backdrop(NULL);
1380 #endif
1382 viewer_draw(col);
1384 while (!done) {
1386 if(autoscroll)
1388 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1390 viewer_scroll_down();
1391 viewer_draw(col);
1392 old_tick = *rb->current_tick;
1396 button = rb->button_get_w_tmo(HZ/10);
1397 switch (button) {
1398 case VIEWER_MENU:
1399 viewer_menu();
1400 break;
1402 case VIEWER_AUTOSCROLL:
1403 #ifdef VIEWER_AUTOSCROLL_PRE
1404 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1405 break;
1406 #endif
1407 autoscroll = !autoscroll;
1408 break;
1410 case VIEWER_PAGE_UP:
1411 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1412 if (prefs.scroll_mode == PAGE)
1414 /* Page up */
1415 #ifdef HAVE_LCD_BITMAP
1416 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1417 #else
1418 for (i = 0; i < display_lines; i++)
1419 #endif
1420 viewer_scroll_up();
1422 else
1423 viewer_scroll_up();
1424 old_tick = *rb->current_tick;
1425 viewer_draw(col);
1426 break;
1428 case VIEWER_PAGE_DOWN:
1429 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1430 if (prefs.scroll_mode == PAGE)
1432 /* Page down */
1433 if (next_screen_ptr != NULL)
1434 screen_top_ptr = next_screen_to_draw_ptr;
1436 else
1437 viewer_scroll_down();
1438 old_tick = *rb->current_tick;
1439 viewer_draw(col);
1440 break;
1442 case VIEWER_SCREEN_LEFT:
1443 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1444 if (prefs.view_mode == WIDE) {
1445 /* Screen left */
1446 col -= draw_columns;
1447 col = col_limit(col);
1449 else { /* prefs.view_mode == NARROW */
1450 /* Top of file */
1451 viewer_top();
1454 viewer_draw(col);
1455 break;
1457 case VIEWER_SCREEN_RIGHT:
1458 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1459 if (prefs.view_mode == WIDE) {
1460 /* Screen right */
1461 col += draw_columns;
1462 col = col_limit(col);
1464 else { /* prefs.view_mode == NARROW */
1465 /* Bottom of file */
1466 viewer_bottom();
1469 viewer_draw(col);
1470 break;
1472 #ifdef VIEWER_LINE_UP
1473 case VIEWER_LINE_UP:
1474 case VIEWER_LINE_UP | BUTTON_REPEAT:
1475 /* Scroll up one line */
1476 viewer_scroll_up();
1477 old_tick = *rb->current_tick;
1478 viewer_draw(col);
1479 break;
1481 case VIEWER_LINE_DOWN:
1482 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1483 /* Scroll down one line */
1484 if (next_screen_ptr != NULL)
1485 screen_top_ptr = next_line_ptr;
1486 old_tick = *rb->current_tick;
1487 viewer_draw(col);
1488 break;
1489 #endif
1490 #ifdef VIEWER_COLUMN_LEFT
1491 case VIEWER_COLUMN_LEFT:
1492 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1493 if (prefs.view_mode == WIDE) {
1494 /* Scroll left one column */
1495 col -= glyph_width('o');
1496 col = col_limit(col);
1497 viewer_draw(col);
1499 break;
1501 case VIEWER_COLUMN_RIGHT:
1502 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1503 if (prefs.view_mode == WIDE) {
1504 /* Scroll right one column */
1505 col += glyph_width('o');
1506 col = col_limit(col);
1507 viewer_draw(col);
1509 break;
1510 #endif
1512 #ifdef VIEWER_RC_QUIT
1513 case VIEWER_RC_QUIT:
1514 #endif
1515 case VIEWER_QUIT:
1516 viewer_exit(NULL);
1517 done = true;
1518 break;
1520 default:
1521 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1522 == SYS_USB_CONNECTED)
1523 return PLUGIN_USB_CONNECTED;
1524 break;
1526 if (button != BUTTON_NONE)
1528 lastbutton = button;
1529 rb->yield();
1532 return PLUGIN_OK;