when changing settings from the Talk and Voice window also update the main widgets...
[Rockbox.git] / apps / plugins / viewer.c
blobea33314c99f6b182ef19d4c9bec49435ea0676ca
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
20 #include "plugin.h"
21 #include <ctype.h>
22 #include "playback_control.h"
23 #include "oldmenuapi.h"
25 PLUGIN_HEADER
27 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
28 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
30 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
31 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
32 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
33 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
34 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
35 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
36 #define TOP_SECTOR buffer
37 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
38 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
39 #define SCROLLBAR_WIDTH 6
41 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
43 /* Out-Of-Bounds test for any pointer to data in the buffer */
44 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
46 /* Does the buffer contain the beginning of the file? */
47 #define BUFFER_BOF() (file_pos==0)
49 /* Does the buffer contain the end of the file? */
50 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
52 /* Formula for the endpoint address outside of buffer data */
53 #define BUFFER_END() \
54 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
56 /* Is the entire file being shown in one screen? */
57 #define ONE_SCREEN_FITS_ALL() \
58 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
60 /* Is a scrollbar called for on the current screen? */
61 #define NEED_SCROLLBAR() \
62 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
64 /* variable button definitions */
66 /* Recorder keys */
67 #if CONFIG_KEYPAD == RECORDER_PAD
68 #define VIEWER_QUIT BUTTON_OFF
69 #define VIEWER_PAGE_UP BUTTON_UP
70 #define VIEWER_PAGE_DOWN BUTTON_DOWN
71 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
72 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
73 #define VIEWER_MENU BUTTON_F1
74 #define VIEWER_AUTOSCROLL BUTTON_PLAY
75 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
76 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
77 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
78 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
80 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
81 #define VIEWER_QUIT BUTTON_OFF
82 #define VIEWER_PAGE_UP BUTTON_UP
83 #define VIEWER_PAGE_DOWN BUTTON_DOWN
84 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
85 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
86 #define VIEWER_MENU BUTTON_F1
87 #define VIEWER_AUTOSCROLL BUTTON_SELECT
88 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
89 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
90 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
91 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
93 /* Ondio keys */
94 #elif CONFIG_KEYPAD == ONDIO_PAD
95 #define VIEWER_QUIT BUTTON_OFF
96 #define VIEWER_PAGE_UP BUTTON_UP
97 #define VIEWER_PAGE_DOWN BUTTON_DOWN
98 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
99 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
100 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
101 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
102 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
104 /* Player keys */
105 #elif CONFIG_KEYPAD == PLAYER_PAD
106 #define VIEWER_QUIT BUTTON_STOP
107 #define VIEWER_PAGE_UP BUTTON_LEFT
108 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
109 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
110 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
111 #define VIEWER_MENU BUTTON_MENU
112 #define VIEWER_AUTOSCROLL BUTTON_PLAY
114 /* iRiver H1x0 && H3x0 keys */
115 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
116 (CONFIG_KEYPAD == IRIVER_H300_PAD)
117 #define VIEWER_QUIT BUTTON_OFF
118 #define VIEWER_PAGE_UP BUTTON_UP
119 #define VIEWER_PAGE_DOWN BUTTON_DOWN
120 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
121 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
122 #define VIEWER_MENU BUTTON_MODE
123 #define VIEWER_AUTOSCROLL BUTTON_SELECT
124 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
125 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
126 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
127 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
129 #define VIEWER_RC_QUIT BUTTON_RC_STOP
131 /* iPods */
132 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
133 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
135 #define VIEWER_QUIT_PRE BUTTON_SELECT
136 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
137 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
138 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
139 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
140 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
141 #define VIEWER_MENU BUTTON_MENU
142 #define VIEWER_AUTOSCROLL BUTTON_PLAY
144 /* iFP7xx keys */
145 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
146 #define VIEWER_QUIT BUTTON_PLAY
147 #define VIEWER_PAGE_UP BUTTON_UP
148 #define VIEWER_PAGE_DOWN BUTTON_DOWN
149 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
150 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
151 #define VIEWER_MENU BUTTON_MODE
152 #define VIEWER_AUTOSCROLL BUTTON_SELECT
154 /* iAudio X5 keys */
155 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
156 #define VIEWER_QUIT BUTTON_POWER
157 #define VIEWER_PAGE_UP BUTTON_UP
158 #define VIEWER_PAGE_DOWN BUTTON_DOWN
159 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
160 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
161 #define VIEWER_MENU BUTTON_SELECT
162 #define VIEWER_AUTOSCROLL BUTTON_PLAY
164 /* GIGABEAT keys */
165 #elif CONFIG_KEYPAD == GIGABEAT_PAD
166 #define VIEWER_QUIT BUTTON_POWER
167 #define VIEWER_PAGE_UP BUTTON_UP
168 #define VIEWER_PAGE_DOWN BUTTON_DOWN
169 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
170 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
171 #define VIEWER_MENU BUTTON_MENU
172 #define VIEWER_AUTOSCROLL BUTTON_A
174 /* Sansa E200 keys */
175 #elif CONFIG_KEYPAD == SANSA_E200_PAD
176 #define VIEWER_QUIT BUTTON_POWER
177 #define VIEWER_PAGE_UP BUTTON_UP
178 #define VIEWER_PAGE_DOWN BUTTON_DOWN
179 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
180 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
181 #define VIEWER_MENU BUTTON_SELECT
182 #define VIEWER_AUTOSCROLL BUTTON_REC
183 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
184 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
186 /* Sansa C200 keys */
187 #elif CONFIG_KEYPAD == SANSA_C200_PAD
188 #define VIEWER_QUIT BUTTON_POWER
189 #define VIEWER_PAGE_UP BUTTON_VOL_UP
190 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
191 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
192 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
193 #define VIEWER_MENU BUTTON_SELECT
194 #define VIEWER_AUTOSCROLL BUTTON_REC
195 #define VIEWER_LINE_UP BUTTON_UP
196 #define VIEWER_LINE_DOWN BUTTON_DOWN
198 /* iriver H10 keys */
199 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
200 #define VIEWER_QUIT BUTTON_POWER
201 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
202 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
203 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
204 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
205 #define VIEWER_MENU BUTTON_REW
206 #define VIEWER_AUTOSCROLL BUTTON_PLAY
208 #elif CONFIG_KEYPAD == MROBE500_PAD
209 #define VIEWER_QUIT BUTTON_POWER
210 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
211 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
212 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
213 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
214 #define VIEWER_MENU BUTTON_RC_HEART
215 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
217 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
218 #define VIEWER_QUIT BUTTON_BACK
219 #define VIEWER_PAGE_UP BUTTON_VOL_UP
220 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
221 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
222 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
223 #define VIEWER_MENU BUTTON_MENU
224 #define VIEWER_AUTOSCROLL BUTTON_PLAY
226 #elif CONFIG_KEYPAD == MROBE100_PAD
227 #define VIEWER_QUIT BUTTON_POWER
228 #define VIEWER_PAGE_UP BUTTON_UP
229 #define VIEWER_PAGE_DOWN BUTTON_DOWN
230 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
231 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
232 #define VIEWER_MENU BUTTON_MENU
233 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
235 #else
236 #error No keymap defined!
237 #endif
239 /* stuff for the bookmarking */
240 struct bookmarked_file_info {
241 long file_position;
242 int top_ptr_pos;
243 char filename[MAX_PATH];
246 struct bookmark_file_data {
247 signed int bookmarked_files_count;
248 struct bookmarked_file_info bookmarks[];
251 struct preferences {
252 enum {
253 WRAP=0,
254 CHOP,
255 } word_mode;
257 enum {
258 NORMAL=0,
259 JOIN,
260 EXPAND,
261 REFLOW, /* won't be set on charcell LCD, must be last */
262 } line_mode;
264 enum {
265 NARROW=0,
266 WIDE,
267 } view_mode;
269 enum {
270 ISO_8859_1=0,
271 ISO_8859_7,
272 ISO_8859_8,
273 CP1251,
274 ISO_8859_11,
275 ISO_8859_6,
276 ISO_8859_9,
277 ISO_8859_2,
278 CP1250,
279 SJIS,
280 GB2312,
281 KSX1001,
282 BIG5,
283 UTF8,
284 ENCODINGS
285 } encoding; /* FIXME: What should default encoding be? */
287 #ifdef HAVE_LCD_BITMAP
288 enum {
289 SB_OFF=0,
290 SB_ON,
291 } scrollbar_mode;
292 bool need_scrollbar;
294 enum {
295 NO_OVERLAP=0,
296 OVERLAP,
297 } page_mode;
298 #endif /* HAVE_LCD_BITMAP */
300 enum {
301 PAGE=0,
302 LINE,
303 } scroll_mode;
305 int autoscroll_speed;
309 struct preferences prefs;
310 struct preferences old_prefs;
312 static unsigned char *buffer;
313 static long buffer_size;
314 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
315 static int display_columns; /* number of (pixel) columns on the display */
316 static int display_lines; /* number of lines on the display */
317 static int draw_columns; /* number of (pixel) columns available for text */
318 static int par_indent_spaces; /* number of spaces to indent first paragraph */
319 static int fd;
320 static char *file_name;
321 static long file_size;
322 static long start_position; /* position in the file after the viewer is started */
323 static bool mac_text;
324 static long file_pos; /* Position of the top of the buffer in the file */
325 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
326 static int max_line_len;
327 static unsigned char *screen_top_ptr;
328 static unsigned char *next_screen_ptr;
329 static unsigned char *next_screen_to_draw_ptr;
330 static unsigned char *next_line_ptr;
331 static struct plugin_api* rb;
332 #ifdef HAVE_LCD_BITMAP
333 static struct font *pf;
334 #endif
337 int glyph_width(int ch)
339 if (ch == 0)
340 ch = ' ';
342 #ifdef HAVE_LCD_BITMAP
343 return rb->font_get_width(pf, ch);
344 #else
345 return 1;
346 #endif
349 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
351 unsigned char utf8_tmp[6];
352 int count;
354 if (prefs.encoding == UTF8)
355 return (unsigned char*)rb->utf8decode(str, ch);
357 count = BUFFER_OOB(str+2)? 1:2;
358 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
359 rb->utf8decode(utf8_tmp, ch);
361 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
362 return (unsigned char*)str+1;
363 else
364 return (unsigned char*)str+2;
367 bool done = false;
368 int col = 0;
370 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
371 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
372 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
373 static unsigned char* crop_at_width(const unsigned char* p)
375 int k,width;
376 unsigned short ch;
377 const unsigned char *oldp = p;
379 k=width=0;
381 while (LINE_IS_NOT_FULL) {
382 oldp = p;
383 p = get_ucs(p, &ch);
384 ADVANCE_COUNTERS(ch);
387 return (unsigned char*)oldp;
390 static unsigned char* find_first_feed(const unsigned char* p, int size)
392 int i;
394 for (i=0; i < size; i++)
395 if (p[i] == 0)
396 return (unsigned char*) p+i;
398 return NULL;
401 static unsigned char* find_last_feed(const unsigned char* p, int size)
403 int i;
405 for (i=size-1; i>=0; i--)
406 if (p[i] == 0)
407 return (unsigned char*) p+i;
409 return NULL;
412 static unsigned char* find_last_space(const unsigned char* p, int size)
414 int i, j, k;
416 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
418 if (!BUFFER_OOB(&p[size]))
419 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
420 if (p[size] == line_break[j])
421 return (unsigned char*) p+size;
423 for (i=size-1; i>=0; i--)
424 for (j=k; j < (int) sizeof(line_break); j++)
426 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
427 if (p[i] == line_break[j])
428 return (unsigned char*) p+i;
431 return NULL;
434 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
436 const unsigned char *next_line = NULL;
437 int size, i, j, k, width, search_len, spaces, newlines;
438 bool first_chars;
439 unsigned char c;
441 if (is_short != NULL)
442 *is_short = true;
444 if BUFFER_OOB(cur_line)
445 return NULL;
447 if (prefs.view_mode == WIDE) {
448 search_len = MAX_WIDTH;
450 else { /* prefs.view_mode == NARROW */
451 search_len = crop_at_width(cur_line) - cur_line;
454 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
456 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
457 /* Need to scan ahead and possibly increase search_len and size,
458 or possibly set next_line at second hard return in a row. */
459 next_line = NULL;
460 first_chars=true;
461 for (j=k=width=spaces=newlines=0; ; j++) {
462 if (BUFFER_OOB(cur_line+j))
463 return NULL;
464 if (LINE_IS_FULL) {
465 size = search_len = j;
466 break;
469 c = cur_line[j];
470 switch (c) {
471 case ' ':
472 if (prefs.line_mode == REFLOW) {
473 if (newlines > 0) {
474 size = j;
475 next_line = cur_line + size;
476 return (unsigned char*) next_line;
478 if (j==0) /* i=1 is intentional */
479 for (i=0; i<par_indent_spaces; i++)
480 ADVANCE_COUNTERS(' ');
482 if (!first_chars) spaces++;
483 break;
485 case 0:
486 if (newlines > 0) {
487 size = j;
488 next_line = cur_line + size - spaces;
489 if (next_line != cur_line)
490 return (unsigned char*) next_line;
491 break;
494 newlines++;
495 size += spaces -1;
496 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
497 return NULL;
498 search_len = size;
499 spaces = first_chars? 0:1;
500 break;
502 default:
503 if (prefs.line_mode==JOIN || newlines>0) {
504 while (spaces) {
505 spaces--;
506 ADVANCE_COUNTERS(' ');
507 if (LINE_IS_FULL) {
508 size = search_len = j;
509 break;
512 newlines=0;
513 } else if (spaces) {
514 /* REFLOW, multiple spaces between words: count only
515 * one. If more are needed, they will be added
516 * while drawing. */
517 search_len = size;
518 spaces=0;
519 ADVANCE_COUNTERS(' ');
520 if (LINE_IS_FULL) {
521 size = search_len = j;
522 break;
525 first_chars = false;
526 ADVANCE_COUNTERS(c);
527 break;
531 else {
532 /* find first hard return */
533 next_line = find_first_feed(cur_line, size);
536 if (next_line == NULL)
537 if (size == search_len) {
538 if (prefs.word_mode == WRAP) /* Find last space */
539 next_line = find_last_space(cur_line, size);
541 if (next_line == NULL)
542 next_line = crop_at_width(cur_line);
543 else
544 if (prefs.word_mode == WRAP)
545 for (i=0;
546 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
547 i++)
548 next_line++;
551 if (prefs.line_mode == EXPAND)
552 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
553 if (next_line[0] == 0)
554 if (next_line != cur_line)
555 return (unsigned char*) next_line;
557 /* If next_line is pointing to a zero, increment it; i.e.,
558 leave the terminator at the end of cur_line. If pointing
559 to a hyphen, increment only if there is room to display
560 the hyphen on current line (won't apply in WIDE mode,
561 since it's guarenteed there won't be room). */
562 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
563 if (next_line[0] == 0)/* ||
564 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
565 next_line++;
567 if (BUFFER_OOB(next_line))
568 return NULL;
570 if (is_short)
571 *is_short = false;
573 return (unsigned char*) next_line;
576 static unsigned char* find_prev_line(const unsigned char* cur_line)
578 const unsigned char *prev_line = NULL;
579 const unsigned char *p;
581 if BUFFER_OOB(cur_line)
582 return NULL;
584 /* To wrap consistently at the same places, we must
585 start with a known hard return, then work downwards.
586 We can either search backwards for a hard return,
587 or simply start wrapping downwards from top of buffer.
588 If current line is not near top of buffer, this is
589 a file with long lines (paragraphs). We would need to
590 read earlier sectors before we could decide how to
591 properly wrap the lines above the current line, but
592 it probably is not worth the disk access. Instead,
593 start with top of buffer and wrap down from there.
594 This may result in some lines wrapping at different
595 points from where they wrap when scrolling down.
596 If buffer is at top of file, start at top of buffer. */
598 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
599 prev_line = p = NULL;
600 else
601 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
602 /* Null means no line feeds in buffer above current line. */
604 if (prev_line == NULL)
605 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
606 prev_line = p = buffer;
607 /* (else return NULL and read previous block) */
609 /* Wrap downwards until too far, then use the one before. */
610 while (p < cur_line && p != NULL) {
611 prev_line = p;
612 p = find_next_line(prev_line, NULL);
615 if (BUFFER_OOB(prev_line))
616 return NULL;
618 return (unsigned char*) prev_line;
621 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
623 /* Read from file and preprocess the data */
624 /* To minimize disk access, always read on sector boundaries */
625 unsigned numread, i;
626 bool found_CR = false;
628 rb->lseek(fd, pos, SEEK_SET);
629 numread = rb->read(fd, buf, size);
630 rb->button_clear_queue(); /* clear button queue */
632 for(i = 0; i < numread; i++) {
633 switch(buf[i]) {
634 case '\r':
635 if (mac_text) {
636 buf[i] = 0;
638 else {
639 buf[i] = ' ';
640 found_CR = true;
642 break;
644 case '\n':
645 buf[i] = 0;
646 found_CR = false;
647 break;
649 case 0: /* No break between case 0 and default, intentionally */
650 buf[i] = ' ';
651 default:
652 if (found_CR) {
653 buf[i - 1] = 0;
654 found_CR = false;
655 mac_text = true;
657 break;
662 static int read_and_synch(int direction)
664 /* Read next (or prev) block, and reposition global pointers. */
665 /* direction: 1 for down (i.e., further into file), -1 for up */
666 int move_size, move_vector, offset;
667 unsigned char *fill_buf;
669 if (direction == -1) /* up */ {
670 move_size = SMALL_BLOCK_SIZE;
671 offset = 0;
672 fill_buf = TOP_SECTOR;
673 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
674 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
676 else /* down */ {
677 if (prefs.view_mode == WIDE) {
678 /* WIDE mode needs more buffer so we have to read smaller blocks */
679 move_size = SMALL_BLOCK_SIZE;
680 offset = LARGE_BLOCK_SIZE;
681 fill_buf = BOTTOM_SECTOR;
682 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
683 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
685 else {
686 move_size = LARGE_BLOCK_SIZE;
687 offset = SMALL_BLOCK_SIZE;
688 fill_buf = MID_SECTOR;
689 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
692 move_vector = direction * move_size;
693 screen_top_ptr -= move_vector;
694 file_pos += move_vector;
695 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
696 fill_buffer(file_pos + offset, fill_buf, move_size);
697 return move_vector;
700 static void viewer_scroll_up(void)
702 unsigned char *p;
704 p = find_prev_line(screen_top_ptr);
705 if (p == NULL && !BUFFER_BOF()) {
706 read_and_synch(-1);
707 p = find_prev_line(screen_top_ptr);
709 if (p != NULL)
710 screen_top_ptr = p;
713 static void viewer_scroll_down(void)
715 if (next_screen_ptr != NULL)
716 screen_top_ptr = next_line_ptr;
719 #ifdef HAVE_LCD_BITMAP
720 static void viewer_scrollbar(void) {
721 int items, min_shown, max_shown;
723 items = (int) file_size; /* (SH1 int is same as long) */
724 min_shown = (int) file_pos + (screen_top_ptr - buffer);
726 if (next_screen_ptr == NULL)
727 max_shown = items;
728 else
729 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
731 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
732 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
734 #endif
736 static void viewer_draw(int col)
738 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
739 int width, extra_spaces, indent_spaces, spaces_per_word;
740 bool multiple_spacing, line_is_short;
741 unsigned short ch;
742 unsigned char *str, *oldstr;
743 unsigned char *line_begin;
744 unsigned char *line_end;
745 unsigned char c;
746 unsigned char scratch_buffer[MAX_COLUMNS + 1];
747 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
748 unsigned char *endptr;
750 /* If col==-1 do all calculations but don't display */
751 if (col != -1) {
752 #ifdef HAVE_LCD_BITMAP
753 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
754 #else
755 left_col = 0;
756 #endif
757 rb->lcd_clear_display();
759 max_line_len = 0;
760 line_begin = line_end = screen_top_ptr;
762 for (i = 0; i < display_lines; i++) {
763 if (BUFFER_OOB(line_end))
764 break; /* Happens after display last line at BUFFER_EOF() */
766 line_begin = line_end;
767 line_end = find_next_line(line_begin, &line_is_short);
769 if (line_end == NULL) {
770 if (BUFFER_EOF()) {
771 if (i < display_lines - 1 && !BUFFER_BOF()) {
772 if (col != -1)
773 rb->lcd_clear_display();
775 for (; i < display_lines - 1; i++)
776 viewer_scroll_up();
778 line_begin = line_end = screen_top_ptr;
779 i = -1;
780 continue;
782 else {
783 line_end = buffer_end;
786 else {
787 resynch_move = read_and_synch(1); /* Read block & move ptrs */
788 line_begin -= resynch_move;
789 if (i > 0)
790 next_line_ptr -= resynch_move;
792 line_end = find_next_line(line_begin, NULL);
793 if (line_end == NULL) /* Should not really happen */
794 break;
797 line_len = line_end - line_begin;
799 /* calculate line_len */
800 str = oldstr = line_begin;
801 j = -1;
802 while (str < line_end) {
803 oldstr = str;
804 str = crop_at_width(str);
805 j++;
807 line_width = j*draw_columns;
808 while (oldstr < line_end) {
809 oldstr = get_ucs(oldstr, &ch);
810 line_width += glyph_width(ch);
813 if (prefs.line_mode == JOIN) {
814 if (line_begin[0] == 0) {
815 line_begin++;
816 if (prefs.word_mode == CHOP)
817 line_end++;
818 else
819 line_len--;
821 for (j=k=spaces=0; j < line_len; j++) {
822 if (k == MAX_COLUMNS)
823 break;
825 c = line_begin[j];
826 switch (c) {
827 case ' ':
828 spaces++;
829 break;
830 case 0:
831 spaces = 0;
832 scratch_buffer[k++] = ' ';
833 break;
834 default:
835 while (spaces) {
836 spaces--;
837 scratch_buffer[k++] = ' ';
838 if (k == MAX_COLUMNS - 1)
839 break;
841 scratch_buffer[k++] = c;
842 break;
845 if (col != -1) {
846 scratch_buffer[k] = 0;
847 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
848 prefs.encoding, draw_columns/glyph_width('i'));
849 *endptr = 0;
852 else if (prefs.line_mode == REFLOW) {
853 if (line_begin[0] == 0) {
854 line_begin++;
855 if (prefs.word_mode == CHOP)
856 line_end++;
857 else
858 line_len--;
861 indent_spaces = 0;
862 if (!line_is_short) {
863 multiple_spacing = false;
864 width=spaces=0;
865 for (str = line_begin; str < line_end; ) {
866 str = get_ucs(str, &ch);
867 switch (ch) {
868 case ' ':
869 case 0:
870 if ((str == line_begin) && (prefs.word_mode==WRAP))
871 /* special case: indent the paragraph,
872 * don't count spaces */
873 indent_spaces = par_indent_spaces;
874 else if (!multiple_spacing)
875 spaces++;
876 multiple_spacing = true;
877 break;
878 default:
879 multiple_spacing = false;
880 width += glyph_width(ch);
881 break;
884 if (multiple_spacing) spaces--;
886 if (spaces) {
887 /* total number of spaces to insert between words */
888 extra_spaces = (draw_columns-width)/glyph_width(' ')
889 - indent_spaces;
890 /* number of spaces between each word*/
891 spaces_per_word = extra_spaces / spaces;
892 /* number of words with n+1 spaces (to fill up) */
893 extra_spaces = extra_spaces % spaces;
894 if (spaces_per_word > 2) { /* too much spacing is awful */
895 spaces_per_word = 3;
896 extra_spaces = 0;
898 } else { /* this doesn't matter much... no spaces anyway */
899 spaces_per_word = extra_spaces = 0;
901 } else { /* end of a paragraph: don't fill line */
902 spaces_per_word = 1;
903 extra_spaces = 0;
906 multiple_spacing = false;
907 for (j=k=spaces=0; j < line_len; j++) {
908 if (k == MAX_COLUMNS)
909 break;
911 c = line_begin[j];
912 switch (c) {
913 case ' ':
914 case 0:
915 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
916 for (j=0; j<par_indent_spaces; j++)
917 scratch_buffer[k++] = ' ';
918 j=0;
920 else if (!multiple_spacing) {
921 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
922 scratch_buffer[k++] = ' ';
923 spaces++;
925 multiple_spacing = true;
926 break;
927 default:
928 scratch_buffer[k++] = c;
929 multiple_spacing = false;
930 break;
934 if (col != -1) {
935 scratch_buffer[k] = 0;
936 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
937 prefs.encoding, k-col);
938 *endptr = 0;
941 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
942 if (col != -1)
943 if (line_width > col) {
944 str = oldstr = line_begin;
945 k = col;
946 width = 0;
947 while( (width<draw_columns) && (oldstr<line_end) )
949 oldstr = get_ucs(oldstr, &ch);
950 if (k > 0) {
951 k -= glyph_width(ch);
952 line_begin = oldstr;
953 } else {
954 width += glyph_width(ch);
958 if(prefs.view_mode==WIDE)
959 endptr = rb->iso_decode(line_begin, utf8_buffer,
960 prefs.encoding, oldstr-line_begin);
961 else
962 endptr = rb->iso_decode(line_begin, utf8_buffer,
963 prefs.encoding, line_end-line_begin);
964 *endptr = 0;
967 if (col != -1 && line_width > col)
968 #ifdef HAVE_LCD_BITMAP
969 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
970 #else
971 rb->lcd_puts(left_col, i, utf8_buffer);
972 #endif
973 if (line_width > max_line_len)
974 max_line_len = line_width;
976 if (i == 0)
977 next_line_ptr = line_end;
979 next_screen_ptr = line_end;
980 if (BUFFER_OOB(next_screen_ptr))
981 next_screen_ptr = NULL;
983 #ifdef HAVE_LCD_BITMAP
984 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
986 if (prefs.need_scrollbar)
987 viewer_scrollbar();
988 #else
989 next_screen_to_draw_ptr = next_screen_ptr;
990 #endif
992 if (col != -1)
993 rb->lcd_update();
996 static void viewer_top(void)
998 /* Read top of file into buffer
999 and point screen pointer to top */
1000 if (file_pos != 0)
1002 file_pos = 0;
1003 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1004 fill_buffer(0, buffer, buffer_size);
1007 screen_top_ptr = buffer;
1010 static void viewer_bottom(void)
1012 /* Read bottom of file into buffer
1013 and point screen pointer to bottom */
1014 long last_sectors;
1016 if (file_size > buffer_size) {
1017 /* Find last buffer in file, round up to next sector boundary */
1018 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1019 last_sectors /= SMALL_BLOCK_SIZE;
1020 last_sectors *= SMALL_BLOCK_SIZE;
1022 else {
1023 last_sectors = 0;
1026 if (file_pos != last_sectors)
1028 file_pos = last_sectors;
1029 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1030 fill_buffer(last_sectors, buffer, buffer_size);
1033 screen_top_ptr = buffer_end-1;
1036 #ifdef HAVE_LCD_BITMAP
1037 static void init_need_scrollbar(void) {
1038 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1039 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1040 viewer_draw(-1);
1041 prefs.need_scrollbar = NEED_SCROLLBAR();
1042 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1043 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1045 #else
1046 #define init_need_scrollbar()
1047 #endif
1049 static bool viewer_init(void)
1051 #ifdef HAVE_LCD_BITMAP
1053 pf = rb->font_get(FONT_UI);
1055 display_lines = LCD_HEIGHT / pf->height;
1056 draw_columns = display_columns = LCD_WIDTH;
1057 #else
1058 /* REAL fixed pitch :) all chars use up 1 cell */
1059 display_lines = 2;
1060 draw_columns = display_columns = 11;
1061 par_indent_spaces = 2;
1062 #endif
1064 fd = rb->open(file_name, O_RDONLY);
1065 if (fd==-1)
1066 return false;
1068 file_size = rb->filesize(fd);
1069 if (file_size==-1)
1070 return false;
1072 /* Init mac_text value used in processing buffer */
1073 mac_text = false;
1075 return true;
1078 static void viewer_default_settings(void)
1080 prefs.word_mode = WRAP;
1081 prefs.line_mode = NORMAL;
1082 prefs.view_mode = NARROW;
1083 prefs.scroll_mode = PAGE;
1084 #ifdef HAVE_LCD_BITMAP
1085 prefs.page_mode = NO_OVERLAP;
1086 prefs.scrollbar_mode = SB_OFF;
1087 #endif
1088 prefs.autoscroll_speed = 1;
1089 /* Set codepage to system default */
1090 prefs.encoding = rb->global_settings->default_codepage;
1093 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1095 int settings_fd, i;
1096 struct bookmark_file_data *data;
1097 struct bookmarked_file_info this_bookmark;
1099 /* read settings file */
1100 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1101 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1103 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1104 rb->close(settings_fd);
1106 else
1108 /* load default settings if there is no settings file */
1109 viewer_default_settings();
1112 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1114 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1115 data->bookmarked_files_count = 0;
1117 /* read bookmarks if file exists */
1118 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1119 if (settings_fd >= 0)
1121 /* figure out how many items to read */
1122 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1123 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1124 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1125 rb->read(settings_fd, data->bookmarks,
1126 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1127 rb->close(settings_fd);
1130 file_pos = 0;
1131 screen_top_ptr = buffer;
1133 /* check if current file is in list */
1134 for (i=0; i < data->bookmarked_files_count; i++)
1136 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1138 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1139 int screen_top = screen_pos % buffer_size;
1140 file_pos = screen_pos - screen_top;
1141 screen_top_ptr = buffer + screen_top;
1142 break;
1146 this_bookmark.file_position = file_pos;
1147 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1149 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1150 rb->strcpy(this_bookmark.filename,file_name);
1152 /* prevent potential slot overflow */
1153 if (i >= data->bookmarked_files_count)
1155 if (i < MAX_BOOKMARKED_FILES)
1156 data->bookmarked_files_count++;
1157 else
1158 i = MAX_BOOKMARKED_FILES-1;
1161 /* write bookmark file with spare slot in first position
1162 to be filled in by viewer_save_settings */
1163 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1164 if (settings_fd >=0 )
1166 /* write count */
1167 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1169 /* write the current bookmark */
1170 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1172 /* write everything that was before this bookmark */
1173 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1175 rb->close(settings_fd);
1178 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1180 if (BUFFER_OOB(screen_top_ptr))
1182 screen_top_ptr = buffer;
1185 fill_buffer(file_pos, buffer, buffer_size);
1187 /* remember the current position */
1188 start_position = file_pos + screen_top_ptr - buffer;
1190 init_need_scrollbar();
1193 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1195 int settings_fd;
1197 /* save the viewer settings if they have been changed */
1198 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1200 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1202 if (settings_fd >= 0 )
1204 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1205 rb->close(settings_fd);
1209 /* save the bookmark if the position has changed */
1210 if (file_pos + screen_top_ptr - buffer != start_position)
1212 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1214 if (settings_fd >= 0 )
1216 struct bookmarked_file_info b;
1217 b.file_position = file_pos + screen_top_ptr - buffer;
1218 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1219 rb->memset(&b.filename[0],0,MAX_PATH);
1220 rb->strcpy(b.filename,file_name);
1221 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1222 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1223 rb->close(settings_fd);
1228 static void viewer_exit(void *parameter)
1230 (void)parameter;
1232 viewer_save_settings();
1233 rb->close(fd);
1236 static int col_limit(int col)
1238 if (col < 0)
1239 col = 0;
1240 else
1241 if (col > max_line_len - 2*glyph_width('o'))
1242 col = max_line_len - 2*glyph_width('o');
1244 return col;
1247 /* settings helper functions */
1249 static bool encoding_setting(void)
1251 static const struct opt_items names[] = {
1252 {"ISO-8859-1", -1},
1253 {"ISO-8859-7", -1},
1254 {"ISO-8859-8", -1},
1255 {"CP1251", -1},
1256 {"ISO-8859-11", -1},
1257 {"ISO-8859-6", -1},
1258 {"ISO-8859-9", -1},
1259 {"ISO-8859-2", -1},
1260 {"CP1250", -1},
1261 {"SJIS", -1},
1262 {"GB-2312", -1},
1263 {"KSX-1001", -1},
1264 {"BIG5", -1},
1265 {"UTF-8", -1},
1268 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1269 sizeof(names) / sizeof(names[0]), NULL);
1272 static bool word_wrap_setting(void)
1274 static const struct opt_items names[] = {
1275 {"On", -1},
1276 {"Off (Chop Words)", -1},
1279 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1280 names, 2, NULL);
1283 static bool line_mode_setting(void)
1285 static const struct opt_items names[] = {
1286 {"Normal", -1},
1287 {"Join Lines", -1},
1288 {"Expand Lines", -1},
1289 #ifdef HAVE_LCD_BITMAP
1290 {"Reflow Lines", -1},
1291 #endif
1294 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1295 sizeof(names) / sizeof(names[0]), NULL);
1298 static bool view_mode_setting(void)
1300 static const struct opt_items names[] = {
1301 {"No (Narrow)", -1},
1302 {"Yes", -1},
1304 bool ret;
1305 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1306 names , 2, NULL);
1307 if (prefs.view_mode == NARROW)
1308 col = 0;
1309 return ret;
1312 static bool scroll_mode_setting(void)
1314 static const struct opt_items names[] = {
1315 {"Scroll by Page", -1},
1316 {"Scroll by Line", -1},
1319 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1320 names, 2, NULL);
1323 #ifdef HAVE_LCD_BITMAP
1324 static bool page_mode_setting(void)
1326 static const struct opt_items names[] = {
1327 {"No", -1},
1328 {"Yes", -1},
1331 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1332 names, 2, NULL);
1335 static bool scrollbar_setting(void)
1337 static const struct opt_items names[] = {
1338 {"Off", -1},
1339 {"On", -1}
1342 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1343 names, 2, NULL);
1345 #endif
1347 static bool autoscroll_speed_setting(void)
1349 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1350 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1353 static bool viewer_options_menu(void)
1355 int m;
1356 bool result;
1358 static const struct menu_item items[] = {
1359 {"Encoding", encoding_setting },
1360 {"Word Wrap", word_wrap_setting },
1361 {"Line Mode", line_mode_setting },
1362 {"Wide View", view_mode_setting },
1363 #ifdef HAVE_LCD_BITMAP
1364 {"Show Scrollbar", scrollbar_setting },
1365 {"Overlap Pages", page_mode_setting },
1366 #endif
1367 {"Scroll Mode", scroll_mode_setting},
1368 {"Auto-Scroll Speed", autoscroll_speed_setting },
1370 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1371 NULL, NULL, NULL, NULL);
1373 result = menu_run(m);
1374 menu_exit(m);
1375 #ifdef HAVE_LCD_BITMAP
1376 rb->lcd_setmargins(0,0);
1378 /* Show-scrollbar mode for current view-width mode */
1379 init_need_scrollbar();
1380 #endif
1381 return result;
1384 static void viewer_menu(void)
1386 int m;
1387 int result;
1388 static const struct menu_item items[] = {
1389 {"Quit", NULL },
1390 {"Viewer Options", NULL },
1391 {"Show Playback Menu", NULL },
1392 {"Return", NULL },
1395 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1396 result=menu_show(m);
1397 switch (result)
1399 case 0: /* quit */
1400 menu_exit(m);
1401 viewer_exit(NULL);
1402 done = true;
1403 break;
1404 case 1: /* change settings */
1405 done = viewer_options_menu();
1406 break;
1407 case 2: /* playback control */
1408 playback_control(rb);
1409 break;
1410 case 3: /* return */
1411 break;
1413 menu_exit(m);
1414 #ifdef HAVE_LCD_BITMAP
1415 rb->lcd_setmargins(0,0);
1416 #endif
1417 viewer_draw(col);
1420 enum plugin_status plugin_start(struct plugin_api* api, void* file)
1422 int button, i, ok;
1423 int lastbutton = BUTTON_NONE;
1424 bool autoscroll = false;
1425 long old_tick;
1427 rb = api;
1428 old_tick = *rb->current_tick;
1430 /* get the plugin buffer */
1431 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1433 if (!file)
1434 return PLUGIN_ERROR;
1436 file_name = file;
1437 ok = viewer_init();
1438 if (!ok) {
1439 rb->splash(HZ, "Error opening file.");
1440 return PLUGIN_ERROR;
1443 viewer_load_settings(); /* load the preferences and bookmark */
1445 #if LCD_DEPTH > 1
1446 rb->lcd_set_backdrop(NULL);
1447 #endif
1449 viewer_draw(col);
1451 while (!done) {
1453 if(autoscroll)
1455 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1457 viewer_scroll_down();
1458 viewer_draw(col);
1459 old_tick = *rb->current_tick;
1463 button = rb->button_get_w_tmo(HZ/10);
1464 switch (button) {
1465 case VIEWER_MENU:
1466 viewer_menu();
1467 break;
1469 case VIEWER_AUTOSCROLL:
1470 #ifdef VIEWER_AUTOSCROLL_PRE
1471 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1472 break;
1473 #endif
1474 autoscroll = !autoscroll;
1475 break;
1477 case VIEWER_PAGE_UP:
1478 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1479 if (prefs.scroll_mode == PAGE)
1481 /* Page up */
1482 #ifdef HAVE_LCD_BITMAP
1483 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1484 #else
1485 for (i = 0; i < display_lines; i++)
1486 #endif
1487 viewer_scroll_up();
1489 else
1490 viewer_scroll_up();
1491 old_tick = *rb->current_tick;
1492 viewer_draw(col);
1493 break;
1495 case VIEWER_PAGE_DOWN:
1496 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1497 if (prefs.scroll_mode == PAGE)
1499 /* Page down */
1500 if (next_screen_ptr != NULL)
1501 screen_top_ptr = next_screen_to_draw_ptr;
1503 else
1504 viewer_scroll_down();
1505 old_tick = *rb->current_tick;
1506 viewer_draw(col);
1507 break;
1509 case VIEWER_SCREEN_LEFT:
1510 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1511 if (prefs.view_mode == WIDE) {
1512 /* Screen left */
1513 col -= draw_columns;
1514 col = col_limit(col);
1516 else { /* prefs.view_mode == NARROW */
1517 /* Top of file */
1518 viewer_top();
1521 viewer_draw(col);
1522 break;
1524 case VIEWER_SCREEN_RIGHT:
1525 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1526 if (prefs.view_mode == WIDE) {
1527 /* Screen right */
1528 col += draw_columns;
1529 col = col_limit(col);
1531 else { /* prefs.view_mode == NARROW */
1532 /* Bottom of file */
1533 viewer_bottom();
1536 viewer_draw(col);
1537 break;
1539 #ifdef VIEWER_LINE_UP
1540 case VIEWER_LINE_UP:
1541 case VIEWER_LINE_UP | BUTTON_REPEAT:
1542 /* Scroll up one line */
1543 viewer_scroll_up();
1544 old_tick = *rb->current_tick;
1545 viewer_draw(col);
1546 break;
1548 case VIEWER_LINE_DOWN:
1549 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1550 /* Scroll down one line */
1551 if (next_screen_ptr != NULL)
1552 screen_top_ptr = next_line_ptr;
1553 old_tick = *rb->current_tick;
1554 viewer_draw(col);
1555 break;
1556 #endif
1557 #ifdef VIEWER_COLUMN_LEFT
1558 case VIEWER_COLUMN_LEFT:
1559 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1560 if (prefs.view_mode == WIDE) {
1561 /* Scroll left one column */
1562 col -= glyph_width('o');
1563 col = col_limit(col);
1564 viewer_draw(col);
1566 break;
1568 case VIEWER_COLUMN_RIGHT:
1569 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1570 if (prefs.view_mode == WIDE) {
1571 /* Scroll right one column */
1572 col += glyph_width('o');
1573 col = col_limit(col);
1574 viewer_draw(col);
1576 break;
1577 #endif
1579 #ifdef VIEWER_RC_QUIT
1580 case VIEWER_RC_QUIT:
1581 #endif
1582 case VIEWER_QUIT:
1583 viewer_exit(NULL);
1584 done = true;
1585 break;
1587 default:
1588 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1589 == SYS_USB_CONNECTED)
1590 return PLUGIN_USB_CONNECTED;
1591 break;
1593 if (button != BUTTON_NONE)
1595 lastbutton = button;
1596 rb->yield();
1599 return PLUGIN_OK;