allow the plugin playback control menu to be put in a viewport.
[kugel-rb.git] / apps / plugins / viewer.c
bloba2afbc06419b5d9c13fbdf7dc9d3db61f0ecd58e
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 /* iAUdio M3 keys */
236 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
237 #define VIEWER_QUIT BUTTON_RC_REC
238 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
239 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
240 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
241 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
242 #define VIEWER_MENU BUTTON_RC_MENU
243 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
245 #define VIEWER_RC_QUIT BUTTON_REC
247 #elif CONFIG_KEYPAD == COWOND2_PAD
248 #define VIEWER_QUIT BUTTON_POWER
249 #define VIEWER_PAGE_UP BUTTON_UP
250 #define VIEWER_PAGE_DOWN BUTTON_DOWN
251 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
252 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
253 #define VIEWER_MENU BUTTON_MENU
254 #define VIEWER_AUTOSCROLL BUTTON_SELECT
256 #else
257 #error No keymap defined!
258 #endif
260 /* stuff for the bookmarking */
261 struct bookmarked_file_info {
262 long file_position;
263 int top_ptr_pos;
264 char filename[MAX_PATH];
267 struct bookmark_file_data {
268 signed int bookmarked_files_count;
269 struct bookmarked_file_info bookmarks[];
272 struct preferences {
273 enum {
274 WRAP=0,
275 CHOP,
276 } word_mode;
278 enum {
279 NORMAL=0,
280 JOIN,
281 EXPAND,
282 REFLOW, /* won't be set on charcell LCD, must be last */
283 } line_mode;
285 enum {
286 NARROW=0,
287 WIDE,
288 } view_mode;
290 enum {
291 ISO_8859_1=0,
292 ISO_8859_7,
293 ISO_8859_8,
294 CP1251,
295 ISO_8859_11,
296 ISO_8859_6,
297 ISO_8859_9,
298 ISO_8859_2,
299 CP1250,
300 SJIS,
301 GB2312,
302 KSX1001,
303 BIG5,
304 UTF8,
305 ENCODINGS
306 } encoding; /* FIXME: What should default encoding be? */
308 #ifdef HAVE_LCD_BITMAP
309 enum {
310 SB_OFF=0,
311 SB_ON,
312 } scrollbar_mode;
313 bool need_scrollbar;
315 enum {
316 NO_OVERLAP=0,
317 OVERLAP,
318 } page_mode;
319 #endif /* HAVE_LCD_BITMAP */
321 enum {
322 PAGE=0,
323 LINE,
324 } scroll_mode;
326 int autoscroll_speed;
330 struct preferences prefs;
331 struct preferences old_prefs;
333 static unsigned char *buffer;
334 static long buffer_size;
335 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
336 static int display_columns; /* number of (pixel) columns on the display */
337 static int display_lines; /* number of lines on the display */
338 static int draw_columns; /* number of (pixel) columns available for text */
339 static int par_indent_spaces; /* number of spaces to indent first paragraph */
340 static int fd;
341 static char *file_name;
342 static long file_size;
343 static long start_position; /* position in the file after the viewer is started */
344 static bool mac_text;
345 static long file_pos; /* Position of the top of the buffer in the file */
346 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
347 static int max_line_len;
348 static unsigned char *screen_top_ptr;
349 static unsigned char *next_screen_ptr;
350 static unsigned char *next_screen_to_draw_ptr;
351 static unsigned char *next_line_ptr;
352 static struct plugin_api* rb;
353 #ifdef HAVE_LCD_BITMAP
354 static struct font *pf;
355 #endif
358 int glyph_width(int ch)
360 if (ch == 0)
361 ch = ' ';
363 #ifdef HAVE_LCD_BITMAP
364 return rb->font_get_width(pf, ch);
365 #else
366 return 1;
367 #endif
370 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
372 unsigned char utf8_tmp[6];
373 int count;
375 if (prefs.encoding == UTF8)
376 return (unsigned char*)rb->utf8decode(str, ch);
378 count = BUFFER_OOB(str+2)? 1:2;
379 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
380 rb->utf8decode(utf8_tmp, ch);
382 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
383 return (unsigned char*)str+1;
384 else
385 return (unsigned char*)str+2;
388 bool done = false;
389 int col = 0;
391 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
392 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
393 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
394 static unsigned char* crop_at_width(const unsigned char* p)
396 int k,width;
397 unsigned short ch;
398 const unsigned char *oldp = p;
400 k=width=0;
402 while (LINE_IS_NOT_FULL) {
403 oldp = p;
404 p = get_ucs(p, &ch);
405 ADVANCE_COUNTERS(ch);
408 return (unsigned char*)oldp;
411 static unsigned char* find_first_feed(const unsigned char* p, int size)
413 int i;
415 for (i=0; i < size; i++)
416 if (p[i] == 0)
417 return (unsigned char*) p+i;
419 return NULL;
422 static unsigned char* find_last_feed(const unsigned char* p, int size)
424 int i;
426 for (i=size-1; i>=0; i--)
427 if (p[i] == 0)
428 return (unsigned char*) p+i;
430 return NULL;
433 static unsigned char* find_last_space(const unsigned char* p, int size)
435 int i, j, k;
437 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
439 if (!BUFFER_OOB(&p[size]))
440 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
441 if (p[size] == line_break[j])
442 return (unsigned char*) p+size;
444 for (i=size-1; i>=0; i--)
445 for (j=k; j < (int) sizeof(line_break); j++)
447 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
448 if (p[i] == line_break[j])
449 return (unsigned char*) p+i;
452 return NULL;
455 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
457 const unsigned char *next_line = NULL;
458 int size, i, j, k, width, search_len, spaces, newlines;
459 bool first_chars;
460 unsigned char c;
462 if (is_short != NULL)
463 *is_short = true;
465 if BUFFER_OOB(cur_line)
466 return NULL;
468 if (prefs.view_mode == WIDE) {
469 search_len = MAX_WIDTH;
471 else { /* prefs.view_mode == NARROW */
472 search_len = crop_at_width(cur_line) - cur_line;
475 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
477 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
478 /* Need to scan ahead and possibly increase search_len and size,
479 or possibly set next_line at second hard return in a row. */
480 next_line = NULL;
481 first_chars=true;
482 for (j=k=width=spaces=newlines=0; ; j++) {
483 if (BUFFER_OOB(cur_line+j))
484 return NULL;
485 if (LINE_IS_FULL) {
486 size = search_len = j;
487 break;
490 c = cur_line[j];
491 switch (c) {
492 case ' ':
493 if (prefs.line_mode == REFLOW) {
494 if (newlines > 0) {
495 size = j;
496 next_line = cur_line + size;
497 return (unsigned char*) next_line;
499 if (j==0) /* i=1 is intentional */
500 for (i=0; i<par_indent_spaces; i++)
501 ADVANCE_COUNTERS(' ');
503 if (!first_chars) spaces++;
504 break;
506 case 0:
507 if (newlines > 0) {
508 size = j;
509 next_line = cur_line + size - spaces;
510 if (next_line != cur_line)
511 return (unsigned char*) next_line;
512 break;
515 newlines++;
516 size += spaces -1;
517 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
518 return NULL;
519 search_len = size;
520 spaces = first_chars? 0:1;
521 break;
523 default:
524 if (prefs.line_mode==JOIN || newlines>0) {
525 while (spaces) {
526 spaces--;
527 ADVANCE_COUNTERS(' ');
528 if (LINE_IS_FULL) {
529 size = search_len = j;
530 break;
533 newlines=0;
534 } else if (spaces) {
535 /* REFLOW, multiple spaces between words: count only
536 * one. If more are needed, they will be added
537 * while drawing. */
538 search_len = size;
539 spaces=0;
540 ADVANCE_COUNTERS(' ');
541 if (LINE_IS_FULL) {
542 size = search_len = j;
543 break;
546 first_chars = false;
547 ADVANCE_COUNTERS(c);
548 break;
552 else {
553 /* find first hard return */
554 next_line = find_first_feed(cur_line, size);
557 if (next_line == NULL)
558 if (size == search_len) {
559 if (prefs.word_mode == WRAP) /* Find last space */
560 next_line = find_last_space(cur_line, size);
562 if (next_line == NULL)
563 next_line = crop_at_width(cur_line);
564 else
565 if (prefs.word_mode == WRAP)
566 for (i=0;
567 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
568 i++)
569 next_line++;
572 if (prefs.line_mode == EXPAND)
573 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
574 if (next_line[0] == 0)
575 if (next_line != cur_line)
576 return (unsigned char*) next_line;
578 /* If next_line is pointing to a zero, increment it; i.e.,
579 leave the terminator at the end of cur_line. If pointing
580 to a hyphen, increment only if there is room to display
581 the hyphen on current line (won't apply in WIDE mode,
582 since it's guarenteed there won't be room). */
583 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
584 if (next_line[0] == 0)/* ||
585 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
586 next_line++;
588 if (BUFFER_OOB(next_line))
589 return NULL;
591 if (is_short)
592 *is_short = false;
594 return (unsigned char*) next_line;
597 static unsigned char* find_prev_line(const unsigned char* cur_line)
599 const unsigned char *prev_line = NULL;
600 const unsigned char *p;
602 if BUFFER_OOB(cur_line)
603 return NULL;
605 /* To wrap consistently at the same places, we must
606 start with a known hard return, then work downwards.
607 We can either search backwards for a hard return,
608 or simply start wrapping downwards from top of buffer.
609 If current line is not near top of buffer, this is
610 a file with long lines (paragraphs). We would need to
611 read earlier sectors before we could decide how to
612 properly wrap the lines above the current line, but
613 it probably is not worth the disk access. Instead,
614 start with top of buffer and wrap down from there.
615 This may result in some lines wrapping at different
616 points from where they wrap when scrolling down.
617 If buffer is at top of file, start at top of buffer. */
619 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
620 prev_line = p = NULL;
621 else
622 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
623 /* Null means no line feeds in buffer above current line. */
625 if (prev_line == NULL)
626 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
627 prev_line = p = buffer;
628 /* (else return NULL and read previous block) */
630 /* Wrap downwards until too far, then use the one before. */
631 while (p < cur_line && p != NULL) {
632 prev_line = p;
633 p = find_next_line(prev_line, NULL);
636 if (BUFFER_OOB(prev_line))
637 return NULL;
639 return (unsigned char*) prev_line;
642 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
644 /* Read from file and preprocess the data */
645 /* To minimize disk access, always read on sector boundaries */
646 unsigned numread, i;
647 bool found_CR = false;
649 rb->lseek(fd, pos, SEEK_SET);
650 numread = rb->read(fd, buf, size);
651 rb->button_clear_queue(); /* clear button queue */
653 for(i = 0; i < numread; i++) {
654 switch(buf[i]) {
655 case '\r':
656 if (mac_text) {
657 buf[i] = 0;
659 else {
660 buf[i] = ' ';
661 found_CR = true;
663 break;
665 case '\n':
666 buf[i] = 0;
667 found_CR = false;
668 break;
670 case 0: /* No break between case 0 and default, intentionally */
671 buf[i] = ' ';
672 default:
673 if (found_CR) {
674 buf[i - 1] = 0;
675 found_CR = false;
676 mac_text = true;
678 break;
683 static int read_and_synch(int direction)
685 /* Read next (or prev) block, and reposition global pointers. */
686 /* direction: 1 for down (i.e., further into file), -1 for up */
687 int move_size, move_vector, offset;
688 unsigned char *fill_buf;
690 if (direction == -1) /* up */ {
691 move_size = SMALL_BLOCK_SIZE;
692 offset = 0;
693 fill_buf = TOP_SECTOR;
694 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
695 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
697 else /* down */ {
698 if (prefs.view_mode == WIDE) {
699 /* WIDE mode needs more buffer so we have to read smaller blocks */
700 move_size = SMALL_BLOCK_SIZE;
701 offset = LARGE_BLOCK_SIZE;
702 fill_buf = BOTTOM_SECTOR;
703 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
704 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
706 else {
707 move_size = LARGE_BLOCK_SIZE;
708 offset = SMALL_BLOCK_SIZE;
709 fill_buf = MID_SECTOR;
710 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
713 move_vector = direction * move_size;
714 screen_top_ptr -= move_vector;
715 file_pos += move_vector;
716 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
717 fill_buffer(file_pos + offset, fill_buf, move_size);
718 return move_vector;
721 static void viewer_scroll_up(void)
723 unsigned char *p;
725 p = find_prev_line(screen_top_ptr);
726 if (p == NULL && !BUFFER_BOF()) {
727 read_and_synch(-1);
728 p = find_prev_line(screen_top_ptr);
730 if (p != NULL)
731 screen_top_ptr = p;
734 static void viewer_scroll_down(void)
736 if (next_screen_ptr != NULL)
737 screen_top_ptr = next_line_ptr;
740 #ifdef HAVE_LCD_BITMAP
741 static void viewer_scrollbar(void) {
742 int items, min_shown, max_shown;
744 items = (int) file_size; /* (SH1 int is same as long) */
745 min_shown = (int) file_pos + (screen_top_ptr - buffer);
747 if (next_screen_ptr == NULL)
748 max_shown = items;
749 else
750 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
752 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
753 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
755 #endif
757 static void viewer_draw(int col)
759 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
760 int width, extra_spaces, indent_spaces, spaces_per_word;
761 bool multiple_spacing, line_is_short;
762 unsigned short ch;
763 unsigned char *str, *oldstr;
764 unsigned char *line_begin;
765 unsigned char *line_end;
766 unsigned char c;
767 unsigned char scratch_buffer[MAX_COLUMNS + 1];
768 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
769 unsigned char *endptr;
771 /* If col==-1 do all calculations but don't display */
772 if (col != -1) {
773 #ifdef HAVE_LCD_BITMAP
774 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
775 #else
776 left_col = 0;
777 #endif
778 rb->lcd_clear_display();
780 max_line_len = 0;
781 line_begin = line_end = screen_top_ptr;
783 for (i = 0; i < display_lines; i++) {
784 if (BUFFER_OOB(line_end))
785 break; /* Happens after display last line at BUFFER_EOF() */
787 line_begin = line_end;
788 line_end = find_next_line(line_begin, &line_is_short);
790 if (line_end == NULL) {
791 if (BUFFER_EOF()) {
792 if (i < display_lines - 1 && !BUFFER_BOF()) {
793 if (col != -1)
794 rb->lcd_clear_display();
796 for (; i < display_lines - 1; i++)
797 viewer_scroll_up();
799 line_begin = line_end = screen_top_ptr;
800 i = -1;
801 continue;
803 else {
804 line_end = buffer_end;
807 else {
808 resynch_move = read_and_synch(1); /* Read block & move ptrs */
809 line_begin -= resynch_move;
810 if (i > 0)
811 next_line_ptr -= resynch_move;
813 line_end = find_next_line(line_begin, NULL);
814 if (line_end == NULL) /* Should not really happen */
815 break;
818 line_len = line_end - line_begin;
820 /* calculate line_len */
821 str = oldstr = line_begin;
822 j = -1;
823 while (str < line_end) {
824 oldstr = str;
825 str = crop_at_width(str);
826 j++;
828 line_width = j*draw_columns;
829 while (oldstr < line_end) {
830 oldstr = get_ucs(oldstr, &ch);
831 line_width += glyph_width(ch);
834 if (prefs.line_mode == JOIN) {
835 if (line_begin[0] == 0) {
836 line_begin++;
837 if (prefs.word_mode == CHOP)
838 line_end++;
839 else
840 line_len--;
842 for (j=k=spaces=0; j < line_len; j++) {
843 if (k == MAX_COLUMNS)
844 break;
846 c = line_begin[j];
847 switch (c) {
848 case ' ':
849 spaces++;
850 break;
851 case 0:
852 spaces = 0;
853 scratch_buffer[k++] = ' ';
854 break;
855 default:
856 while (spaces) {
857 spaces--;
858 scratch_buffer[k++] = ' ';
859 if (k == MAX_COLUMNS - 1)
860 break;
862 scratch_buffer[k++] = c;
863 break;
866 if (col != -1) {
867 scratch_buffer[k] = 0;
868 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
869 prefs.encoding, draw_columns/glyph_width('i'));
870 *endptr = 0;
873 else if (prefs.line_mode == REFLOW) {
874 if (line_begin[0] == 0) {
875 line_begin++;
876 if (prefs.word_mode == CHOP)
877 line_end++;
878 else
879 line_len--;
882 indent_spaces = 0;
883 if (!line_is_short) {
884 multiple_spacing = false;
885 width=spaces=0;
886 for (str = line_begin; str < line_end; ) {
887 str = get_ucs(str, &ch);
888 switch (ch) {
889 case ' ':
890 case 0:
891 if ((str == line_begin) && (prefs.word_mode==WRAP))
892 /* special case: indent the paragraph,
893 * don't count spaces */
894 indent_spaces = par_indent_spaces;
895 else if (!multiple_spacing)
896 spaces++;
897 multiple_spacing = true;
898 break;
899 default:
900 multiple_spacing = false;
901 width += glyph_width(ch);
902 break;
905 if (multiple_spacing) spaces--;
907 if (spaces) {
908 /* total number of spaces to insert between words */
909 extra_spaces = (draw_columns-width)/glyph_width(' ')
910 - indent_spaces;
911 /* number of spaces between each word*/
912 spaces_per_word = extra_spaces / spaces;
913 /* number of words with n+1 spaces (to fill up) */
914 extra_spaces = extra_spaces % spaces;
915 if (spaces_per_word > 2) { /* too much spacing is awful */
916 spaces_per_word = 3;
917 extra_spaces = 0;
919 } else { /* this doesn't matter much... no spaces anyway */
920 spaces_per_word = extra_spaces = 0;
922 } else { /* end of a paragraph: don't fill line */
923 spaces_per_word = 1;
924 extra_spaces = 0;
927 multiple_spacing = false;
928 for (j=k=spaces=0; j < line_len; j++) {
929 if (k == MAX_COLUMNS)
930 break;
932 c = line_begin[j];
933 switch (c) {
934 case ' ':
935 case 0:
936 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
937 for (j=0; j<par_indent_spaces; j++)
938 scratch_buffer[k++] = ' ';
939 j=0;
941 else if (!multiple_spacing) {
942 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
943 scratch_buffer[k++] = ' ';
944 spaces++;
946 multiple_spacing = true;
947 break;
948 default:
949 scratch_buffer[k++] = c;
950 multiple_spacing = false;
951 break;
955 if (col != -1) {
956 scratch_buffer[k] = 0;
957 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
958 prefs.encoding, k-col);
959 *endptr = 0;
962 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
963 if (col != -1)
964 if (line_width > col) {
965 str = oldstr = line_begin;
966 k = col;
967 width = 0;
968 while( (width<draw_columns) && (oldstr<line_end) )
970 oldstr = get_ucs(oldstr, &ch);
971 if (k > 0) {
972 k -= glyph_width(ch);
973 line_begin = oldstr;
974 } else {
975 width += glyph_width(ch);
979 if(prefs.view_mode==WIDE)
980 endptr = rb->iso_decode(line_begin, utf8_buffer,
981 prefs.encoding, oldstr-line_begin);
982 else
983 endptr = rb->iso_decode(line_begin, utf8_buffer,
984 prefs.encoding, line_end-line_begin);
985 *endptr = 0;
988 if (col != -1 && line_width > col)
989 #ifdef HAVE_LCD_BITMAP
990 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
991 #else
992 rb->lcd_puts(left_col, i, utf8_buffer);
993 #endif
994 if (line_width > max_line_len)
995 max_line_len = line_width;
997 if (i == 0)
998 next_line_ptr = line_end;
1000 next_screen_ptr = line_end;
1001 if (BUFFER_OOB(next_screen_ptr))
1002 next_screen_ptr = NULL;
1004 #ifdef HAVE_LCD_BITMAP
1005 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1007 if (prefs.need_scrollbar)
1008 viewer_scrollbar();
1009 #else
1010 next_screen_to_draw_ptr = next_screen_ptr;
1011 #endif
1013 if (col != -1)
1014 rb->lcd_update();
1017 static void viewer_top(void)
1019 /* Read top of file into buffer
1020 and point screen pointer to top */
1021 if (file_pos != 0)
1023 file_pos = 0;
1024 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1025 fill_buffer(0, buffer, buffer_size);
1028 screen_top_ptr = buffer;
1031 static void viewer_bottom(void)
1033 /* Read bottom of file into buffer
1034 and point screen pointer to bottom */
1035 long last_sectors;
1037 if (file_size > buffer_size) {
1038 /* Find last buffer in file, round up to next sector boundary */
1039 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1040 last_sectors /= SMALL_BLOCK_SIZE;
1041 last_sectors *= SMALL_BLOCK_SIZE;
1043 else {
1044 last_sectors = 0;
1047 if (file_pos != last_sectors)
1049 file_pos = last_sectors;
1050 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1051 fill_buffer(last_sectors, buffer, buffer_size);
1054 screen_top_ptr = buffer_end-1;
1057 #ifdef HAVE_LCD_BITMAP
1058 static void init_need_scrollbar(void) {
1059 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1060 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1061 viewer_draw(-1);
1062 prefs.need_scrollbar = NEED_SCROLLBAR();
1063 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1064 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1066 #else
1067 #define init_need_scrollbar()
1068 #endif
1070 static bool viewer_init(void)
1072 #ifdef HAVE_LCD_BITMAP
1074 pf = rb->font_get(FONT_UI);
1076 display_lines = LCD_HEIGHT / pf->height;
1077 draw_columns = display_columns = LCD_WIDTH;
1078 #else
1079 /* REAL fixed pitch :) all chars use up 1 cell */
1080 display_lines = 2;
1081 draw_columns = display_columns = 11;
1082 par_indent_spaces = 2;
1083 #endif
1085 fd = rb->open(file_name, O_RDONLY);
1086 if (fd==-1)
1087 return false;
1089 file_size = rb->filesize(fd);
1090 if (file_size==-1)
1091 return false;
1093 /* Init mac_text value used in processing buffer */
1094 mac_text = false;
1096 return true;
1099 static void viewer_default_settings(void)
1101 prefs.word_mode = WRAP;
1102 prefs.line_mode = NORMAL;
1103 prefs.view_mode = NARROW;
1104 prefs.scroll_mode = PAGE;
1105 #ifdef HAVE_LCD_BITMAP
1106 prefs.page_mode = NO_OVERLAP;
1107 prefs.scrollbar_mode = SB_OFF;
1108 #endif
1109 prefs.autoscroll_speed = 1;
1110 /* Set codepage to system default */
1111 prefs.encoding = rb->global_settings->default_codepage;
1114 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1116 int settings_fd, i;
1117 struct bookmark_file_data *data;
1118 struct bookmarked_file_info this_bookmark;
1120 /* read settings file */
1121 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1122 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1124 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1125 rb->close(settings_fd);
1127 else
1129 /* load default settings if there is no settings file */
1130 viewer_default_settings();
1133 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1135 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1136 data->bookmarked_files_count = 0;
1138 /* read bookmarks if file exists */
1139 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1140 if (settings_fd >= 0)
1142 /* figure out how many items to read */
1143 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1144 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1145 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1146 rb->read(settings_fd, data->bookmarks,
1147 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1148 rb->close(settings_fd);
1151 file_pos = 0;
1152 screen_top_ptr = buffer;
1154 /* check if current file is in list */
1155 for (i=0; i < data->bookmarked_files_count; i++)
1157 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1159 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1160 int screen_top = screen_pos % buffer_size;
1161 file_pos = screen_pos - screen_top;
1162 screen_top_ptr = buffer + screen_top;
1163 break;
1167 this_bookmark.file_position = file_pos;
1168 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1170 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1171 rb->strcpy(this_bookmark.filename,file_name);
1173 /* prevent potential slot overflow */
1174 if (i >= data->bookmarked_files_count)
1176 if (i < MAX_BOOKMARKED_FILES)
1177 data->bookmarked_files_count++;
1178 else
1179 i = MAX_BOOKMARKED_FILES-1;
1182 /* write bookmark file with spare slot in first position
1183 to be filled in by viewer_save_settings */
1184 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1185 if (settings_fd >=0 )
1187 /* write count */
1188 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1190 /* write the current bookmark */
1191 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1193 /* write everything that was before this bookmark */
1194 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1196 rb->close(settings_fd);
1199 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1201 if (BUFFER_OOB(screen_top_ptr))
1203 screen_top_ptr = buffer;
1206 fill_buffer(file_pos, buffer, buffer_size);
1208 /* remember the current position */
1209 start_position = file_pos + screen_top_ptr - buffer;
1211 init_need_scrollbar();
1214 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1216 int settings_fd;
1218 /* save the viewer settings if they have been changed */
1219 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1221 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1223 if (settings_fd >= 0 )
1225 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1226 rb->close(settings_fd);
1230 /* save the bookmark if the position has changed */
1231 if (file_pos + screen_top_ptr - buffer != start_position)
1233 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1235 if (settings_fd >= 0 )
1237 struct bookmarked_file_info b;
1238 b.file_position = file_pos + screen_top_ptr - buffer;
1239 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1240 rb->memset(&b.filename[0],0,MAX_PATH);
1241 rb->strcpy(b.filename,file_name);
1242 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1243 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1244 rb->close(settings_fd);
1249 static void viewer_exit(void *parameter)
1251 (void)parameter;
1253 viewer_save_settings();
1254 rb->close(fd);
1257 static int col_limit(int col)
1259 if (col < 0)
1260 col = 0;
1261 else
1262 if (col > max_line_len - 2*glyph_width('o'))
1263 col = max_line_len - 2*glyph_width('o');
1265 return col;
1268 /* settings helper functions */
1270 static bool encoding_setting(void)
1272 static const struct opt_items names[] = {
1273 {"ISO-8859-1", -1},
1274 {"ISO-8859-7", -1},
1275 {"ISO-8859-8", -1},
1276 {"CP1251", -1},
1277 {"ISO-8859-11", -1},
1278 {"ISO-8859-6", -1},
1279 {"ISO-8859-9", -1},
1280 {"ISO-8859-2", -1},
1281 {"CP1250", -1},
1282 {"SJIS", -1},
1283 {"GB-2312", -1},
1284 {"KSX-1001", -1},
1285 {"BIG5", -1},
1286 {"UTF-8", -1},
1289 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1290 sizeof(names) / sizeof(names[0]), NULL);
1293 static bool word_wrap_setting(void)
1295 static const struct opt_items names[] = {
1296 {"On", -1},
1297 {"Off (Chop Words)", -1},
1300 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1301 names, 2, NULL);
1304 static bool line_mode_setting(void)
1306 static const struct opt_items names[] = {
1307 {"Normal", -1},
1308 {"Join Lines", -1},
1309 {"Expand Lines", -1},
1310 #ifdef HAVE_LCD_BITMAP
1311 {"Reflow Lines", -1},
1312 #endif
1315 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1316 sizeof(names) / sizeof(names[0]), NULL);
1319 static bool view_mode_setting(void)
1321 static const struct opt_items names[] = {
1322 {"No (Narrow)", -1},
1323 {"Yes", -1},
1325 bool ret;
1326 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1327 names , 2, NULL);
1328 if (prefs.view_mode == NARROW)
1329 col = 0;
1330 return ret;
1333 static bool scroll_mode_setting(void)
1335 static const struct opt_items names[] = {
1336 {"Scroll by Page", -1},
1337 {"Scroll by Line", -1},
1340 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1341 names, 2, NULL);
1344 #ifdef HAVE_LCD_BITMAP
1345 static bool page_mode_setting(void)
1347 static const struct opt_items names[] = {
1348 {"No", -1},
1349 {"Yes", -1},
1352 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1353 names, 2, NULL);
1356 static bool scrollbar_setting(void)
1358 static const struct opt_items names[] = {
1359 {"Off", -1},
1360 {"On", -1}
1363 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1364 names, 2, NULL);
1366 #endif
1368 static bool autoscroll_speed_setting(void)
1370 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1371 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1374 static bool viewer_options_menu(void)
1376 int m;
1377 bool result;
1379 static const struct menu_item items[] = {
1380 {"Encoding", encoding_setting },
1381 {"Word Wrap", word_wrap_setting },
1382 {"Line Mode", line_mode_setting },
1383 {"Wide View", view_mode_setting },
1384 #ifdef HAVE_LCD_BITMAP
1385 {"Show Scrollbar", scrollbar_setting },
1386 {"Overlap Pages", page_mode_setting },
1387 #endif
1388 {"Scroll Mode", scroll_mode_setting},
1389 {"Auto-Scroll Speed", autoscroll_speed_setting },
1391 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1392 NULL, NULL, NULL, NULL);
1394 result = menu_run(m);
1395 menu_exit(m);
1396 #ifdef HAVE_LCD_BITMAP
1397 rb->lcd_setmargins(0,0);
1399 /* Show-scrollbar mode for current view-width mode */
1400 init_need_scrollbar();
1401 #endif
1402 return result;
1405 static void viewer_menu(void)
1407 int m;
1408 int result;
1409 static const struct menu_item items[] = {
1410 {"Quit", NULL },
1411 {"Viewer Options", NULL },
1412 {"Show Playback Menu", NULL },
1413 {"Return", NULL },
1416 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1417 result=menu_show(m);
1418 switch (result)
1420 case 0: /* quit */
1421 menu_exit(m);
1422 viewer_exit(NULL);
1423 done = true;
1424 break;
1425 case 1: /* change settings */
1426 done = viewer_options_menu();
1427 break;
1428 case 2: /* playback control */
1429 playback_control(rb, NULL);
1430 break;
1431 case 3: /* return */
1432 break;
1434 menu_exit(m);
1435 #ifdef HAVE_LCD_BITMAP
1436 rb->lcd_setmargins(0,0);
1437 #endif
1438 viewer_draw(col);
1441 enum plugin_status plugin_start(struct plugin_api* api, void* file)
1443 int button, i, ok;
1444 int lastbutton = BUTTON_NONE;
1445 bool autoscroll = false;
1446 long old_tick;
1448 rb = api;
1449 old_tick = *rb->current_tick;
1451 /* get the plugin buffer */
1452 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1454 if (!file)
1455 return PLUGIN_ERROR;
1457 file_name = file;
1458 ok = viewer_init();
1459 if (!ok) {
1460 rb->splash(HZ, "Error opening file.");
1461 return PLUGIN_ERROR;
1464 viewer_load_settings(); /* load the preferences and bookmark */
1466 #if LCD_DEPTH > 1
1467 rb->lcd_set_backdrop(NULL);
1468 #endif
1470 viewer_draw(col);
1472 while (!done) {
1474 if(autoscroll)
1476 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1478 viewer_scroll_down();
1479 viewer_draw(col);
1480 old_tick = *rb->current_tick;
1484 button = rb->button_get_w_tmo(HZ/10);
1485 switch (button) {
1486 case VIEWER_MENU:
1487 viewer_menu();
1488 break;
1490 case VIEWER_AUTOSCROLL:
1491 #ifdef VIEWER_AUTOSCROLL_PRE
1492 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1493 break;
1494 #endif
1495 autoscroll = !autoscroll;
1496 break;
1498 case VIEWER_PAGE_UP:
1499 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1500 if (prefs.scroll_mode == PAGE)
1502 /* Page up */
1503 #ifdef HAVE_LCD_BITMAP
1504 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1505 #else
1506 for (i = 0; i < display_lines; i++)
1507 #endif
1508 viewer_scroll_up();
1510 else
1511 viewer_scroll_up();
1512 old_tick = *rb->current_tick;
1513 viewer_draw(col);
1514 break;
1516 case VIEWER_PAGE_DOWN:
1517 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1518 if (prefs.scroll_mode == PAGE)
1520 /* Page down */
1521 if (next_screen_ptr != NULL)
1522 screen_top_ptr = next_screen_to_draw_ptr;
1524 else
1525 viewer_scroll_down();
1526 old_tick = *rb->current_tick;
1527 viewer_draw(col);
1528 break;
1530 case VIEWER_SCREEN_LEFT:
1531 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1532 if (prefs.view_mode == WIDE) {
1533 /* Screen left */
1534 col -= draw_columns;
1535 col = col_limit(col);
1537 else { /* prefs.view_mode == NARROW */
1538 /* Top of file */
1539 viewer_top();
1542 viewer_draw(col);
1543 break;
1545 case VIEWER_SCREEN_RIGHT:
1546 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1547 if (prefs.view_mode == WIDE) {
1548 /* Screen right */
1549 col += draw_columns;
1550 col = col_limit(col);
1552 else { /* prefs.view_mode == NARROW */
1553 /* Bottom of file */
1554 viewer_bottom();
1557 viewer_draw(col);
1558 break;
1560 #ifdef VIEWER_LINE_UP
1561 case VIEWER_LINE_UP:
1562 case VIEWER_LINE_UP | BUTTON_REPEAT:
1563 /* Scroll up one line */
1564 viewer_scroll_up();
1565 old_tick = *rb->current_tick;
1566 viewer_draw(col);
1567 break;
1569 case VIEWER_LINE_DOWN:
1570 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1571 /* Scroll down one line */
1572 if (next_screen_ptr != NULL)
1573 screen_top_ptr = next_line_ptr;
1574 old_tick = *rb->current_tick;
1575 viewer_draw(col);
1576 break;
1577 #endif
1578 #ifdef VIEWER_COLUMN_LEFT
1579 case VIEWER_COLUMN_LEFT:
1580 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1581 if (prefs.view_mode == WIDE) {
1582 /* Scroll left one column */
1583 col -= glyph_width('o');
1584 col = col_limit(col);
1585 viewer_draw(col);
1587 break;
1589 case VIEWER_COLUMN_RIGHT:
1590 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1591 if (prefs.view_mode == WIDE) {
1592 /* Scroll right one column */
1593 col += glyph_width('o');
1594 col = col_limit(col);
1595 viewer_draw(col);
1597 break;
1598 #endif
1600 #ifdef VIEWER_RC_QUIT
1601 case VIEWER_RC_QUIT:
1602 #endif
1603 case VIEWER_QUIT:
1604 viewer_exit(NULL);
1605 done = true;
1606 break;
1608 default:
1609 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1610 == SYS_USB_CONNECTED)
1611 return PLUGIN_USB_CONNECTED;
1612 break;
1614 if (button != BUTTON_NONE)
1616 lastbutton = button;
1617 rb->yield();
1620 return PLUGIN_OK;