Update the Swedish translation.
[kugel-rb.git] / apps / plugins / viewer.c
blobd3f74e6514b526bdeea635a4c4b182ee8fd76ccc
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 "/.rockbox/viewers/viewer.dat" /* binary file, so dont use .cfg */
28 #define BOOKMARKS_FILE "/.rockbox/viewers/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 with the 4G pad */
133 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_3G_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_UP
184 #define VIEWER_LINE_DOWN BUTTON_SCROLL_DOWN
186 /* iriver H10 keys */
187 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
188 #define VIEWER_QUIT BUTTON_POWER
189 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
190 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
191 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
192 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
193 #define VIEWER_MENU BUTTON_REW
194 #define VIEWER_AUTOSCROLL BUTTON_PLAY
196 #endif
198 /* stuff for the bookmarking */
199 struct bookmarked_file_info {
200 long file_position;
201 int top_ptr_pos;
202 char filename[MAX_PATH];
205 struct bookmark_file_data {
206 signed int bookmarked_files_count;
207 struct bookmarked_file_info bookmarks[];
210 struct preferences {
211 enum {
212 WRAP=0,
213 CHOP,
214 } word_mode;
216 enum {
217 NORMAL=0,
218 JOIN,
219 EXPAND,
220 REFLOW, /* won't be set on charcell LCD, must be last */
221 } line_mode;
223 enum {
224 NARROW=0,
225 WIDE,
226 } view_mode;
228 enum {
229 ISO_8859_1=0,
230 ISO_8859_7,
231 ISO_8859_8,
232 CP1251,
233 ISO_8859_11,
234 ISO_8859_6,
235 ISO_8859_9,
236 ISO_8859_2,
237 SJIS,
238 GB2312,
239 KSX1001,
240 BIG5,
241 UTF8,
242 ENCODINGS
243 } encoding; /* FIXME: What should default encoding be? */
245 #ifdef HAVE_LCD_BITMAP
246 enum {
247 SB_OFF=0,
248 SB_ON,
249 } scrollbar_mode;
250 bool need_scrollbar;
252 enum {
253 NO_OVERLAP=0,
254 OVERLAP,
255 } page_mode;
256 #endif /* HAVE_LCD_BITMAP */
258 enum {
259 PAGE=0,
260 LINE,
261 } scroll_mode;
263 int autoscroll_speed;
265 } prefs;
267 static unsigned char buffer[BUFFER_SIZE + 1];
268 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
269 static int display_columns; /* number of (pixel) columns on the display */
270 static int display_lines; /* number of lines on the display */
271 static int draw_columns; /* number of (pixel) columns available for text */
272 static int par_indent_spaces; /* number of spaces to indent first paragraph */
273 static int fd;
274 static char *file_name;
275 static long file_size;
276 static bool mac_text;
277 static long file_pos; /* Position of the top of the buffer in the file */
278 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
279 static int max_line_len;
280 static unsigned char *screen_top_ptr;
281 static unsigned char *next_screen_ptr;
282 static unsigned char *next_screen_to_draw_ptr;
283 static unsigned char *next_line_ptr;
284 static struct plugin_api* rb;
285 #ifdef HAVE_LCD_BITMAP
286 static struct font *pf;
287 #endif
290 int glyph_width(int ch)
292 if (ch == 0)
293 ch = ' ';
295 #ifdef HAVE_LCD_BITMAP
296 return rb->font_get_width(pf, ch);
297 #else
298 return 1;
299 #endif
302 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
304 unsigned char utf8_tmp[6];
305 int count;
307 if (prefs.encoding == UTF8)
308 return (unsigned char*)rb->utf8decode(str, ch);
310 count = BUFFER_OOB(str+2)? 1:2;
311 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
312 rb->utf8decode(utf8_tmp, ch);
314 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
315 return (unsigned char*)str+1;
316 else
317 return (unsigned char*)str+2;
320 bool done = false;
321 int col = 0;
323 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
324 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
325 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
326 static unsigned char* crop_at_width(const unsigned char* p)
328 int k,width;
329 unsigned short ch;
330 const unsigned char *oldp = p;
332 k=width=0;
334 while (LINE_IS_NOT_FULL) {
335 oldp = p;
336 p = get_ucs(p, &ch);
337 ADVANCE_COUNTERS(ch);
340 return (unsigned char*)oldp;
343 static unsigned char* find_first_feed(const unsigned char* p, int size)
345 int i;
347 for (i=0; i < size; i++)
348 if (p[i] == 0)
349 return (unsigned char*) p+i;
351 return NULL;
354 static unsigned char* find_last_feed(const unsigned char* p, int size)
356 int i;
358 for (i=size-1; i>=0; i--)
359 if (p[i] == 0)
360 return (unsigned char*) p+i;
362 return NULL;
365 static unsigned char* find_last_space(const unsigned char* p, int size)
367 int i, j, k;
369 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
371 if (!BUFFER_OOB(&p[size]))
372 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
373 if (p[size] == line_break[j])
374 return (unsigned char*) p+size;
376 for (i=size-1; i>=0; i--)
377 for (j=k; j < (int) sizeof(line_break); j++)
378 if (p[i] == line_break[j])
379 return (unsigned char*) p+i;
381 return NULL;
384 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
386 const unsigned char *next_line = NULL;
387 int size, i, j, k, width, search_len, spaces, newlines;
388 bool first_chars;
389 unsigned char c;
391 if (is_short != NULL)
392 *is_short = true;
394 if BUFFER_OOB(cur_line)
395 return NULL;
397 if (prefs.view_mode == WIDE) {
398 search_len = MAX_WIDTH;
400 else { /* prefs.view_mode == NARROW */
401 search_len = crop_at_width(cur_line) - cur_line;
404 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
406 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
407 /* Need to scan ahead and possibly increase search_len and size,
408 or possibly set next_line at second hard return in a row. */
409 next_line = NULL;
410 first_chars=true;
411 for (j=k=width=spaces=newlines=0; ; j++) {
412 if (BUFFER_OOB(cur_line+j))
413 return NULL;
414 if (LINE_IS_FULL) {
415 size = search_len = j;
416 break;
419 c = cur_line[j];
420 switch (c) {
421 case ' ':
422 if (prefs.line_mode == REFLOW) {
423 if (newlines > 0) {
424 size = j;
425 next_line = cur_line + size;
426 return (unsigned char*) next_line;
428 if (j==0) /* i=1 is intentional */
429 for (i=0; i<par_indent_spaces; i++)
430 ADVANCE_COUNTERS(' ');
432 if (!first_chars) spaces++;
433 break;
435 case 0:
436 if (newlines > 0) {
437 size = j;
438 next_line = cur_line + size - spaces;
439 if (next_line != cur_line)
440 return (unsigned char*) next_line;
441 break;
444 newlines++;
445 size += spaces -1;
446 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
447 return NULL;
448 search_len = size;
449 spaces = first_chars? 0:1;
450 break;
452 default:
453 if (prefs.line_mode==JOIN || newlines>0) {
454 while (spaces) {
455 spaces--;
456 ADVANCE_COUNTERS(' ');
457 if (LINE_IS_FULL) {
458 size = search_len = j;
459 break;
462 newlines=0;
463 } else if (spaces) {
464 /* REFLOW, multiple spaces between words: count only
465 * one. If more are needed, they will be added
466 * while drawing. */
467 search_len = size;
468 spaces=0;
469 ADVANCE_COUNTERS(' ');
470 if (LINE_IS_FULL) {
471 size = search_len = j;
472 break;
475 first_chars = false;
476 ADVANCE_COUNTERS(c);
477 break;
481 else {
482 /* find first hard return */
483 next_line = find_first_feed(cur_line, size);
486 if (next_line == NULL)
487 if (size == search_len) {
488 if (prefs.word_mode == WRAP) /* Find last space */
489 next_line = find_last_space(cur_line, size);
491 if (next_line == NULL)
492 next_line = crop_at_width(cur_line);
493 else
494 if (prefs.word_mode == WRAP)
495 for (i=0;
496 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
497 i++)
498 next_line++;
501 if (prefs.line_mode == EXPAND)
502 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
503 if (next_line[0] == 0)
504 if (next_line != cur_line)
505 return (unsigned char*) next_line;
507 /* If next_line is pointing to a zero, increment it; i.e.,
508 leave the terminator at the end of cur_line. If pointing
509 to a hyphen, increment only if there is room to display
510 the hyphen on current line (won't apply in WIDE mode,
511 since it's guarenteed there won't be room). */
512 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
513 if (next_line[0] == 0)/* ||
514 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
515 next_line++;
517 if (BUFFER_OOB(next_line))
518 return NULL;
520 if (is_short)
521 *is_short = false;
523 return (unsigned char*) next_line;
526 static unsigned char* find_prev_line(const unsigned char* cur_line)
528 const unsigned char *prev_line = NULL;
529 const unsigned char *p;
531 if BUFFER_OOB(cur_line)
532 return NULL;
534 /* To wrap consistently at the same places, we must
535 start with a known hard return, then work downwards.
536 We can either search backwards for a hard return,
537 or simply start wrapping downwards from top of buffer.
538 If current line is not near top of buffer, this is
539 a file with long lines (paragraphs). We would need to
540 read earlier sectors before we could decide how to
541 properly wrap the lines above the current line, but
542 it probably is not worth the disk access. Instead,
543 start with top of buffer and wrap down from there.
544 This may result in some lines wrapping at different
545 points from where they wrap when scrolling down.
546 If buffer is at top of file, start at top of buffer. */
548 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
549 prev_line = p = NULL;
550 else
551 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
552 /* Null means no line feeds in buffer above current line. */
554 if (prev_line == NULL)
555 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
556 prev_line = p = buffer;
557 /* (else return NULL and read previous block) */
559 /* Wrap downwards until too far, then use the one before. */
560 while (p < cur_line && p != NULL) {
561 prev_line = p;
562 p = find_next_line(prev_line, NULL);
565 if (BUFFER_OOB(prev_line))
566 return NULL;
568 return (unsigned char*) prev_line;
571 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
573 /* Read from file and preprocess the data */
574 /* To minimize disk access, always read on sector boundaries */
575 unsigned numread, i;
576 bool found_CR = false;
578 rb->lseek(fd, pos, SEEK_SET);
579 numread = rb->read(fd, buf, size);
580 rb->button_clear_queue(); /* clear button queue */
582 for(i = 0; i < numread; i++) {
583 switch(buf[i]) {
584 case '\r':
585 if (mac_text) {
586 buf[i] = 0;
588 else {
589 buf[i] = ' ';
590 found_CR = true;
592 break;
594 case '\n':
595 buf[i] = 0;
596 found_CR = false;
597 break;
599 case 0: /* No break between case 0 and default, intentionally */
600 buf[i] = ' ';
601 default:
602 if (found_CR) {
603 buf[i - 1] = 0;
604 found_CR = false;
605 mac_text = true;
607 break;
612 static int read_and_synch(int direction)
614 /* Read next (or prev) block, and reposition global pointers. */
615 /* direction: 1 for down (i.e., further into file), -1 for up */
616 int move_size, move_vector, offset;
617 unsigned char *fill_buf;
619 if (direction == -1) /* up */ {
620 move_size = SMALL_BLOCK_SIZE;
621 offset = 0;
622 fill_buf = TOP_SECTOR;
623 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
624 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
626 else /* down */ {
627 if (prefs.view_mode == WIDE) {
628 /* WIDE mode needs more buffer so we have to read smaller blocks */
629 move_size = SMALL_BLOCK_SIZE;
630 offset = LARGE_BLOCK_SIZE;
631 fill_buf = BOTTOM_SECTOR;
632 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
633 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
635 else {
636 move_size = LARGE_BLOCK_SIZE;
637 offset = SMALL_BLOCK_SIZE;
638 fill_buf = MID_SECTOR;
639 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
642 move_vector = direction * move_size;
643 screen_top_ptr -= move_vector;
644 file_pos += move_vector;
645 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
646 fill_buffer(file_pos + offset, fill_buf, move_size);
647 return move_vector;
650 static void viewer_scroll_up(void)
652 unsigned char *p;
654 p = find_prev_line(screen_top_ptr);
655 if (p == NULL && !BUFFER_BOF()) {
656 read_and_synch(-1);
657 p = find_prev_line(screen_top_ptr);
659 if (p != NULL)
660 screen_top_ptr = p;
663 static void viewer_scroll_down(void)
665 if (next_screen_ptr != NULL)
666 screen_top_ptr = next_line_ptr;
669 #ifdef HAVE_LCD_BITMAP
670 static void viewer_scrollbar(void) {
671 int items, min_shown, max_shown;
673 items = (int) file_size; /* (SH1 int is same as long) */
674 min_shown = (int) file_pos + (screen_top_ptr - buffer);
676 if (next_screen_ptr == NULL)
677 max_shown = items;
678 else
679 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
681 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
682 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
684 #endif
686 static void viewer_draw(int col)
688 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
689 int width, extra_spaces, indent_spaces, spaces_per_word;
690 bool multiple_spacing, line_is_short;
691 unsigned short ch;
692 unsigned char *str, *oldstr;
693 unsigned char *line_begin;
694 unsigned char *line_end;
695 unsigned char c;
696 unsigned char scratch_buffer[MAX_COLUMNS + 1];
697 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
698 unsigned char *endptr;
700 /* If col==-1 do all calculations but don't display */
701 if (col != -1) {
702 #ifdef HAVE_LCD_BITMAP
703 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
704 #else
705 left_col = 0;
706 #endif
707 rb->lcd_clear_display();
709 max_line_len = 0;
710 line_begin = line_end = screen_top_ptr;
712 for (i = 0; i < display_lines; i++) {
713 if (BUFFER_OOB(line_end))
714 break; /* Happens after display last line at BUFFER_EOF() */
716 line_begin = line_end;
717 line_end = find_next_line(line_begin, &line_is_short);
719 if (line_end == NULL) {
720 if (BUFFER_EOF()) {
721 if (i < display_lines - 1 && !BUFFER_BOF()) {
722 if (col != -1)
723 rb->lcd_clear_display();
725 for (; i < display_lines - 1; i++)
726 viewer_scroll_up();
728 line_begin = line_end = screen_top_ptr;
729 i = -1;
730 continue;
732 else {
733 line_end = buffer_end;
736 else {
737 resynch_move = read_and_synch(1); /* Read block & move ptrs */
738 line_begin -= resynch_move;
739 if (i > 0)
740 next_line_ptr -= resynch_move;
742 line_end = find_next_line(line_begin, NULL);
743 if (line_end == NULL) /* Should not really happen */
744 break;
747 line_len = line_end - line_begin;
749 /* calculate line_len */
750 str = oldstr = line_begin;
751 j = -1;
752 while (str < line_end) {
753 oldstr = str;
754 str = crop_at_width(str);
755 j++;
757 line_width = j*draw_columns;
758 while (oldstr < line_end) {
759 oldstr = get_ucs(oldstr, &ch);
760 line_width += glyph_width(ch);
763 if (prefs.line_mode == JOIN) {
764 if (line_begin[0] == 0) {
765 line_begin++;
766 if (prefs.word_mode == CHOP)
767 line_end++;
768 else
769 line_len--;
771 for (j=k=spaces=0; j < line_len; j++) {
772 if (k == MAX_COLUMNS)
773 break;
775 c = line_begin[j];
776 switch (c) {
777 case ' ':
778 spaces++;
779 break;
780 case 0:
781 spaces = 0;
782 scratch_buffer[k++] = ' ';
783 break;
784 default:
785 while (spaces) {
786 spaces--;
787 scratch_buffer[k++] = ' ';
788 if (k == MAX_COLUMNS - 1)
789 break;
791 scratch_buffer[k++] = c;
792 break;
795 if (col != -1) {
796 scratch_buffer[k] = 0;
797 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
798 prefs.encoding, draw_columns/glyph_width('i'));
799 *endptr = 0;
802 else if (prefs.line_mode == REFLOW) {
803 if (line_begin[0] == 0) {
804 line_begin++;
805 if (prefs.word_mode == CHOP)
806 line_end++;
807 else
808 line_len--;
811 indent_spaces = 0;
812 if (!line_is_short) {
813 multiple_spacing = false;
814 width=spaces=0;
815 for (str = line_begin; str < line_end; ) {
816 str = get_ucs(str, &ch);
817 switch (ch) {
818 case ' ':
819 case 0:
820 if ((str == line_begin) && (prefs.word_mode==WRAP))
821 /* special case: indent the paragraph,
822 * don't count spaces */
823 indent_spaces = par_indent_spaces;
824 else if (!multiple_spacing)
825 spaces++;
826 multiple_spacing = true;
827 break;
828 default:
829 multiple_spacing = false;
830 width += glyph_width(ch);
831 break;
834 if (multiple_spacing) spaces--;
836 if (spaces) {
837 /* total number of spaces to insert between words */
838 extra_spaces = (draw_columns-width)/glyph_width(' ')
839 - indent_spaces;
840 /* number of spaces between each word*/
841 spaces_per_word = extra_spaces / spaces;
842 /* number of words with n+1 spaces (to fill up) */
843 extra_spaces = extra_spaces % spaces;
844 if (spaces_per_word > 2) { /* too much spacing is awful */
845 spaces_per_word = 3;
846 extra_spaces = 0;
848 } else { /* this doesn't matter much... no spaces anyway */
849 spaces_per_word = extra_spaces = 0;
851 } else { /* end of a paragraph: don't fill line */
852 spaces_per_word = 1;
853 extra_spaces = 0;
856 multiple_spacing = false;
857 for (j=k=spaces=0; j < line_len; j++) {
858 if (k == MAX_COLUMNS)
859 break;
861 c = line_begin[j];
862 switch (c) {
863 case ' ':
864 case 0:
865 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
866 for (j=0; j<par_indent_spaces; j++)
867 scratch_buffer[k++] = ' ';
868 j=0;
870 else if (!multiple_spacing) {
871 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
872 scratch_buffer[k++] = ' ';
873 spaces++;
875 multiple_spacing = true;
876 break;
877 default:
878 scratch_buffer[k++] = c;
879 multiple_spacing = false;
880 break;
884 if (col != -1) {
885 scratch_buffer[k] = 0;
886 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
887 prefs.encoding, k-col);
888 *endptr = 0;
891 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
892 if (col != -1)
893 if (line_width > col) {
894 str = oldstr = line_begin;
895 k = col;
896 width = 0;
897 while( (width<draw_columns) && (oldstr<line_end) )
899 oldstr = get_ucs(oldstr, &ch);
900 if (k > 0) {
901 k -= glyph_width(ch);
902 line_begin = oldstr;
903 } else {
904 width += glyph_width(ch);
908 if(prefs.view_mode==WIDE)
909 endptr = rb->iso_decode(line_begin, utf8_buffer,
910 prefs.encoding, oldstr-line_begin);
911 else
912 endptr = rb->iso_decode(line_begin, utf8_buffer,
913 prefs.encoding, line_end-line_begin);
914 *endptr = 0;
917 if (col != -1 && line_width > col)
918 #ifdef HAVE_LCD_BITMAP
919 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
920 #else
921 rb->lcd_puts(left_col, i, utf8_buffer);
922 #endif
923 if (line_width > max_line_len)
924 max_line_len = line_width;
926 if (i == 0)
927 next_line_ptr = line_end;
929 next_screen_ptr = line_end;
930 if (BUFFER_OOB(next_screen_ptr))
931 next_screen_ptr = NULL;
933 #ifdef HAVE_LCD_BITMAP
934 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
936 if (prefs.need_scrollbar)
937 viewer_scrollbar();
938 #else
939 next_screen_to_draw_ptr = next_screen_ptr;
940 #endif
942 if (col != -1)
943 rb->lcd_update();
946 static void viewer_top(void)
948 /* Read top of file into buffer
949 and point screen pointer to top */
950 file_pos = 0;
951 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
952 screen_top_ptr = buffer;
953 fill_buffer(0, buffer, BUFFER_SIZE);
956 static void viewer_bottom(void)
958 /* Read bottom of file into buffer
959 and point screen pointer to bottom */
960 long last_sectors;
962 if (file_size > BUFFER_SIZE) {
963 /* Find last buffer in file, round up to next sector boundary */
964 last_sectors = file_size - BUFFER_SIZE + SMALL_BLOCK_SIZE;
965 last_sectors /= SMALL_BLOCK_SIZE;
966 last_sectors *= SMALL_BLOCK_SIZE;
968 else {
969 last_sectors = 0;
971 file_pos = last_sectors;
972 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
973 screen_top_ptr = buffer_end-1;
974 fill_buffer(last_sectors, buffer, BUFFER_SIZE);
977 #ifdef HAVE_LCD_BITMAP
978 static void init_need_scrollbar(void) {
979 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
980 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
981 viewer_draw(-1);
982 prefs.need_scrollbar = NEED_SCROLLBAR();
983 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
984 par_indent_spaces = draw_columns/(5*glyph_width(' '));
986 #else
987 #define init_need_scrollbar()
988 #endif
990 static bool viewer_init(void)
992 #ifdef HAVE_LCD_BITMAP
994 pf = rb->font_get(FONT_UI);
996 display_lines = LCD_HEIGHT / pf->height;
997 draw_columns = display_columns = LCD_WIDTH;
998 #else
999 /* REAL fixed pitch :) all chars use up 1 cell */
1000 display_lines = 2;
1001 draw_columns = display_columns = 11;
1002 par_indent_spaces = 2;
1003 #endif
1005 fd = rb->open(file_name, O_RDONLY);
1006 if (fd==-1)
1007 return false;
1009 file_size = rb->filesize(fd);
1010 if (file_size==-1)
1011 return false;
1013 /* Init mac_text value used in processing buffer */
1014 mac_text = false;
1016 /* Set codepage to system default */
1017 prefs.encoding = rb->global_settings->default_codepage;
1019 /* Read top of file into buffer;
1020 init file_pos, buffer_end, screen_top_ptr */
1021 viewer_top();
1023 /* Init prefs.need_scrollbar value */
1024 init_need_scrollbar();
1026 return true;
1029 static void viewer_reset_settings(void)
1031 prefs.word_mode = WRAP;
1032 prefs.line_mode = NORMAL;
1033 prefs.view_mode = NARROW;
1034 prefs.scroll_mode = PAGE;
1035 #ifdef HAVE_LCD_BITMAP
1036 prefs.page_mode = NO_OVERLAP;
1037 prefs.scrollbar_mode = SB_OFF;
1038 #endif
1039 prefs.autoscroll_speed = 1;
1042 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1044 int settings_fd;
1046 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1047 if (settings_fd < 0)
1049 rb->splash(HZ*2, "No Settings File");
1050 return;
1052 if (rb->filesize(settings_fd) != sizeof(struct preferences))
1054 rb->splash(HZ*2, "Settings File Invalid");
1055 return;
1058 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1059 rb->close(settings_fd);
1060 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1061 if (settings_fd >= 0)
1063 struct bookmark_file_data *data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1064 int i;
1065 data->bookmarked_files_count = 0;
1066 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int)); /* figure out how many items to read */
1067 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1068 data->bookmarked_files_count = MAX_BOOKMARKED_FILES; /* dump the older files */
1069 rb->read(settings_fd, data->bookmarks,
1070 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1071 rb->close(settings_fd);
1072 for (i=0; i < data->bookmarked_files_count; i++)
1074 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1075 break;
1077 if (i < data->bookmarked_files_count)
1079 /* it is in the list, write everything back in the correct order, and reload the file correctly */
1080 settings_fd = rb->creat(BOOKMARKS_FILE);
1081 if (settings_fd >=0 )
1083 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1084 data->bookmarked_files_count = MAX_BOOKMARKED_FILES; /* dump the older files */
1085 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1086 /* write this item, then all up to it, then all after it */
1087 rb->write (settings_fd, &data->bookmarks[i], sizeof(struct bookmarked_file_info));
1088 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1089 rb->write (settings_fd, &data->bookmarks[i+1],
1090 sizeof(struct bookmarked_file_info)*(data->bookmarked_files_count-i-1));
1091 rb->close(settings_fd);
1093 file_pos = data->bookmarks[i].file_position;
1094 screen_top_ptr = buffer + data->bookmarks[i].top_ptr_pos;
1096 else /* not in list, write the list to the file */
1098 settings_fd = rb->creat(BOOKMARKS_FILE);
1099 if (settings_fd >=0 )
1101 if ((data->bookmarked_files_count + 1) > MAX_BOOKMARKED_FILES)
1102 data->bookmarked_files_count = MAX_BOOKMARKED_FILES; /* dump the older files */
1103 else data->bookmarked_files_count++;
1104 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1105 rb->PREFIX(lseek)(settings_fd,sizeof(struct bookmarked_file_info),SEEK_CUR);
1106 // rb->memset(&dummy,0,sizeof(struct bookmarked_file_info)); /* the actual info will be written on exit */
1107 //rb->write (settings_fd, &dummy, sizeof(struct bookmarked_file_info));
1108 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*(data->bookmarked_files_count));
1109 rb->close(settings_fd);
1112 } /* BOOKMARKS_FILE opened ok */
1113 init_need_scrollbar();
1115 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1117 if (BUFFER_OOB(screen_top_ptr)) {
1118 screen_top_ptr = buffer;
1121 fill_buffer(file_pos, buffer, BUFFER_SIZE);
1124 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1126 int settings_fd;
1127 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1129 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1130 rb->close(settings_fd);
1132 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1133 if (settings_fd >= 0 )
1135 struct bookmarked_file_info b;
1136 b.file_position = file_pos;
1137 b.top_ptr_pos = screen_top_ptr - buffer;
1138 rb->memset(&b.filename[0],0,MAX_PATH);
1139 rb->strcpy(b.filename,file_name);
1140 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1141 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1142 rb->close(settings_fd);
1146 static void viewer_exit(void *parameter)
1148 (void)parameter;
1150 viewer_save_settings();
1151 rb->close(fd);
1154 static int col_limit(int col)
1156 if (col < 0)
1157 col = 0;
1158 else
1159 if (col > max_line_len - 2*glyph_width('o'))
1160 col = max_line_len - 2*glyph_width('o');
1162 return col;
1165 /* settings helper functions */
1167 static bool encoding_setting(void)
1169 static const struct opt_items names[] = {
1170 {"ISO-8859-1", -1},
1171 {"ISO-8859-7", -1},
1172 {"ISO-8859-8", -1},
1173 {"CP1251", -1},
1174 {"ISO-8859-11", -1},
1175 {"ISO-8859-6", -1},
1176 {"ISO-8859-9", -1},
1177 {"ISO-8859-2", -1},
1178 {"SJIS", -1},
1179 {"GB-2312", -1},
1180 {"KSX-1001", -1},
1181 {"BIG5", -1},
1182 {"UTF-8", -1},
1185 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1186 sizeof(names) / sizeof(names[0]), NULL);
1189 static bool word_wrap_setting(void)
1191 static const struct opt_items names[] = {
1192 {"On", -1},
1193 {"Off (Chop Words)", -1},
1196 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1197 names, 2, NULL);
1200 static bool line_mode_setting(void)
1202 static const struct opt_items names[] = {
1203 {"Normal", -1},
1204 {"Join Lines", -1},
1205 {"Expand Lines", -1},
1206 #ifdef HAVE_LCD_BITMAP
1207 {"Reflow Lines", -1},
1208 #endif
1211 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1212 sizeof(names) / sizeof(names[0]), NULL);
1215 static bool view_mode_setting(void)
1217 static const struct opt_items names[] = {
1218 {"No (Narrow)", -1},
1219 {"Yes", -1},
1221 bool ret;
1222 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1223 names , 2, NULL);
1224 if (prefs.view_mode == NARROW)
1225 col = 0;
1226 return ret;
1229 static bool scroll_mode_setting(void)
1231 static const struct opt_items names[] = {
1232 {"Scroll by Page", -1},
1233 {"Scroll by Line", -1},
1236 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1237 names, 2, NULL);
1240 #ifdef HAVE_LCD_BITMAP
1241 static bool page_mode_setting(void)
1243 static const struct opt_items names[] = {
1244 {"No", -1},
1245 {"Yes", -1},
1248 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1249 names, 2, NULL);
1252 static bool scrollbar_setting(void)
1254 static const struct opt_items names[] = {
1255 {"Off", -1},
1256 {"On", -1}
1259 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1260 names, 2, NULL);
1262 #endif
1264 static bool autoscroll_speed_setting(void)
1266 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1267 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1270 static bool viewer_options_menu(void)
1272 int m;
1273 bool result;
1275 static const struct menu_item items[] = {
1276 {"Encoding", encoding_setting },
1277 {"Word Wrap", word_wrap_setting },
1278 {"Line Mode", line_mode_setting },
1279 {"Wide View", view_mode_setting },
1280 #ifdef HAVE_LCD_BITMAP
1281 {"Show Scrollbar", scrollbar_setting },
1282 {"Overlap Pages", page_mode_setting },
1283 #endif
1284 {"Scroll Mode", scroll_mode_setting},
1285 {"Auto-Scroll Speed", autoscroll_speed_setting },
1287 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1288 NULL, NULL, NULL, NULL);
1290 result = menu_run(m);
1291 menu_exit(m);
1292 #ifdef HAVE_LCD_BITMAP
1293 rb->lcd_setmargins(0,0);
1295 /* Show-scrollbar mode for current view-width mode */
1296 if (!ONE_SCREEN_FITS_ALL())
1297 if (prefs.scrollbar_mode == true)
1298 init_need_scrollbar();
1299 #endif
1300 return result;
1303 static void viewer_menu(void)
1305 int m;
1306 int result;
1307 static const struct menu_item items[] = {
1308 {"Quit", NULL },
1309 {"Viewer Options", NULL },
1310 {"Show Playback Menu", NULL },
1311 {"Return", NULL },
1314 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1315 result=menu_show(m);
1316 switch (result)
1318 case 0: /* quit */
1319 rb->splash(1, "Saving Settings");
1320 menu_exit(m);
1321 viewer_exit(NULL);
1322 done = true;
1323 break;
1324 case 1: /* change settings */
1325 done = viewer_options_menu();
1326 break;
1327 case 2: /* playback control */
1328 playback_control(rb);
1329 break;
1330 case 3: /* return */
1331 break;
1333 menu_exit(m);
1334 #ifdef HAVE_LCD_BITMAP
1335 rb->lcd_setmargins(0,0);
1336 #endif
1337 viewer_draw(col);
1340 enum plugin_status plugin_start(struct plugin_api* api, void* file)
1342 int button, i, ok;
1343 int lastbutton = BUTTON_NONE;
1344 bool autoscroll = false;
1345 long old_tick;
1347 rb = api;
1348 old_tick = *rb->current_tick;
1350 if (!file)
1351 return PLUGIN_ERROR;
1353 file_name = file;
1354 ok = viewer_init();
1355 if (!ok) {
1356 rb->splash(HZ, "Error");
1357 viewer_exit(NULL);
1358 return PLUGIN_OK;
1361 viewer_reset_settings(); /* load defaults first */
1362 viewer_load_settings(); /* .. then try to load from disk */
1364 viewer_draw(col);
1366 while (!done) {
1368 if(autoscroll)
1370 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1372 viewer_scroll_down();
1373 viewer_draw(col);
1374 old_tick = *rb->current_tick;
1378 button = rb->button_get_w_tmo(HZ/10);
1379 switch (button) {
1380 case VIEWER_MENU:
1381 viewer_menu();
1382 break;
1384 case VIEWER_AUTOSCROLL:
1385 #ifdef VIEWER_AUTOSCROLL_PRE
1386 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1387 break;
1388 #endif
1389 autoscroll = !autoscroll;
1390 break;
1392 case VIEWER_PAGE_UP:
1393 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1394 if (prefs.scroll_mode == PAGE)
1396 /* Page up */
1397 #ifdef HAVE_LCD_BITMAP
1398 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1399 #else
1400 for (i = 0; i < display_lines; i++)
1401 #endif
1402 viewer_scroll_up();
1404 else
1405 viewer_scroll_up();
1406 old_tick = *rb->current_tick;
1407 viewer_draw(col);
1408 break;
1410 case VIEWER_PAGE_DOWN:
1411 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1412 if (prefs.scroll_mode == PAGE)
1414 /* Page down */
1415 if (next_screen_ptr != NULL)
1416 screen_top_ptr = next_screen_to_draw_ptr;
1418 else
1419 viewer_scroll_down();
1420 old_tick = *rb->current_tick;
1421 viewer_draw(col);
1422 break;
1424 case VIEWER_SCREEN_LEFT:
1425 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1426 if (prefs.view_mode == WIDE) {
1427 /* Screen left */
1428 col -= draw_columns;
1429 col = col_limit(col);
1431 else { /* prefs.view_mode == NARROW */
1432 /* Top of file */
1433 viewer_top();
1436 viewer_draw(col);
1437 break;
1439 case VIEWER_SCREEN_RIGHT:
1440 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1441 if (prefs.view_mode == WIDE) {
1442 /* Screen right */
1443 col += draw_columns;
1444 col = col_limit(col);
1446 else { /* prefs.view_mode == NARROW */
1447 /* Bottom of file */
1448 viewer_bottom();
1451 viewer_draw(col);
1452 break;
1454 #ifdef VIEWER_LINE_UP
1455 case VIEWER_LINE_UP:
1456 case VIEWER_LINE_UP | BUTTON_REPEAT:
1457 /* Scroll up one line */
1458 viewer_scroll_up();
1459 old_tick = *rb->current_tick;
1460 viewer_draw(col);
1461 break;
1463 case VIEWER_LINE_DOWN:
1464 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1465 /* Scroll down one line */
1466 if (next_screen_ptr != NULL)
1467 screen_top_ptr = next_line_ptr;
1468 old_tick = *rb->current_tick;
1469 viewer_draw(col);
1470 break;
1471 #endif
1472 #ifdef VIEWER_COLUMN_LEFT
1473 case VIEWER_COLUMN_LEFT:
1474 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1475 if (prefs.view_mode == WIDE) {
1476 /* Scroll left one column */
1477 col -= glyph_width('o');
1478 col = col_limit(col);
1479 viewer_draw(col);
1481 break;
1483 case VIEWER_COLUMN_RIGHT:
1484 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1485 if (prefs.view_mode == WIDE) {
1486 /* Scroll right one column */
1487 col += glyph_width('o');
1488 col = col_limit(col);
1489 viewer_draw(col);
1491 break;
1492 #endif
1494 #ifdef VIEWER_RC_QUIT
1495 case VIEWER_RC_QUIT:
1496 #endif
1497 case VIEWER_QUIT:
1498 viewer_exit(NULL);
1499 done = true;
1500 break;
1502 default:
1503 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1504 == SYS_USB_CONNECTED)
1505 return PLUGIN_USB_CONNECTED;
1506 break;
1508 if (button != BUTTON_NONE)
1510 lastbutton = button;
1511 rb->yield();
1514 return PLUGIN_OK;