remove debug leftover
[Rockbox.git] / apps / plugins / viewer.c
blobe5091d052cb6bd7e718355cb680fd55e9eb57207
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include "plugin.h"
21 #include <ctype.h>
22 #include "playback_control.h"
23 #include "oldmenuapi.h"
25 PLUGIN_HEADER
27 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
28 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
30 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
31 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
32 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
33 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
34 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
35 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
36 #define TOP_SECTOR buffer
37 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
38 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
39 #define SCROLLBAR_WIDTH 6
41 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
43 /* Out-Of-Bounds test for any pointer to data in the buffer */
44 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
46 /* Does the buffer contain the beginning of the file? */
47 #define BUFFER_BOF() (file_pos==0)
49 /* Does the buffer contain the end of the file? */
50 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
52 /* Formula for the endpoint address outside of buffer data */
53 #define BUFFER_END() \
54 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
56 /* Is the entire file being shown in one screen? */
57 #define ONE_SCREEN_FITS_ALL() \
58 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
60 /* Is a scrollbar called for on the current screen? */
61 #define NEED_SCROLLBAR() \
62 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
64 /* variable button definitions */
66 /* Recorder keys */
67 #if CONFIG_KEYPAD == RECORDER_PAD
68 #define VIEWER_QUIT BUTTON_OFF
69 #define VIEWER_PAGE_UP BUTTON_UP
70 #define VIEWER_PAGE_DOWN BUTTON_DOWN
71 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
72 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
73 #define VIEWER_MENU BUTTON_F1
74 #define VIEWER_AUTOSCROLL BUTTON_PLAY
75 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
76 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
77 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
78 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
80 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
81 #define VIEWER_QUIT BUTTON_OFF
82 #define VIEWER_PAGE_UP BUTTON_UP
83 #define VIEWER_PAGE_DOWN BUTTON_DOWN
84 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
85 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
86 #define VIEWER_MENU BUTTON_F1
87 #define VIEWER_AUTOSCROLL BUTTON_SELECT
88 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
89 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
90 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
91 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
93 /* Ondio keys */
94 #elif CONFIG_KEYPAD == ONDIO_PAD
95 #define VIEWER_QUIT BUTTON_OFF
96 #define VIEWER_PAGE_UP BUTTON_UP
97 #define VIEWER_PAGE_DOWN BUTTON_DOWN
98 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
99 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
100 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
101 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
102 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
104 /* Player keys */
105 #elif CONFIG_KEYPAD == PLAYER_PAD
106 #define VIEWER_QUIT BUTTON_STOP
107 #define VIEWER_PAGE_UP BUTTON_LEFT
108 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
109 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
110 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
111 #define VIEWER_MENU BUTTON_MENU
112 #define VIEWER_AUTOSCROLL BUTTON_PLAY
114 /* iRiver H1x0 && H3x0 keys */
115 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
116 (CONFIG_KEYPAD == IRIVER_H300_PAD)
117 #define VIEWER_QUIT BUTTON_OFF
118 #define VIEWER_PAGE_UP BUTTON_UP
119 #define VIEWER_PAGE_DOWN BUTTON_DOWN
120 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
121 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
122 #define VIEWER_MENU BUTTON_MODE
123 #define VIEWER_AUTOSCROLL BUTTON_SELECT
124 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
125 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
126 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
127 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
129 #define VIEWER_RC_QUIT BUTTON_RC_STOP
131 /* iPods */
132 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
133 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
135 #define VIEWER_QUIT_PRE BUTTON_SELECT
136 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
137 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
138 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
139 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
140 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
141 #define VIEWER_MENU BUTTON_MENU
142 #define VIEWER_AUTOSCROLL BUTTON_PLAY
144 /* iFP7xx keys */
145 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
146 #define VIEWER_QUIT BUTTON_PLAY
147 #define VIEWER_PAGE_UP BUTTON_UP
148 #define VIEWER_PAGE_DOWN BUTTON_DOWN
149 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
150 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
151 #define VIEWER_MENU BUTTON_MODE
152 #define VIEWER_AUTOSCROLL BUTTON_SELECT
154 /* iAudio X5 keys */
155 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
156 #define VIEWER_QUIT BUTTON_POWER
157 #define VIEWER_PAGE_UP BUTTON_UP
158 #define VIEWER_PAGE_DOWN BUTTON_DOWN
159 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
160 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
161 #define VIEWER_MENU BUTTON_SELECT
162 #define VIEWER_AUTOSCROLL BUTTON_PLAY
164 /* GIGABEAT keys */
165 #elif CONFIG_KEYPAD == GIGABEAT_PAD
166 #define VIEWER_QUIT BUTTON_POWER
167 #define VIEWER_PAGE_UP BUTTON_UP
168 #define VIEWER_PAGE_DOWN BUTTON_DOWN
169 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
170 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
171 #define VIEWER_MENU BUTTON_MENU
172 #define VIEWER_AUTOSCROLL BUTTON_A
174 /* Sansa E200 keys */
175 #elif CONFIG_KEYPAD == SANSA_E200_PAD
176 #define VIEWER_QUIT BUTTON_POWER
177 #define VIEWER_PAGE_UP BUTTON_UP
178 #define VIEWER_PAGE_DOWN BUTTON_DOWN
179 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
180 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
181 #define VIEWER_MENU BUTTON_SELECT
182 #define VIEWER_AUTOSCROLL BUTTON_REC
183 #define VIEWER_LINE_UP BUTTON_SCROLL_UP
184 #define VIEWER_LINE_DOWN BUTTON_SCROLL_DOWN
186 /* Sansa C200 keys */
187 #elif CONFIG_KEYPAD == SANSA_C200_PAD
188 #define VIEWER_QUIT BUTTON_POWER
189 #define VIEWER_PAGE_UP BUTTON_VOL_UP
190 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
191 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
192 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
193 #define VIEWER_MENU BUTTON_SELECT
194 #define VIEWER_AUTOSCROLL BUTTON_REC
195 #define VIEWER_LINE_UP BUTTON_UP
196 #define VIEWER_LINE_DOWN BUTTON_DOWN
198 /* iriver H10 keys */
199 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
200 #define VIEWER_QUIT BUTTON_POWER
201 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
202 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
203 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
204 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
205 #define VIEWER_MENU BUTTON_REW
206 #define VIEWER_AUTOSCROLL BUTTON_PLAY
208 #elif CONFIG_KEYPAD == MROBE500_PAD
209 #define VIEWER_QUIT BUTTON_POWER
210 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
211 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
212 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
213 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
214 #define VIEWER_MENU BUTTON_RC_HEART
215 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
217 #endif
219 /* stuff for the bookmarking */
220 struct bookmarked_file_info {
221 long file_position;
222 int top_ptr_pos;
223 char filename[MAX_PATH];
226 struct bookmark_file_data {
227 signed int bookmarked_files_count;
228 struct bookmarked_file_info bookmarks[];
231 struct preferences {
232 enum {
233 WRAP=0,
234 CHOP,
235 } word_mode;
237 enum {
238 NORMAL=0,
239 JOIN,
240 EXPAND,
241 REFLOW, /* won't be set on charcell LCD, must be last */
242 } line_mode;
244 enum {
245 NARROW=0,
246 WIDE,
247 } view_mode;
249 enum {
250 ISO_8859_1=0,
251 ISO_8859_7,
252 ISO_8859_8,
253 CP1251,
254 ISO_8859_11,
255 ISO_8859_6,
256 ISO_8859_9,
257 ISO_8859_2,
258 CP1250,
259 SJIS,
260 GB2312,
261 KSX1001,
262 BIG5,
263 UTF8,
264 ENCODINGS
265 } encoding; /* FIXME: What should default encoding be? */
267 #ifdef HAVE_LCD_BITMAP
268 enum {
269 SB_OFF=0,
270 SB_ON,
271 } scrollbar_mode;
272 bool need_scrollbar;
274 enum {
275 NO_OVERLAP=0,
276 OVERLAP,
277 } page_mode;
278 #endif /* HAVE_LCD_BITMAP */
280 enum {
281 PAGE=0,
282 LINE,
283 } scroll_mode;
285 int autoscroll_speed;
289 struct preferences prefs;
290 struct preferences old_prefs;
292 static unsigned char *buffer;
293 static long buffer_size;
294 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
295 static int display_columns; /* number of (pixel) columns on the display */
296 static int display_lines; /* number of lines on the display */
297 static int draw_columns; /* number of (pixel) columns available for text */
298 static int par_indent_spaces; /* number of spaces to indent first paragraph */
299 static int fd;
300 static char *file_name;
301 static long file_size;
302 static long start_position; /* position in the file after the viewer is started */
303 static bool mac_text;
304 static long file_pos; /* Position of the top of the buffer in the file */
305 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
306 static int max_line_len;
307 static unsigned char *screen_top_ptr;
308 static unsigned char *next_screen_ptr;
309 static unsigned char *next_screen_to_draw_ptr;
310 static unsigned char *next_line_ptr;
311 static struct plugin_api* rb;
312 #ifdef HAVE_LCD_BITMAP
313 static struct font *pf;
314 #endif
317 int glyph_width(int ch)
319 if (ch == 0)
320 ch = ' ';
322 #ifdef HAVE_LCD_BITMAP
323 return rb->font_get_width(pf, ch);
324 #else
325 return 1;
326 #endif
329 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
331 unsigned char utf8_tmp[6];
332 int count;
334 if (prefs.encoding == UTF8)
335 return (unsigned char*)rb->utf8decode(str, ch);
337 count = BUFFER_OOB(str+2)? 1:2;
338 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
339 rb->utf8decode(utf8_tmp, ch);
341 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
342 return (unsigned char*)str+1;
343 else
344 return (unsigned char*)str+2;
347 bool done = false;
348 int col = 0;
350 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
351 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
352 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
353 static unsigned char* crop_at_width(const unsigned char* p)
355 int k,width;
356 unsigned short ch;
357 const unsigned char *oldp = p;
359 k=width=0;
361 while (LINE_IS_NOT_FULL) {
362 oldp = p;
363 p = get_ucs(p, &ch);
364 ADVANCE_COUNTERS(ch);
367 return (unsigned char*)oldp;
370 static unsigned char* find_first_feed(const unsigned char* p, int size)
372 int i;
374 for (i=0; i < size; i++)
375 if (p[i] == 0)
376 return (unsigned char*) p+i;
378 return NULL;
381 static unsigned char* find_last_feed(const unsigned char* p, int size)
383 int i;
385 for (i=size-1; i>=0; i--)
386 if (p[i] == 0)
387 return (unsigned char*) p+i;
389 return NULL;
392 static unsigned char* find_last_space(const unsigned char* p, int size)
394 int i, j, k;
396 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
398 if (!BUFFER_OOB(&p[size]))
399 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
400 if (p[size] == line_break[j])
401 return (unsigned char*) p+size;
403 for (i=size-1; i>=0; i--)
404 for (j=k; j < (int) sizeof(line_break); j++)
406 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
407 if (p[i] == line_break[j])
408 return (unsigned char*) p+i;
411 return NULL;
414 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
416 const unsigned char *next_line = NULL;
417 int size, i, j, k, width, search_len, spaces, newlines;
418 bool first_chars;
419 unsigned char c;
421 if (is_short != NULL)
422 *is_short = true;
424 if BUFFER_OOB(cur_line)
425 return NULL;
427 if (prefs.view_mode == WIDE) {
428 search_len = MAX_WIDTH;
430 else { /* prefs.view_mode == NARROW */
431 search_len = crop_at_width(cur_line) - cur_line;
434 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
436 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
437 /* Need to scan ahead and possibly increase search_len and size,
438 or possibly set next_line at second hard return in a row. */
439 next_line = NULL;
440 first_chars=true;
441 for (j=k=width=spaces=newlines=0; ; j++) {
442 if (BUFFER_OOB(cur_line+j))
443 return NULL;
444 if (LINE_IS_FULL) {
445 size = search_len = j;
446 break;
449 c = cur_line[j];
450 switch (c) {
451 case ' ':
452 if (prefs.line_mode == REFLOW) {
453 if (newlines > 0) {
454 size = j;
455 next_line = cur_line + size;
456 return (unsigned char*) next_line;
458 if (j==0) /* i=1 is intentional */
459 for (i=0; i<par_indent_spaces; i++)
460 ADVANCE_COUNTERS(' ');
462 if (!first_chars) spaces++;
463 break;
465 case 0:
466 if (newlines > 0) {
467 size = j;
468 next_line = cur_line + size - spaces;
469 if (next_line != cur_line)
470 return (unsigned char*) next_line;
471 break;
474 newlines++;
475 size += spaces -1;
476 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
477 return NULL;
478 search_len = size;
479 spaces = first_chars? 0:1;
480 break;
482 default:
483 if (prefs.line_mode==JOIN || newlines>0) {
484 while (spaces) {
485 spaces--;
486 ADVANCE_COUNTERS(' ');
487 if (LINE_IS_FULL) {
488 size = search_len = j;
489 break;
492 newlines=0;
493 } else if (spaces) {
494 /* REFLOW, multiple spaces between words: count only
495 * one. If more are needed, they will be added
496 * while drawing. */
497 search_len = size;
498 spaces=0;
499 ADVANCE_COUNTERS(' ');
500 if (LINE_IS_FULL) {
501 size = search_len = j;
502 break;
505 first_chars = false;
506 ADVANCE_COUNTERS(c);
507 break;
511 else {
512 /* find first hard return */
513 next_line = find_first_feed(cur_line, size);
516 if (next_line == NULL)
517 if (size == search_len) {
518 if (prefs.word_mode == WRAP) /* Find last space */
519 next_line = find_last_space(cur_line, size);
521 if (next_line == NULL)
522 next_line = crop_at_width(cur_line);
523 else
524 if (prefs.word_mode == WRAP)
525 for (i=0;
526 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
527 i++)
528 next_line++;
531 if (prefs.line_mode == EXPAND)
532 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
533 if (next_line[0] == 0)
534 if (next_line != cur_line)
535 return (unsigned char*) next_line;
537 /* If next_line is pointing to a zero, increment it; i.e.,
538 leave the terminator at the end of cur_line. If pointing
539 to a hyphen, increment only if there is room to display
540 the hyphen on current line (won't apply in WIDE mode,
541 since it's guarenteed there won't be room). */
542 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
543 if (next_line[0] == 0)/* ||
544 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
545 next_line++;
547 if (BUFFER_OOB(next_line))
548 return NULL;
550 if (is_short)
551 *is_short = false;
553 return (unsigned char*) next_line;
556 static unsigned char* find_prev_line(const unsigned char* cur_line)
558 const unsigned char *prev_line = NULL;
559 const unsigned char *p;
561 if BUFFER_OOB(cur_line)
562 return NULL;
564 /* To wrap consistently at the same places, we must
565 start with a known hard return, then work downwards.
566 We can either search backwards for a hard return,
567 or simply start wrapping downwards from top of buffer.
568 If current line is not near top of buffer, this is
569 a file with long lines (paragraphs). We would need to
570 read earlier sectors before we could decide how to
571 properly wrap the lines above the current line, but
572 it probably is not worth the disk access. Instead,
573 start with top of buffer and wrap down from there.
574 This may result in some lines wrapping at different
575 points from where they wrap when scrolling down.
576 If buffer is at top of file, start at top of buffer. */
578 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
579 prev_line = p = NULL;
580 else
581 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
582 /* Null means no line feeds in buffer above current line. */
584 if (prev_line == NULL)
585 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
586 prev_line = p = buffer;
587 /* (else return NULL and read previous block) */
589 /* Wrap downwards until too far, then use the one before. */
590 while (p < cur_line && p != NULL) {
591 prev_line = p;
592 p = find_next_line(prev_line, NULL);
595 if (BUFFER_OOB(prev_line))
596 return NULL;
598 return (unsigned char*) prev_line;
601 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
603 /* Read from file and preprocess the data */
604 /* To minimize disk access, always read on sector boundaries */
605 unsigned numread, i;
606 bool found_CR = false;
608 rb->lseek(fd, pos, SEEK_SET);
609 numread = rb->read(fd, buf, size);
610 rb->button_clear_queue(); /* clear button queue */
612 for(i = 0; i < numread; i++) {
613 switch(buf[i]) {
614 case '\r':
615 if (mac_text) {
616 buf[i] = 0;
618 else {
619 buf[i] = ' ';
620 found_CR = true;
622 break;
624 case '\n':
625 buf[i] = 0;
626 found_CR = false;
627 break;
629 case 0: /* No break between case 0 and default, intentionally */
630 buf[i] = ' ';
631 default:
632 if (found_CR) {
633 buf[i - 1] = 0;
634 found_CR = false;
635 mac_text = true;
637 break;
642 static int read_and_synch(int direction)
644 /* Read next (or prev) block, and reposition global pointers. */
645 /* direction: 1 for down (i.e., further into file), -1 for up */
646 int move_size, move_vector, offset;
647 unsigned char *fill_buf;
649 if (direction == -1) /* up */ {
650 move_size = SMALL_BLOCK_SIZE;
651 offset = 0;
652 fill_buf = TOP_SECTOR;
653 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
654 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
656 else /* down */ {
657 if (prefs.view_mode == WIDE) {
658 /* WIDE mode needs more buffer so we have to read smaller blocks */
659 move_size = SMALL_BLOCK_SIZE;
660 offset = LARGE_BLOCK_SIZE;
661 fill_buf = BOTTOM_SECTOR;
662 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
663 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
665 else {
666 move_size = LARGE_BLOCK_SIZE;
667 offset = SMALL_BLOCK_SIZE;
668 fill_buf = MID_SECTOR;
669 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
672 move_vector = direction * move_size;
673 screen_top_ptr -= move_vector;
674 file_pos += move_vector;
675 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
676 fill_buffer(file_pos + offset, fill_buf, move_size);
677 return move_vector;
680 static void viewer_scroll_up(void)
682 unsigned char *p;
684 p = find_prev_line(screen_top_ptr);
685 if (p == NULL && !BUFFER_BOF()) {
686 read_and_synch(-1);
687 p = find_prev_line(screen_top_ptr);
689 if (p != NULL)
690 screen_top_ptr = p;
693 static void viewer_scroll_down(void)
695 if (next_screen_ptr != NULL)
696 screen_top_ptr = next_line_ptr;
699 #ifdef HAVE_LCD_BITMAP
700 static void viewer_scrollbar(void) {
701 int items, min_shown, max_shown;
703 items = (int) file_size; /* (SH1 int is same as long) */
704 min_shown = (int) file_pos + (screen_top_ptr - buffer);
706 if (next_screen_ptr == NULL)
707 max_shown = items;
708 else
709 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
711 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
712 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
714 #endif
716 static void viewer_draw(int col)
718 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
719 int width, extra_spaces, indent_spaces, spaces_per_word;
720 bool multiple_spacing, line_is_short;
721 unsigned short ch;
722 unsigned char *str, *oldstr;
723 unsigned char *line_begin;
724 unsigned char *line_end;
725 unsigned char c;
726 unsigned char scratch_buffer[MAX_COLUMNS + 1];
727 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
728 unsigned char *endptr;
730 /* If col==-1 do all calculations but don't display */
731 if (col != -1) {
732 #ifdef HAVE_LCD_BITMAP
733 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
734 #else
735 left_col = 0;
736 #endif
737 rb->lcd_clear_display();
739 max_line_len = 0;
740 line_begin = line_end = screen_top_ptr;
742 for (i = 0; i < display_lines; i++) {
743 if (BUFFER_OOB(line_end))
744 break; /* Happens after display last line at BUFFER_EOF() */
746 line_begin = line_end;
747 line_end = find_next_line(line_begin, &line_is_short);
749 if (line_end == NULL) {
750 if (BUFFER_EOF()) {
751 if (i < display_lines - 1 && !BUFFER_BOF()) {
752 if (col != -1)
753 rb->lcd_clear_display();
755 for (; i < display_lines - 1; i++)
756 viewer_scroll_up();
758 line_begin = line_end = screen_top_ptr;
759 i = -1;
760 continue;
762 else {
763 line_end = buffer_end;
766 else {
767 resynch_move = read_and_synch(1); /* Read block & move ptrs */
768 line_begin -= resynch_move;
769 if (i > 0)
770 next_line_ptr -= resynch_move;
772 line_end = find_next_line(line_begin, NULL);
773 if (line_end == NULL) /* Should not really happen */
774 break;
777 line_len = line_end - line_begin;
779 /* calculate line_len */
780 str = oldstr = line_begin;
781 j = -1;
782 while (str < line_end) {
783 oldstr = str;
784 str = crop_at_width(str);
785 j++;
787 line_width = j*draw_columns;
788 while (oldstr < line_end) {
789 oldstr = get_ucs(oldstr, &ch);
790 line_width += glyph_width(ch);
793 if (prefs.line_mode == JOIN) {
794 if (line_begin[0] == 0) {
795 line_begin++;
796 if (prefs.word_mode == CHOP)
797 line_end++;
798 else
799 line_len--;
801 for (j=k=spaces=0; j < line_len; j++) {
802 if (k == MAX_COLUMNS)
803 break;
805 c = line_begin[j];
806 switch (c) {
807 case ' ':
808 spaces++;
809 break;
810 case 0:
811 spaces = 0;
812 scratch_buffer[k++] = ' ';
813 break;
814 default:
815 while (spaces) {
816 spaces--;
817 scratch_buffer[k++] = ' ';
818 if (k == MAX_COLUMNS - 1)
819 break;
821 scratch_buffer[k++] = c;
822 break;
825 if (col != -1) {
826 scratch_buffer[k] = 0;
827 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
828 prefs.encoding, draw_columns/glyph_width('i'));
829 *endptr = 0;
832 else if (prefs.line_mode == REFLOW) {
833 if (line_begin[0] == 0) {
834 line_begin++;
835 if (prefs.word_mode == CHOP)
836 line_end++;
837 else
838 line_len--;
841 indent_spaces = 0;
842 if (!line_is_short) {
843 multiple_spacing = false;
844 width=spaces=0;
845 for (str = line_begin; str < line_end; ) {
846 str = get_ucs(str, &ch);
847 switch (ch) {
848 case ' ':
849 case 0:
850 if ((str == line_begin) && (prefs.word_mode==WRAP))
851 /* special case: indent the paragraph,
852 * don't count spaces */
853 indent_spaces = par_indent_spaces;
854 else if (!multiple_spacing)
855 spaces++;
856 multiple_spacing = true;
857 break;
858 default:
859 multiple_spacing = false;
860 width += glyph_width(ch);
861 break;
864 if (multiple_spacing) spaces--;
866 if (spaces) {
867 /* total number of spaces to insert between words */
868 extra_spaces = (draw_columns-width)/glyph_width(' ')
869 - indent_spaces;
870 /* number of spaces between each word*/
871 spaces_per_word = extra_spaces / spaces;
872 /* number of words with n+1 spaces (to fill up) */
873 extra_spaces = extra_spaces % spaces;
874 if (spaces_per_word > 2) { /* too much spacing is awful */
875 spaces_per_word = 3;
876 extra_spaces = 0;
878 } else { /* this doesn't matter much... no spaces anyway */
879 spaces_per_word = extra_spaces = 0;
881 } else { /* end of a paragraph: don't fill line */
882 spaces_per_word = 1;
883 extra_spaces = 0;
886 multiple_spacing = false;
887 for (j=k=spaces=0; j < line_len; j++) {
888 if (k == MAX_COLUMNS)
889 break;
891 c = line_begin[j];
892 switch (c) {
893 case ' ':
894 case 0:
895 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
896 for (j=0; j<par_indent_spaces; j++)
897 scratch_buffer[k++] = ' ';
898 j=0;
900 else if (!multiple_spacing) {
901 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
902 scratch_buffer[k++] = ' ';
903 spaces++;
905 multiple_spacing = true;
906 break;
907 default:
908 scratch_buffer[k++] = c;
909 multiple_spacing = false;
910 break;
914 if (col != -1) {
915 scratch_buffer[k] = 0;
916 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
917 prefs.encoding, k-col);
918 *endptr = 0;
921 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
922 if (col != -1)
923 if (line_width > col) {
924 str = oldstr = line_begin;
925 k = col;
926 width = 0;
927 while( (width<draw_columns) && (oldstr<line_end) )
929 oldstr = get_ucs(oldstr, &ch);
930 if (k > 0) {
931 k -= glyph_width(ch);
932 line_begin = oldstr;
933 } else {
934 width += glyph_width(ch);
938 if(prefs.view_mode==WIDE)
939 endptr = rb->iso_decode(line_begin, utf8_buffer,
940 prefs.encoding, oldstr-line_begin);
941 else
942 endptr = rb->iso_decode(line_begin, utf8_buffer,
943 prefs.encoding, line_end-line_begin);
944 *endptr = 0;
947 if (col != -1 && line_width > col)
948 #ifdef HAVE_LCD_BITMAP
949 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
950 #else
951 rb->lcd_puts(left_col, i, utf8_buffer);
952 #endif
953 if (line_width > max_line_len)
954 max_line_len = line_width;
956 if (i == 0)
957 next_line_ptr = line_end;
959 next_screen_ptr = line_end;
960 if (BUFFER_OOB(next_screen_ptr))
961 next_screen_ptr = NULL;
963 #ifdef HAVE_LCD_BITMAP
964 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
966 if (prefs.need_scrollbar)
967 viewer_scrollbar();
968 #else
969 next_screen_to_draw_ptr = next_screen_ptr;
970 #endif
972 if (col != -1)
973 rb->lcd_update();
976 static void viewer_top(void)
978 /* Read top of file into buffer
979 and point screen pointer to top */
980 if (file_pos != 0)
982 file_pos = 0;
983 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
984 fill_buffer(0, buffer, buffer_size);
987 screen_top_ptr = buffer;
990 static void viewer_bottom(void)
992 /* Read bottom of file into buffer
993 and point screen pointer to bottom */
994 long last_sectors;
996 if (file_size > buffer_size) {
997 /* Find last buffer in file, round up to next sector boundary */
998 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
999 last_sectors /= SMALL_BLOCK_SIZE;
1000 last_sectors *= SMALL_BLOCK_SIZE;
1002 else {
1003 last_sectors = 0;
1006 if (file_pos != last_sectors)
1008 file_pos = last_sectors;
1009 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1010 fill_buffer(last_sectors, buffer, buffer_size);
1013 screen_top_ptr = buffer_end-1;
1016 #ifdef HAVE_LCD_BITMAP
1017 static void init_need_scrollbar(void) {
1018 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1019 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1020 viewer_draw(-1);
1021 prefs.need_scrollbar = NEED_SCROLLBAR();
1022 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1023 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1025 #else
1026 #define init_need_scrollbar()
1027 #endif
1029 static bool viewer_init(void)
1031 #ifdef HAVE_LCD_BITMAP
1033 pf = rb->font_get(FONT_UI);
1035 display_lines = LCD_HEIGHT / pf->height;
1036 draw_columns = display_columns = LCD_WIDTH;
1037 #else
1038 /* REAL fixed pitch :) all chars use up 1 cell */
1039 display_lines = 2;
1040 draw_columns = display_columns = 11;
1041 par_indent_spaces = 2;
1042 #endif
1044 fd = rb->open(file_name, O_RDONLY);
1045 if (fd==-1)
1046 return false;
1048 file_size = rb->filesize(fd);
1049 if (file_size==-1)
1050 return false;
1052 /* Init mac_text value used in processing buffer */
1053 mac_text = false;
1055 return true;
1058 static void viewer_default_settings(void)
1060 prefs.word_mode = WRAP;
1061 prefs.line_mode = NORMAL;
1062 prefs.view_mode = NARROW;
1063 prefs.scroll_mode = PAGE;
1064 #ifdef HAVE_LCD_BITMAP
1065 prefs.page_mode = NO_OVERLAP;
1066 prefs.scrollbar_mode = SB_OFF;
1067 #endif
1068 prefs.autoscroll_speed = 1;
1069 /* Set codepage to system default */
1070 prefs.encoding = rb->global_settings->default_codepage;
1073 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1075 int settings_fd, i;
1076 struct bookmark_file_data *data;
1077 struct bookmarked_file_info this_bookmark;
1079 /* read settings file */
1080 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1081 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1083 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1084 rb->close(settings_fd);
1086 else
1088 /* load default settings if there is no settings file */
1089 viewer_default_settings();
1092 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1094 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1095 data->bookmarked_files_count = 0;
1097 /* read bookmarks if file exists */
1098 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1099 if (settings_fd >= 0)
1101 /* figure out how many items to read */
1102 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1103 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1104 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1105 rb->read(settings_fd, data->bookmarks,
1106 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1107 rb->close(settings_fd);
1110 file_pos = 0;
1111 screen_top_ptr = buffer;
1113 /* check if current file is in list */
1114 for (i=0; i < data->bookmarked_files_count; i++)
1116 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1118 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1119 int screen_top = screen_pos % buffer_size;
1120 file_pos = screen_pos - screen_top;
1121 screen_top_ptr = buffer + screen_top;
1122 break;
1126 this_bookmark.file_position = file_pos;
1127 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1129 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1130 rb->strcpy(this_bookmark.filename,file_name);
1132 /* prevent potential slot overflow */
1133 if (i >= data->bookmarked_files_count)
1135 if (i < MAX_BOOKMARKED_FILES)
1136 data->bookmarked_files_count++;
1137 else
1138 i = MAX_BOOKMARKED_FILES-1;
1141 /* write bookmark file with spare slot in first position
1142 to be filled in by viewer_save_settings */
1143 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1144 if (settings_fd >=0 )
1146 /* write count */
1147 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1149 /* write the current bookmark */
1150 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1152 /* write everything that was before this bookmark */
1153 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1155 rb->close(settings_fd);
1158 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1160 if (BUFFER_OOB(screen_top_ptr))
1162 screen_top_ptr = buffer;
1165 fill_buffer(file_pos, buffer, buffer_size);
1167 /* remember the current position */
1168 start_position = file_pos + screen_top_ptr - buffer;
1170 init_need_scrollbar();
1173 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1175 int settings_fd;
1177 /* save the viewer settings if they have been changed */
1178 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1180 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1182 if (settings_fd >= 0 )
1184 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1185 rb->close(settings_fd);
1189 /* save the bookmark if the position has changed */
1190 if (file_pos + screen_top_ptr - buffer != start_position)
1192 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1194 if (settings_fd >= 0 )
1196 struct bookmarked_file_info b;
1197 b.file_position = file_pos + screen_top_ptr - buffer;
1198 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1199 rb->memset(&b.filename[0],0,MAX_PATH);
1200 rb->strcpy(b.filename,file_name);
1201 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1202 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1203 rb->close(settings_fd);
1208 static void viewer_exit(void *parameter)
1210 (void)parameter;
1212 viewer_save_settings();
1213 rb->close(fd);
1216 static int col_limit(int col)
1218 if (col < 0)
1219 col = 0;
1220 else
1221 if (col > max_line_len - 2*glyph_width('o'))
1222 col = max_line_len - 2*glyph_width('o');
1224 return col;
1227 /* settings helper functions */
1229 static bool encoding_setting(void)
1231 static const struct opt_items names[] = {
1232 {"ISO-8859-1", -1},
1233 {"ISO-8859-7", -1},
1234 {"ISO-8859-8", -1},
1235 {"CP1251", -1},
1236 {"ISO-8859-11", -1},
1237 {"ISO-8859-6", -1},
1238 {"ISO-8859-9", -1},
1239 {"ISO-8859-2", -1},
1240 {"CP1250", -1},
1241 {"SJIS", -1},
1242 {"GB-2312", -1},
1243 {"KSX-1001", -1},
1244 {"BIG5", -1},
1245 {"UTF-8", -1},
1248 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1249 sizeof(names) / sizeof(names[0]), NULL);
1252 static bool word_wrap_setting(void)
1254 static const struct opt_items names[] = {
1255 {"On", -1},
1256 {"Off (Chop Words)", -1},
1259 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1260 names, 2, NULL);
1263 static bool line_mode_setting(void)
1265 static const struct opt_items names[] = {
1266 {"Normal", -1},
1267 {"Join Lines", -1},
1268 {"Expand Lines", -1},
1269 #ifdef HAVE_LCD_BITMAP
1270 {"Reflow Lines", -1},
1271 #endif
1274 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1275 sizeof(names) / sizeof(names[0]), NULL);
1278 static bool view_mode_setting(void)
1280 static const struct opt_items names[] = {
1281 {"No (Narrow)", -1},
1282 {"Yes", -1},
1284 bool ret;
1285 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1286 names , 2, NULL);
1287 if (prefs.view_mode == NARROW)
1288 col = 0;
1289 return ret;
1292 static bool scroll_mode_setting(void)
1294 static const struct opt_items names[] = {
1295 {"Scroll by Page", -1},
1296 {"Scroll by Line", -1},
1299 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1300 names, 2, NULL);
1303 #ifdef HAVE_LCD_BITMAP
1304 static bool page_mode_setting(void)
1306 static const struct opt_items names[] = {
1307 {"No", -1},
1308 {"Yes", -1},
1311 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1312 names, 2, NULL);
1315 static bool scrollbar_setting(void)
1317 static const struct opt_items names[] = {
1318 {"Off", -1},
1319 {"On", -1}
1322 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1323 names, 2, NULL);
1325 #endif
1327 static bool autoscroll_speed_setting(void)
1329 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1330 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1333 static bool viewer_options_menu(void)
1335 int m;
1336 bool result;
1338 static const struct menu_item items[] = {
1339 {"Encoding", encoding_setting },
1340 {"Word Wrap", word_wrap_setting },
1341 {"Line Mode", line_mode_setting },
1342 {"Wide View", view_mode_setting },
1343 #ifdef HAVE_LCD_BITMAP
1344 {"Show Scrollbar", scrollbar_setting },
1345 {"Overlap Pages", page_mode_setting },
1346 #endif
1347 {"Scroll Mode", scroll_mode_setting},
1348 {"Auto-Scroll Speed", autoscroll_speed_setting },
1350 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1351 NULL, NULL, NULL, NULL);
1353 result = menu_run(m);
1354 menu_exit(m);
1355 #ifdef HAVE_LCD_BITMAP
1356 rb->lcd_setmargins(0,0);
1358 /* Show-scrollbar mode for current view-width mode */
1359 if (!ONE_SCREEN_FITS_ALL())
1360 if (prefs.scrollbar_mode == true)
1361 init_need_scrollbar();
1362 #endif
1363 return result;
1366 static void viewer_menu(void)
1368 int m;
1369 int result;
1370 static const struct menu_item items[] = {
1371 {"Quit", NULL },
1372 {"Viewer Options", NULL },
1373 {"Show Playback Menu", NULL },
1374 {"Return", NULL },
1377 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1378 result=menu_show(m);
1379 switch (result)
1381 case 0: /* quit */
1382 menu_exit(m);
1383 viewer_exit(NULL);
1384 done = true;
1385 break;
1386 case 1: /* change settings */
1387 done = viewer_options_menu();
1388 break;
1389 case 2: /* playback control */
1390 playback_control(rb);
1391 break;
1392 case 3: /* return */
1393 break;
1395 menu_exit(m);
1396 #ifdef HAVE_LCD_BITMAP
1397 rb->lcd_setmargins(0,0);
1398 #endif
1399 viewer_draw(col);
1402 enum plugin_status plugin_start(struct plugin_api* api, void* file)
1404 int button, i, ok;
1405 int lastbutton = BUTTON_NONE;
1406 bool autoscroll = false;
1407 long old_tick;
1409 rb = api;
1410 old_tick = *rb->current_tick;
1412 /* get the plugin buffer */
1413 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1415 if (!file)
1416 return PLUGIN_ERROR;
1418 file_name = file;
1419 ok = viewer_init();
1420 if (!ok) {
1421 rb->splash(HZ, "Error opening file.");
1422 return PLUGIN_ERROR;
1425 viewer_load_settings(); /* load the preferences and bookmark */
1427 #if LCD_DEPTH > 1
1428 rb->lcd_set_backdrop(NULL);
1429 #endif
1431 viewer_draw(col);
1433 while (!done) {
1435 if(autoscroll)
1437 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1439 viewer_scroll_down();
1440 viewer_draw(col);
1441 old_tick = *rb->current_tick;
1445 button = rb->button_get_w_tmo(HZ/10);
1446 switch (button) {
1447 case VIEWER_MENU:
1448 viewer_menu();
1449 break;
1451 case VIEWER_AUTOSCROLL:
1452 #ifdef VIEWER_AUTOSCROLL_PRE
1453 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1454 break;
1455 #endif
1456 autoscroll = !autoscroll;
1457 break;
1459 case VIEWER_PAGE_UP:
1460 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1461 if (prefs.scroll_mode == PAGE)
1463 /* Page up */
1464 #ifdef HAVE_LCD_BITMAP
1465 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1466 #else
1467 for (i = 0; i < display_lines; i++)
1468 #endif
1469 viewer_scroll_up();
1471 else
1472 viewer_scroll_up();
1473 old_tick = *rb->current_tick;
1474 viewer_draw(col);
1475 break;
1477 case VIEWER_PAGE_DOWN:
1478 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1479 if (prefs.scroll_mode == PAGE)
1481 /* Page down */
1482 if (next_screen_ptr != NULL)
1483 screen_top_ptr = next_screen_to_draw_ptr;
1485 else
1486 viewer_scroll_down();
1487 old_tick = *rb->current_tick;
1488 viewer_draw(col);
1489 break;
1491 case VIEWER_SCREEN_LEFT:
1492 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1493 if (prefs.view_mode == WIDE) {
1494 /* Screen left */
1495 col -= draw_columns;
1496 col = col_limit(col);
1498 else { /* prefs.view_mode == NARROW */
1499 /* Top of file */
1500 viewer_top();
1503 viewer_draw(col);
1504 break;
1506 case VIEWER_SCREEN_RIGHT:
1507 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1508 if (prefs.view_mode == WIDE) {
1509 /* Screen right */
1510 col += draw_columns;
1511 col = col_limit(col);
1513 else { /* prefs.view_mode == NARROW */
1514 /* Bottom of file */
1515 viewer_bottom();
1518 viewer_draw(col);
1519 break;
1521 #ifdef VIEWER_LINE_UP
1522 case VIEWER_LINE_UP:
1523 case VIEWER_LINE_UP | BUTTON_REPEAT:
1524 /* Scroll up one line */
1525 viewer_scroll_up();
1526 old_tick = *rb->current_tick;
1527 viewer_draw(col);
1528 break;
1530 case VIEWER_LINE_DOWN:
1531 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1532 /* Scroll down one line */
1533 if (next_screen_ptr != NULL)
1534 screen_top_ptr = next_line_ptr;
1535 old_tick = *rb->current_tick;
1536 viewer_draw(col);
1537 break;
1538 #endif
1539 #ifdef VIEWER_COLUMN_LEFT
1540 case VIEWER_COLUMN_LEFT:
1541 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1542 if (prefs.view_mode == WIDE) {
1543 /* Scroll left one column */
1544 col -= glyph_width('o');
1545 col = col_limit(col);
1546 viewer_draw(col);
1548 break;
1550 case VIEWER_COLUMN_RIGHT:
1551 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1552 if (prefs.view_mode == WIDE) {
1553 /* Scroll right one column */
1554 col += glyph_width('o');
1555 col = col_limit(col);
1556 viewer_draw(col);
1558 break;
1559 #endif
1561 #ifdef VIEWER_RC_QUIT
1562 case VIEWER_RC_QUIT:
1563 #endif
1564 case VIEWER_QUIT:
1565 viewer_exit(NULL);
1566 done = true;
1567 break;
1569 default:
1570 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1571 == SYS_USB_CONNECTED)
1572 return PLUGIN_USB_CONNECTED;
1573 break;
1575 if (button != BUTTON_NONE)
1577 lastbutton = button;
1578 rb->yield();
1581 return PLUGIN_OK;