Rename variables sectorbuf and verbose to avoid clashes in rbutil. Cleanup exports...
[Rockbox.git] / apps / plugins / viewer.c
blobd596f43b837534510803a47ca727b2f303058562
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 /*M-Robe 500 keys */
209 #elif CONFIG_KEYPAD == MROBE500_PAD
210 #define VIEWER_QUIT BUTTON_POWER
211 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
212 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
213 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
214 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
215 #define VIEWER_MENU BUTTON_RC_HEART
216 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
218 /*Gigabeat S keys */
219 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
220 #define VIEWER_QUIT BUTTON_BACK
221 #define VIEWER_PAGE_UP BUTTON_PREV
222 #define VIEWER_PAGE_DOWN BUTTON_NEXT
223 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
224 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
225 #define VIEWER_MENU BUTTON_MENU
226 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
227 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
228 #define VIEWER_LINE_UP BUTTON_UP
229 #define VIEWER_LINE_DOWN BUTTON_DOWN
230 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
231 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
233 /*M-Robe 100 keys */
234 #elif CONFIG_KEYPAD == MROBE100_PAD
235 #define VIEWER_QUIT BUTTON_POWER
236 #define VIEWER_PAGE_UP BUTTON_UP
237 #define VIEWER_PAGE_DOWN BUTTON_DOWN
238 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
239 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
240 #define VIEWER_MENU BUTTON_MENU
241 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
243 /* iAUdio M3 keys */
244 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
245 #define VIEWER_QUIT BUTTON_RC_REC
246 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
247 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
248 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
249 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
250 #define VIEWER_MENU BUTTON_RC_MENU
251 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
252 #define VIEWER_RC_QUIT BUTTON_REC
254 /* Cowon D2 keys */
255 #elif CONFIG_KEYPAD == COWOND2_PAD
256 #define VIEWER_QUIT BUTTON_POWER
257 #define VIEWER_MENU BUTTON_MENU
259 #else
260 #error No keymap defined!
261 #endif
263 #ifdef HAVE_TOUCHPAD
264 #ifndef VIEWER_QUIT
265 #define VIEWER_QUIT BUTTON_TOPLEFT
266 #endif
267 #ifndef VIEWER_PAGE_UP
268 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
269 #endif
270 #ifndef VIEWER_PAGE_DOWN
271 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
272 #endif
273 #ifndef VIEWER_SCREEN_LEFT
274 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
275 #endif
276 #ifndef VIEWER_SCREEN_RIGHT
277 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
278 #endif
279 #ifndef VIEWER_MENU
280 #define VIEWER_MENU BUTTON_TOPRIGHT
281 #endif
282 #ifndef VIEWER_AUTOSCROLL
283 #define VIEWER_AUTOSCROLL BUTTON_CENTER
284 #endif
285 #endif
287 /* stuff for the bookmarking */
288 struct bookmarked_file_info {
289 long file_position;
290 int top_ptr_pos;
291 char filename[MAX_PATH];
294 struct bookmark_file_data {
295 signed int bookmarked_files_count;
296 struct bookmarked_file_info bookmarks[];
299 struct preferences {
300 enum {
301 WRAP=0,
302 CHOP,
303 } word_mode;
305 enum {
306 NORMAL=0,
307 JOIN,
308 EXPAND,
309 REFLOW, /* won't be set on charcell LCD, must be last */
310 } line_mode;
312 enum {
313 NARROW=0,
314 WIDE,
315 } view_mode;
317 enum {
318 ISO_8859_1=0,
319 ISO_8859_7,
320 ISO_8859_8,
321 CP1251,
322 ISO_8859_11,
323 ISO_8859_6,
324 ISO_8859_9,
325 ISO_8859_2,
326 CP1250,
327 SJIS,
328 GB2312,
329 KSX1001,
330 BIG5,
331 UTF8,
332 ENCODINGS
333 } encoding; /* FIXME: What should default encoding be? */
335 #ifdef HAVE_LCD_BITMAP
336 enum {
337 SB_OFF=0,
338 SB_ON,
339 } scrollbar_mode;
340 bool need_scrollbar;
342 enum {
343 NO_OVERLAP=0,
344 OVERLAP,
345 } page_mode;
346 #endif /* HAVE_LCD_BITMAP */
348 enum {
349 PAGE=0,
350 LINE,
351 } scroll_mode;
353 int autoscroll_speed;
357 struct preferences prefs;
358 struct preferences old_prefs;
360 static unsigned char *buffer;
361 static long buffer_size;
362 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
363 static int display_columns; /* number of (pixel) columns on the display */
364 static int display_lines; /* number of lines on the display */
365 static int draw_columns; /* number of (pixel) columns available for text */
366 static int par_indent_spaces; /* number of spaces to indent first paragraph */
367 static int fd;
368 static const char *file_name;
369 static long file_size;
370 static long start_position; /* position in the file after the viewer is started */
371 static bool mac_text;
372 static long file_pos; /* Position of the top of the buffer in the file */
373 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
374 static int max_line_len;
375 static unsigned char *screen_top_ptr;
376 static unsigned char *next_screen_ptr;
377 static unsigned char *next_screen_to_draw_ptr;
378 static unsigned char *next_line_ptr;
379 static const struct plugin_api* rb;
380 #ifdef HAVE_LCD_BITMAP
381 static struct font *pf;
382 #endif
385 int glyph_width(int ch)
387 if (ch == 0)
388 ch = ' ';
390 #ifdef HAVE_LCD_BITMAP
391 return rb->font_get_width(pf, ch);
392 #else
393 return 1;
394 #endif
397 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
399 unsigned char utf8_tmp[6];
400 int count;
402 if (prefs.encoding == UTF8)
403 return (unsigned char*)rb->utf8decode(str, ch);
405 count = BUFFER_OOB(str+2)? 1:2;
406 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
407 rb->utf8decode(utf8_tmp, ch);
409 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
410 return (unsigned char*)str+1;
411 else
412 return (unsigned char*)str+2;
415 bool done = false;
416 int col = 0;
418 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
419 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
420 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
421 static unsigned char* crop_at_width(const unsigned char* p)
423 int k,width;
424 unsigned short ch;
425 const unsigned char *oldp = p;
427 k=width=0;
429 while (LINE_IS_NOT_FULL) {
430 oldp = p;
431 p = get_ucs(p, &ch);
432 ADVANCE_COUNTERS(ch);
435 return (unsigned char*)oldp;
438 static unsigned char* find_first_feed(const unsigned char* p, int size)
440 int i;
442 for (i=0; i < size; i++)
443 if (p[i] == 0)
444 return (unsigned char*) p+i;
446 return NULL;
449 static unsigned char* find_last_feed(const unsigned char* p, int size)
451 int i;
453 for (i=size-1; i>=0; i--)
454 if (p[i] == 0)
455 return (unsigned char*) p+i;
457 return NULL;
460 static unsigned char* find_last_space(const unsigned char* p, int size)
462 int i, j, k;
464 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
466 if (!BUFFER_OOB(&p[size]))
467 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
468 if (p[size] == line_break[j])
469 return (unsigned char*) p+size;
471 for (i=size-1; i>=0; i--)
472 for (j=k; j < (int) sizeof(line_break); j++)
474 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
475 if (p[i] == line_break[j])
476 return (unsigned char*) p+i;
479 return NULL;
482 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
484 const unsigned char *next_line = NULL;
485 int size, i, j, k, width, search_len, spaces, newlines;
486 bool first_chars;
487 unsigned char c;
489 if (is_short != NULL)
490 *is_short = true;
492 if BUFFER_OOB(cur_line)
493 return NULL;
495 if (prefs.view_mode == WIDE) {
496 search_len = MAX_WIDTH;
498 else { /* prefs.view_mode == NARROW */
499 search_len = crop_at_width(cur_line) - cur_line;
502 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
504 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
505 /* Need to scan ahead and possibly increase search_len and size,
506 or possibly set next_line at second hard return in a row. */
507 next_line = NULL;
508 first_chars=true;
509 for (j=k=width=spaces=newlines=0; ; j++) {
510 if (BUFFER_OOB(cur_line+j))
511 return NULL;
512 if (LINE_IS_FULL) {
513 size = search_len = j;
514 break;
517 c = cur_line[j];
518 switch (c) {
519 case ' ':
520 if (prefs.line_mode == REFLOW) {
521 if (newlines > 0) {
522 size = j;
523 next_line = cur_line + size;
524 return (unsigned char*) next_line;
526 if (j==0) /* i=1 is intentional */
527 for (i=0; i<par_indent_spaces; i++)
528 ADVANCE_COUNTERS(' ');
530 if (!first_chars) spaces++;
531 break;
533 case 0:
534 if (newlines > 0) {
535 size = j;
536 next_line = cur_line + size - spaces;
537 if (next_line != cur_line)
538 return (unsigned char*) next_line;
539 break;
542 newlines++;
543 size += spaces -1;
544 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
545 return NULL;
546 search_len = size;
547 spaces = first_chars? 0:1;
548 break;
550 default:
551 if (prefs.line_mode==JOIN || newlines>0) {
552 while (spaces) {
553 spaces--;
554 ADVANCE_COUNTERS(' ');
555 if (LINE_IS_FULL) {
556 size = search_len = j;
557 break;
560 newlines=0;
561 } else if (spaces) {
562 /* REFLOW, multiple spaces between words: count only
563 * one. If more are needed, they will be added
564 * while drawing. */
565 search_len = size;
566 spaces=0;
567 ADVANCE_COUNTERS(' ');
568 if (LINE_IS_FULL) {
569 size = search_len = j;
570 break;
573 first_chars = false;
574 ADVANCE_COUNTERS(c);
575 break;
579 else {
580 /* find first hard return */
581 next_line = find_first_feed(cur_line, size);
584 if (next_line == NULL)
585 if (size == search_len) {
586 if (prefs.word_mode == WRAP) /* Find last space */
587 next_line = find_last_space(cur_line, size);
589 if (next_line == NULL)
590 next_line = crop_at_width(cur_line);
591 else
592 if (prefs.word_mode == WRAP)
593 for (i=0;
594 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
595 i++)
596 next_line++;
599 if (prefs.line_mode == EXPAND)
600 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
601 if (next_line[0] == 0)
602 if (next_line != cur_line)
603 return (unsigned char*) next_line;
605 /* If next_line is pointing to a zero, increment it; i.e.,
606 leave the terminator at the end of cur_line. If pointing
607 to a hyphen, increment only if there is room to display
608 the hyphen on current line (won't apply in WIDE mode,
609 since it's guarenteed there won't be room). */
610 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
611 if (next_line[0] == 0)/* ||
612 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
613 next_line++;
615 if (BUFFER_OOB(next_line))
616 return NULL;
618 if (is_short)
619 *is_short = false;
621 return (unsigned char*) next_line;
624 static unsigned char* find_prev_line(const unsigned char* cur_line)
626 const unsigned char *prev_line = NULL;
627 const unsigned char *p;
629 if BUFFER_OOB(cur_line)
630 return NULL;
632 /* To wrap consistently at the same places, we must
633 start with a known hard return, then work downwards.
634 We can either search backwards for a hard return,
635 or simply start wrapping downwards from top of buffer.
636 If current line is not near top of buffer, this is
637 a file with long lines (paragraphs). We would need to
638 read earlier sectors before we could decide how to
639 properly wrap the lines above the current line, but
640 it probably is not worth the disk access. Instead,
641 start with top of buffer and wrap down from there.
642 This may result in some lines wrapping at different
643 points from where they wrap when scrolling down.
644 If buffer is at top of file, start at top of buffer. */
646 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
647 prev_line = p = NULL;
648 else
649 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
650 /* Null means no line feeds in buffer above current line. */
652 if (prev_line == NULL)
653 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
654 prev_line = p = buffer;
655 /* (else return NULL and read previous block) */
657 /* Wrap downwards until too far, then use the one before. */
658 while (p < cur_line && p != NULL) {
659 prev_line = p;
660 p = find_next_line(prev_line, NULL);
663 if (BUFFER_OOB(prev_line))
664 return NULL;
666 return (unsigned char*) prev_line;
669 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
671 /* Read from file and preprocess the data */
672 /* To minimize disk access, always read on sector boundaries */
673 unsigned numread, i;
674 bool found_CR = false;
676 rb->lseek(fd, pos, SEEK_SET);
677 numread = rb->read(fd, buf, size);
678 rb->button_clear_queue(); /* clear button queue */
680 for(i = 0; i < numread; i++) {
681 switch(buf[i]) {
682 case '\r':
683 if (mac_text) {
684 buf[i] = 0;
686 else {
687 buf[i] = ' ';
688 found_CR = true;
690 break;
692 case '\n':
693 buf[i] = 0;
694 found_CR = false;
695 break;
697 case 0: /* No break between case 0 and default, intentionally */
698 buf[i] = ' ';
699 default:
700 if (found_CR) {
701 buf[i - 1] = 0;
702 found_CR = false;
703 mac_text = true;
705 break;
710 static int read_and_synch(int direction)
712 /* Read next (or prev) block, and reposition global pointers. */
713 /* direction: 1 for down (i.e., further into file), -1 for up */
714 int move_size, move_vector, offset;
715 unsigned char *fill_buf;
717 if (direction == -1) /* up */ {
718 move_size = SMALL_BLOCK_SIZE;
719 offset = 0;
720 fill_buf = TOP_SECTOR;
721 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
722 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
724 else /* down */ {
725 if (prefs.view_mode == WIDE) {
726 /* WIDE mode needs more buffer so we have to read smaller blocks */
727 move_size = SMALL_BLOCK_SIZE;
728 offset = LARGE_BLOCK_SIZE;
729 fill_buf = BOTTOM_SECTOR;
730 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
731 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
733 else {
734 move_size = LARGE_BLOCK_SIZE;
735 offset = SMALL_BLOCK_SIZE;
736 fill_buf = MID_SECTOR;
737 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
740 move_vector = direction * move_size;
741 screen_top_ptr -= move_vector;
742 file_pos += move_vector;
743 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
744 fill_buffer(file_pos + offset, fill_buf, move_size);
745 return move_vector;
748 static void viewer_scroll_up(void)
750 unsigned char *p;
752 p = find_prev_line(screen_top_ptr);
753 if (p == NULL && !BUFFER_BOF()) {
754 read_and_synch(-1);
755 p = find_prev_line(screen_top_ptr);
757 if (p != NULL)
758 screen_top_ptr = p;
761 static void viewer_scroll_down(void)
763 if (next_screen_ptr != NULL)
764 screen_top_ptr = next_line_ptr;
767 #ifdef HAVE_LCD_BITMAP
768 static void viewer_scrollbar(void) {
769 int items, min_shown, max_shown;
771 items = (int) file_size; /* (SH1 int is same as long) */
772 min_shown = (int) file_pos + (screen_top_ptr - buffer);
774 if (next_screen_ptr == NULL)
775 max_shown = items;
776 else
777 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
779 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
780 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
782 #endif
784 static void viewer_draw(int col)
786 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
787 int width, extra_spaces, indent_spaces, spaces_per_word;
788 bool multiple_spacing, line_is_short;
789 unsigned short ch;
790 unsigned char *str, *oldstr;
791 unsigned char *line_begin;
792 unsigned char *line_end;
793 unsigned char c;
794 unsigned char scratch_buffer[MAX_COLUMNS + 1];
795 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
796 unsigned char *endptr;
798 /* If col==-1 do all calculations but don't display */
799 if (col != -1) {
800 #ifdef HAVE_LCD_BITMAP
801 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
802 #else
803 left_col = 0;
804 #endif
805 rb->lcd_clear_display();
807 max_line_len = 0;
808 line_begin = line_end = screen_top_ptr;
810 for (i = 0; i < display_lines; i++) {
811 if (BUFFER_OOB(line_end))
812 break; /* Happens after display last line at BUFFER_EOF() */
814 line_begin = line_end;
815 line_end = find_next_line(line_begin, &line_is_short);
817 if (line_end == NULL) {
818 if (BUFFER_EOF()) {
819 if (i < display_lines - 1 && !BUFFER_BOF()) {
820 if (col != -1)
821 rb->lcd_clear_display();
823 for (; i < display_lines - 1; i++)
824 viewer_scroll_up();
826 line_begin = line_end = screen_top_ptr;
827 i = -1;
828 continue;
830 else {
831 line_end = buffer_end;
834 else {
835 resynch_move = read_and_synch(1); /* Read block & move ptrs */
836 line_begin -= resynch_move;
837 if (i > 0)
838 next_line_ptr -= resynch_move;
840 line_end = find_next_line(line_begin, NULL);
841 if (line_end == NULL) /* Should not really happen */
842 break;
845 line_len = line_end - line_begin;
847 /* calculate line_len */
848 str = oldstr = line_begin;
849 j = -1;
850 while (str < line_end) {
851 oldstr = str;
852 str = crop_at_width(str);
853 j++;
855 line_width = j*draw_columns;
856 while (oldstr < line_end) {
857 oldstr = get_ucs(oldstr, &ch);
858 line_width += glyph_width(ch);
861 if (prefs.line_mode == JOIN) {
862 if (line_begin[0] == 0) {
863 line_begin++;
864 if (prefs.word_mode == CHOP)
865 line_end++;
866 else
867 line_len--;
869 for (j=k=spaces=0; j < line_len; j++) {
870 if (k == MAX_COLUMNS)
871 break;
873 c = line_begin[j];
874 switch (c) {
875 case ' ':
876 spaces++;
877 break;
878 case 0:
879 spaces = 0;
880 scratch_buffer[k++] = ' ';
881 break;
882 default:
883 while (spaces) {
884 spaces--;
885 scratch_buffer[k++] = ' ';
886 if (k == MAX_COLUMNS - 1)
887 break;
889 scratch_buffer[k++] = c;
890 break;
893 if (col != -1) {
894 scratch_buffer[k] = 0;
895 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
896 prefs.encoding, draw_columns/glyph_width('i'));
897 *endptr = 0;
900 else if (prefs.line_mode == REFLOW) {
901 if (line_begin[0] == 0) {
902 line_begin++;
903 if (prefs.word_mode == CHOP)
904 line_end++;
905 else
906 line_len--;
909 indent_spaces = 0;
910 if (!line_is_short) {
911 multiple_spacing = false;
912 width=spaces=0;
913 for (str = line_begin; str < line_end; ) {
914 str = get_ucs(str, &ch);
915 switch (ch) {
916 case ' ':
917 case 0:
918 if ((str == line_begin) && (prefs.word_mode==WRAP))
919 /* special case: indent the paragraph,
920 * don't count spaces */
921 indent_spaces = par_indent_spaces;
922 else if (!multiple_spacing)
923 spaces++;
924 multiple_spacing = true;
925 break;
926 default:
927 multiple_spacing = false;
928 width += glyph_width(ch);
929 break;
932 if (multiple_spacing) spaces--;
934 if (spaces) {
935 /* total number of spaces to insert between words */
936 extra_spaces = (draw_columns-width)/glyph_width(' ')
937 - indent_spaces;
938 /* number of spaces between each word*/
939 spaces_per_word = extra_spaces / spaces;
940 /* number of words with n+1 spaces (to fill up) */
941 extra_spaces = extra_spaces % spaces;
942 if (spaces_per_word > 2) { /* too much spacing is awful */
943 spaces_per_word = 3;
944 extra_spaces = 0;
946 } else { /* this doesn't matter much... no spaces anyway */
947 spaces_per_word = extra_spaces = 0;
949 } else { /* end of a paragraph: don't fill line */
950 spaces_per_word = 1;
951 extra_spaces = 0;
954 multiple_spacing = false;
955 for (j=k=spaces=0; j < line_len; j++) {
956 if (k == MAX_COLUMNS)
957 break;
959 c = line_begin[j];
960 switch (c) {
961 case ' ':
962 case 0:
963 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
964 for (j=0; j<par_indent_spaces; j++)
965 scratch_buffer[k++] = ' ';
966 j=0;
968 else if (!multiple_spacing) {
969 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
970 scratch_buffer[k++] = ' ';
971 spaces++;
973 multiple_spacing = true;
974 break;
975 default:
976 scratch_buffer[k++] = c;
977 multiple_spacing = false;
978 break;
982 if (col != -1) {
983 scratch_buffer[k] = 0;
984 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
985 prefs.encoding, k-col);
986 *endptr = 0;
989 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
990 if (col != -1)
991 if (line_width > col) {
992 str = oldstr = line_begin;
993 k = col;
994 width = 0;
995 while( (width<draw_columns) && (oldstr<line_end) )
997 oldstr = get_ucs(oldstr, &ch);
998 if (k > 0) {
999 k -= glyph_width(ch);
1000 line_begin = oldstr;
1001 } else {
1002 width += glyph_width(ch);
1006 if(prefs.view_mode==WIDE)
1007 endptr = rb->iso_decode(line_begin, utf8_buffer,
1008 prefs.encoding, oldstr-line_begin);
1009 else
1010 endptr = rb->iso_decode(line_begin, utf8_buffer,
1011 prefs.encoding, line_end-line_begin);
1012 *endptr = 0;
1015 if (col != -1 && line_width > col)
1016 #ifdef HAVE_LCD_BITMAP
1017 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1018 #else
1019 rb->lcd_puts(left_col, i, utf8_buffer);
1020 #endif
1021 if (line_width > max_line_len)
1022 max_line_len = line_width;
1024 if (i == 0)
1025 next_line_ptr = line_end;
1027 next_screen_ptr = line_end;
1028 if (BUFFER_OOB(next_screen_ptr))
1029 next_screen_ptr = NULL;
1031 #ifdef HAVE_LCD_BITMAP
1032 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1034 if (prefs.need_scrollbar)
1035 viewer_scrollbar();
1036 #else
1037 next_screen_to_draw_ptr = next_screen_ptr;
1038 #endif
1040 if (col != -1)
1041 rb->lcd_update();
1044 static void viewer_top(void)
1046 /* Read top of file into buffer
1047 and point screen pointer to top */
1048 if (file_pos != 0)
1050 file_pos = 0;
1051 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1052 fill_buffer(0, buffer, buffer_size);
1055 screen_top_ptr = buffer;
1058 static void viewer_bottom(void)
1060 /* Read bottom of file into buffer
1061 and point screen pointer to bottom */
1062 long last_sectors;
1064 if (file_size > buffer_size) {
1065 /* Find last buffer in file, round up to next sector boundary */
1066 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1067 last_sectors /= SMALL_BLOCK_SIZE;
1068 last_sectors *= SMALL_BLOCK_SIZE;
1070 else {
1071 last_sectors = 0;
1074 if (file_pos != last_sectors)
1076 file_pos = last_sectors;
1077 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1078 fill_buffer(last_sectors, buffer, buffer_size);
1081 screen_top_ptr = buffer_end-1;
1084 #ifdef HAVE_LCD_BITMAP
1085 static void init_need_scrollbar(void) {
1086 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1087 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1088 viewer_draw(-1);
1089 prefs.need_scrollbar = NEED_SCROLLBAR();
1090 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1091 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1093 #else
1094 #define init_need_scrollbar()
1095 #endif
1097 static bool viewer_init(void)
1099 #ifdef HAVE_LCD_BITMAP
1101 pf = rb->font_get(FONT_UI);
1103 display_lines = LCD_HEIGHT / pf->height;
1104 draw_columns = display_columns = LCD_WIDTH;
1105 #else
1106 /* REAL fixed pitch :) all chars use up 1 cell */
1107 display_lines = 2;
1108 draw_columns = display_columns = 11;
1109 par_indent_spaces = 2;
1110 #endif
1112 fd = rb->open(file_name, O_RDONLY);
1113 if (fd==-1)
1114 return false;
1116 file_size = rb->filesize(fd);
1117 if (file_size==-1)
1118 return false;
1120 /* Init mac_text value used in processing buffer */
1121 mac_text = false;
1123 return true;
1126 static void viewer_default_settings(void)
1128 prefs.word_mode = WRAP;
1129 prefs.line_mode = NORMAL;
1130 prefs.view_mode = NARROW;
1131 prefs.scroll_mode = PAGE;
1132 #ifdef HAVE_LCD_BITMAP
1133 prefs.page_mode = NO_OVERLAP;
1134 prefs.scrollbar_mode = SB_OFF;
1135 #endif
1136 prefs.autoscroll_speed = 1;
1137 /* Set codepage to system default */
1138 prefs.encoding = rb->global_settings->default_codepage;
1141 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1143 int settings_fd, i;
1144 struct bookmark_file_data *data;
1145 struct bookmarked_file_info this_bookmark;
1147 /* read settings file */
1148 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1149 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1151 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1152 rb->close(settings_fd);
1154 else
1156 /* load default settings if there is no settings file */
1157 viewer_default_settings();
1160 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1162 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1163 data->bookmarked_files_count = 0;
1165 /* read bookmarks if file exists */
1166 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1167 if (settings_fd >= 0)
1169 /* figure out how many items to read */
1170 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1171 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1172 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1173 rb->read(settings_fd, data->bookmarks,
1174 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1175 rb->close(settings_fd);
1178 file_pos = 0;
1179 screen_top_ptr = buffer;
1181 /* check if current file is in list */
1182 for (i=0; i < data->bookmarked_files_count; i++)
1184 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1186 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1187 int screen_top = screen_pos % buffer_size;
1188 file_pos = screen_pos - screen_top;
1189 screen_top_ptr = buffer + screen_top;
1190 break;
1194 this_bookmark.file_position = file_pos;
1195 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1197 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1198 rb->strcpy(this_bookmark.filename,file_name);
1200 /* prevent potential slot overflow */
1201 if (i >= data->bookmarked_files_count)
1203 if (i < MAX_BOOKMARKED_FILES)
1204 data->bookmarked_files_count++;
1205 else
1206 i = MAX_BOOKMARKED_FILES-1;
1209 /* write bookmark file with spare slot in first position
1210 to be filled in by viewer_save_settings */
1211 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1212 if (settings_fd >=0 )
1214 /* write count */
1215 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1217 /* write the current bookmark */
1218 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1220 /* write everything that was before this bookmark */
1221 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1223 rb->close(settings_fd);
1226 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1228 if (BUFFER_OOB(screen_top_ptr))
1230 screen_top_ptr = buffer;
1233 fill_buffer(file_pos, buffer, buffer_size);
1235 /* remember the current position */
1236 start_position = file_pos + screen_top_ptr - buffer;
1238 init_need_scrollbar();
1241 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1243 int settings_fd;
1245 /* save the viewer settings if they have been changed */
1246 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1248 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1250 if (settings_fd >= 0 )
1252 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1253 rb->close(settings_fd);
1257 /* save the bookmark if the position has changed */
1258 if (file_pos + screen_top_ptr - buffer != start_position)
1260 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1262 if (settings_fd >= 0 )
1264 struct bookmarked_file_info b;
1265 b.file_position = file_pos + screen_top_ptr - buffer;
1266 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1267 rb->memset(&b.filename[0],0,MAX_PATH);
1268 rb->strcpy(b.filename,file_name);
1269 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1270 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1271 rb->close(settings_fd);
1276 static void viewer_exit(void *parameter)
1278 (void)parameter;
1280 viewer_save_settings();
1281 rb->close(fd);
1284 static int col_limit(int col)
1286 if (col < 0)
1287 col = 0;
1288 else
1289 if (col > max_line_len - 2*glyph_width('o'))
1290 col = max_line_len - 2*glyph_width('o');
1292 return col;
1295 /* settings helper functions */
1297 static bool encoding_setting(void)
1299 static const struct opt_items names[] = {
1300 {"ISO-8859-1", -1},
1301 {"ISO-8859-7", -1},
1302 {"ISO-8859-8", -1},
1303 {"CP1251", -1},
1304 {"ISO-8859-11", -1},
1305 {"ISO-8859-6", -1},
1306 {"ISO-8859-9", -1},
1307 {"ISO-8859-2", -1},
1308 {"CP1250", -1},
1309 {"SJIS", -1},
1310 {"GB-2312", -1},
1311 {"KSX-1001", -1},
1312 {"BIG5", -1},
1313 {"UTF-8", -1},
1316 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1317 sizeof(names) / sizeof(names[0]), NULL);
1320 static bool word_wrap_setting(void)
1322 static const struct opt_items names[] = {
1323 {"On", -1},
1324 {"Off (Chop Words)", -1},
1327 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1328 names, 2, NULL);
1331 static bool line_mode_setting(void)
1333 static const struct opt_items names[] = {
1334 {"Normal", -1},
1335 {"Join Lines", -1},
1336 {"Expand Lines", -1},
1337 #ifdef HAVE_LCD_BITMAP
1338 {"Reflow Lines", -1},
1339 #endif
1342 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1343 sizeof(names) / sizeof(names[0]), NULL);
1346 static bool view_mode_setting(void)
1348 static const struct opt_items names[] = {
1349 {"No (Narrow)", -1},
1350 {"Yes", -1},
1352 bool ret;
1353 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1354 names , 2, NULL);
1355 if (prefs.view_mode == NARROW)
1356 col = 0;
1357 return ret;
1360 static bool scroll_mode_setting(void)
1362 static const struct opt_items names[] = {
1363 {"Scroll by Page", -1},
1364 {"Scroll by Line", -1},
1367 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1368 names, 2, NULL);
1371 #ifdef HAVE_LCD_BITMAP
1372 static bool page_mode_setting(void)
1374 static const struct opt_items names[] = {
1375 {"No", -1},
1376 {"Yes", -1},
1379 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1380 names, 2, NULL);
1383 static bool scrollbar_setting(void)
1385 static const struct opt_items names[] = {
1386 {"Off", -1},
1387 {"On", -1}
1390 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1391 names, 2, NULL);
1393 #endif
1395 static bool autoscroll_speed_setting(void)
1397 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1398 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1401 static bool viewer_options_menu(void)
1403 int m;
1404 bool result;
1406 static const struct menu_item items[] = {
1407 {"Encoding", encoding_setting },
1408 {"Word Wrap", word_wrap_setting },
1409 {"Line Mode", line_mode_setting },
1410 {"Wide View", view_mode_setting },
1411 #ifdef HAVE_LCD_BITMAP
1412 {"Show Scrollbar", scrollbar_setting },
1413 {"Overlap Pages", page_mode_setting },
1414 #endif
1415 {"Scroll Mode", scroll_mode_setting},
1416 {"Auto-Scroll Speed", autoscroll_speed_setting },
1418 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1419 NULL, NULL, NULL, NULL);
1421 result = menu_run(m);
1422 menu_exit(m);
1423 #ifdef HAVE_LCD_BITMAP
1424 rb->lcd_setmargins(0,0);
1426 /* Show-scrollbar mode for current view-width mode */
1427 init_need_scrollbar();
1428 #endif
1429 return result;
1432 static void viewer_menu(void)
1434 int m;
1435 int result;
1436 static const struct menu_item items[] = {
1437 {"Quit", NULL },
1438 {"Viewer Options", NULL },
1439 {"Show Playback Menu", NULL },
1440 {"Return", NULL },
1443 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1444 result=menu_show(m);
1445 switch (result)
1447 case 0: /* quit */
1448 menu_exit(m);
1449 viewer_exit(NULL);
1450 done = true;
1451 break;
1452 case 1: /* change settings */
1453 done = viewer_options_menu();
1454 break;
1455 case 2: /* playback control */
1456 playback_control(rb, NULL);
1457 break;
1458 case 3: /* return */
1459 break;
1461 menu_exit(m);
1462 #ifdef HAVE_LCD_BITMAP
1463 rb->lcd_setmargins(0,0);
1464 #endif
1465 viewer_draw(col);
1468 enum plugin_status plugin_start(const struct plugin_api* api, const void* file)
1470 int button, i, ok;
1471 int lastbutton = BUTTON_NONE;
1472 bool autoscroll = false;
1473 long old_tick;
1475 rb = api;
1476 old_tick = *rb->current_tick;
1478 /* get the plugin buffer */
1479 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1481 if (!file)
1482 return PLUGIN_ERROR;
1484 file_name = file;
1485 ok = viewer_init();
1486 if (!ok) {
1487 rb->splash(HZ, "Error opening file.");
1488 return PLUGIN_ERROR;
1491 viewer_load_settings(); /* load the preferences and bookmark */
1493 #if LCD_DEPTH > 1
1494 rb->lcd_set_backdrop(NULL);
1495 #endif
1497 viewer_draw(col);
1499 while (!done) {
1501 if(autoscroll)
1503 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1505 viewer_scroll_down();
1506 viewer_draw(col);
1507 old_tick = *rb->current_tick;
1511 button = rb->button_get_w_tmo(HZ/10);
1512 switch (button) {
1513 case VIEWER_MENU:
1514 viewer_menu();
1515 break;
1517 case VIEWER_AUTOSCROLL:
1518 #ifdef VIEWER_AUTOSCROLL_PRE
1519 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1520 break;
1521 #endif
1522 autoscroll = !autoscroll;
1523 break;
1525 case VIEWER_PAGE_UP:
1526 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1527 if (prefs.scroll_mode == PAGE)
1529 /* Page up */
1530 #ifdef HAVE_LCD_BITMAP
1531 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1532 #else
1533 for (i = 0; i < display_lines; i++)
1534 #endif
1535 viewer_scroll_up();
1537 else
1538 viewer_scroll_up();
1539 old_tick = *rb->current_tick;
1540 viewer_draw(col);
1541 break;
1543 case VIEWER_PAGE_DOWN:
1544 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1545 if (prefs.scroll_mode == PAGE)
1547 /* Page down */
1548 if (next_screen_ptr != NULL)
1549 screen_top_ptr = next_screen_to_draw_ptr;
1551 else
1552 viewer_scroll_down();
1553 old_tick = *rb->current_tick;
1554 viewer_draw(col);
1555 break;
1557 case VIEWER_SCREEN_LEFT:
1558 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1559 if (prefs.view_mode == WIDE) {
1560 /* Screen left */
1561 col -= draw_columns;
1562 col = col_limit(col);
1564 else { /* prefs.view_mode == NARROW */
1565 /* Top of file */
1566 viewer_top();
1569 viewer_draw(col);
1570 break;
1572 case VIEWER_SCREEN_RIGHT:
1573 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1574 if (prefs.view_mode == WIDE) {
1575 /* Screen right */
1576 col += draw_columns;
1577 col = col_limit(col);
1579 else { /* prefs.view_mode == NARROW */
1580 /* Bottom of file */
1581 viewer_bottom();
1584 viewer_draw(col);
1585 break;
1587 #ifdef VIEWER_LINE_UP
1588 case VIEWER_LINE_UP:
1589 case VIEWER_LINE_UP | BUTTON_REPEAT:
1590 /* Scroll up one line */
1591 viewer_scroll_up();
1592 old_tick = *rb->current_tick;
1593 viewer_draw(col);
1594 break;
1596 case VIEWER_LINE_DOWN:
1597 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1598 /* Scroll down one line */
1599 if (next_screen_ptr != NULL)
1600 screen_top_ptr = next_line_ptr;
1601 old_tick = *rb->current_tick;
1602 viewer_draw(col);
1603 break;
1604 #endif
1605 #ifdef VIEWER_COLUMN_LEFT
1606 case VIEWER_COLUMN_LEFT:
1607 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1608 if (prefs.view_mode == WIDE) {
1609 /* Scroll left one column */
1610 col -= glyph_width('o');
1611 col = col_limit(col);
1612 viewer_draw(col);
1614 break;
1616 case VIEWER_COLUMN_RIGHT:
1617 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1618 if (prefs.view_mode == WIDE) {
1619 /* Scroll right one column */
1620 col += glyph_width('o');
1621 col = col_limit(col);
1622 viewer_draw(col);
1624 break;
1625 #endif
1627 #ifdef VIEWER_RC_QUIT
1628 case VIEWER_RC_QUIT:
1629 #endif
1630 case VIEWER_QUIT:
1631 viewer_exit(NULL);
1632 done = true;
1633 break;
1635 default:
1636 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1637 == SYS_USB_CONNECTED)
1638 return PLUGIN_USB_CONNECTED;
1639 break;
1641 if (button != BUTTON_NONE)
1643 lastbutton = button;
1644 rb->yield();
1647 return PLUGIN_OK;