Colour targets: Revert an optimisation from almost 18 months ago that actually turned...
[Rockbox.git] / apps / plugins / viewer.c
blob7005745e01c172807c4d3ea48b29dfec44abaf08
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "plugin.h"
23 #include <ctype.h>
24 #include "playback_control.h"
25 #include "oldmenuapi.h"
27 PLUGIN_HEADER
29 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
30 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
32 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
33 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
34 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
35 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
36 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
37 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
38 #define TOP_SECTOR buffer
39 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
40 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
41 #define SCROLLBAR_WIDTH 6
43 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
45 /* Out-Of-Bounds test for any pointer to data in the buffer */
46 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
48 /* Does the buffer contain the beginning of the file? */
49 #define BUFFER_BOF() (file_pos==0)
51 /* Does the buffer contain the end of the file? */
52 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
54 /* Formula for the endpoint address outside of buffer data */
55 #define BUFFER_END() \
56 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
58 /* Is the entire file being shown in one screen? */
59 #define ONE_SCREEN_FITS_ALL() \
60 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
62 /* Is a scrollbar called for on the current screen? */
63 #define NEED_SCROLLBAR() \
64 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
66 /* variable button definitions */
68 /* Recorder keys */
69 #if CONFIG_KEYPAD == RECORDER_PAD
70 #define VIEWER_QUIT BUTTON_OFF
71 #define VIEWER_PAGE_UP BUTTON_UP
72 #define VIEWER_PAGE_DOWN BUTTON_DOWN
73 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
74 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
75 #define VIEWER_MENU BUTTON_F1
76 #define VIEWER_AUTOSCROLL BUTTON_PLAY
77 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
78 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
79 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
80 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
82 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
83 #define VIEWER_QUIT BUTTON_OFF
84 #define VIEWER_PAGE_UP BUTTON_UP
85 #define VIEWER_PAGE_DOWN BUTTON_DOWN
86 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
87 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
88 #define VIEWER_MENU BUTTON_F1
89 #define VIEWER_AUTOSCROLL BUTTON_SELECT
90 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
91 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
92 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
93 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
95 /* Ondio keys */
96 #elif CONFIG_KEYPAD == ONDIO_PAD
97 #define VIEWER_QUIT BUTTON_OFF
98 #define VIEWER_PAGE_UP BUTTON_UP
99 #define VIEWER_PAGE_DOWN BUTTON_DOWN
100 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
101 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
102 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
103 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
104 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
106 /* Player keys */
107 #elif CONFIG_KEYPAD == PLAYER_PAD
108 #define VIEWER_QUIT BUTTON_STOP
109 #define VIEWER_PAGE_UP BUTTON_LEFT
110 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
111 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
112 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
113 #define VIEWER_MENU BUTTON_MENU
114 #define VIEWER_AUTOSCROLL BUTTON_PLAY
116 /* iRiver H1x0 && H3x0 keys */
117 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
118 (CONFIG_KEYPAD == IRIVER_H300_PAD)
119 #define VIEWER_QUIT BUTTON_OFF
120 #define VIEWER_PAGE_UP BUTTON_UP
121 #define VIEWER_PAGE_DOWN BUTTON_DOWN
122 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
123 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
124 #define VIEWER_MENU BUTTON_MODE
125 #define VIEWER_AUTOSCROLL BUTTON_SELECT
126 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
127 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
128 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
129 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
131 #define VIEWER_RC_QUIT BUTTON_RC_STOP
133 /* iPods */
134 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
135 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
136 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
137 #define VIEWER_QUIT_PRE BUTTON_SELECT
138 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
139 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
140 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
141 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
142 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
143 #define VIEWER_MENU BUTTON_MENU
144 #define VIEWER_AUTOSCROLL BUTTON_PLAY
146 /* iFP7xx keys */
147 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
148 #define VIEWER_QUIT BUTTON_PLAY
149 #define VIEWER_PAGE_UP BUTTON_UP
150 #define VIEWER_PAGE_DOWN BUTTON_DOWN
151 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
152 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
153 #define VIEWER_MENU BUTTON_MODE
154 #define VIEWER_AUTOSCROLL BUTTON_SELECT
156 /* iAudio X5 keys */
157 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
158 #define VIEWER_QUIT BUTTON_POWER
159 #define VIEWER_PAGE_UP BUTTON_UP
160 #define VIEWER_PAGE_DOWN BUTTON_DOWN
161 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
162 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
163 #define VIEWER_MENU BUTTON_SELECT
164 #define VIEWER_AUTOSCROLL BUTTON_PLAY
166 /* GIGABEAT keys */
167 #elif CONFIG_KEYPAD == GIGABEAT_PAD
168 #define VIEWER_QUIT BUTTON_POWER
169 #define VIEWER_PAGE_UP BUTTON_UP
170 #define VIEWER_PAGE_DOWN BUTTON_DOWN
171 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
172 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
173 #define VIEWER_MENU BUTTON_MENU
174 #define VIEWER_AUTOSCROLL BUTTON_A
176 /* Sansa E200 keys */
177 #elif CONFIG_KEYPAD == SANSA_E200_PAD
178 #define VIEWER_QUIT BUTTON_POWER
179 #define VIEWER_PAGE_UP BUTTON_UP
180 #define VIEWER_PAGE_DOWN BUTTON_DOWN
181 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
182 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
183 #define VIEWER_MENU BUTTON_SELECT
184 #define VIEWER_AUTOSCROLL BUTTON_REC
185 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
186 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
188 /* Sansa C200 keys */
189 #elif CONFIG_KEYPAD == SANSA_C200_PAD
190 #define VIEWER_QUIT BUTTON_POWER
191 #define VIEWER_PAGE_UP BUTTON_VOL_UP
192 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
193 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
194 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
195 #define VIEWER_MENU BUTTON_SELECT
196 #define VIEWER_AUTOSCROLL BUTTON_REC
197 #define VIEWER_LINE_UP BUTTON_UP
198 #define VIEWER_LINE_DOWN BUTTON_DOWN
200 /* iriver H10 keys */
201 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
202 #define VIEWER_QUIT BUTTON_POWER
203 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
204 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
205 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
206 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
207 #define VIEWER_MENU BUTTON_REW
208 #define VIEWER_AUTOSCROLL BUTTON_PLAY
210 /*M-Robe 500 keys */
211 #elif CONFIG_KEYPAD == MROBE500_PAD
212 #define VIEWER_QUIT BUTTON_POWER
213 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
214 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
215 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
216 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
217 #define VIEWER_MENU BUTTON_RC_HEART
218 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
220 /*Gigabeat S keys */
221 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
222 #define VIEWER_QUIT BUTTON_BACK
223 #define VIEWER_PAGE_UP BUTTON_PREV
224 #define VIEWER_PAGE_DOWN BUTTON_NEXT
225 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
226 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
227 #define VIEWER_MENU BUTTON_MENU
228 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
229 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
230 #define VIEWER_LINE_UP BUTTON_UP
231 #define VIEWER_LINE_DOWN BUTTON_DOWN
232 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
233 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
235 /*M-Robe 100 keys */
236 #elif CONFIG_KEYPAD == MROBE100_PAD
237 #define VIEWER_QUIT BUTTON_POWER
238 #define VIEWER_PAGE_UP BUTTON_UP
239 #define VIEWER_PAGE_DOWN BUTTON_DOWN
240 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
241 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
242 #define VIEWER_MENU BUTTON_MENU
243 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
245 /* iAUdio M3 keys */
246 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
247 #define VIEWER_QUIT BUTTON_RC_REC
248 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
249 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
250 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
251 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
252 #define VIEWER_MENU BUTTON_RC_MENU
253 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
254 #define VIEWER_RC_QUIT BUTTON_REC
256 /* Cowon D2 keys */
257 #elif CONFIG_KEYPAD == COWOND2_PAD
258 #define VIEWER_QUIT BUTTON_POWER
259 #define VIEWER_MENU BUTTON_MENU
261 #else
262 #error No keymap defined!
263 #endif
265 #ifdef HAVE_TOUCHPAD
266 #ifndef VIEWER_QUIT
267 #define VIEWER_QUIT BUTTON_TOPLEFT
268 #endif
269 #ifndef VIEWER_PAGE_UP
270 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
271 #endif
272 #ifndef VIEWER_PAGE_DOWN
273 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
274 #endif
275 #ifndef VIEWER_SCREEN_LEFT
276 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
277 #endif
278 #ifndef VIEWER_SCREEN_RIGHT
279 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
280 #endif
281 #ifndef VIEWER_MENU
282 #define VIEWER_MENU BUTTON_TOPRIGHT
283 #endif
284 #ifndef VIEWER_AUTOSCROLL
285 #define VIEWER_AUTOSCROLL BUTTON_CENTER
286 #endif
287 #endif
289 /* stuff for the bookmarking */
290 struct bookmarked_file_info {
291 long file_position;
292 int top_ptr_pos;
293 char filename[MAX_PATH];
296 struct bookmark_file_data {
297 signed int bookmarked_files_count;
298 struct bookmarked_file_info bookmarks[];
301 struct preferences {
302 enum {
303 WRAP=0,
304 CHOP,
305 } word_mode;
307 enum {
308 NORMAL=0,
309 JOIN,
310 EXPAND,
311 REFLOW, /* won't be set on charcell LCD, must be last */
312 } line_mode;
314 enum {
315 NARROW=0,
316 WIDE,
317 } view_mode;
319 enum {
320 ISO_8859_1=0,
321 ISO_8859_7,
322 ISO_8859_8,
323 CP1251,
324 ISO_8859_11,
325 ISO_8859_6,
326 ISO_8859_9,
327 ISO_8859_2,
328 CP1250,
329 SJIS,
330 GB2312,
331 KSX1001,
332 BIG5,
333 UTF8,
334 ENCODINGS
335 } encoding; /* FIXME: What should default encoding be? */
337 #ifdef HAVE_LCD_BITMAP
338 enum {
339 SB_OFF=0,
340 SB_ON,
341 } scrollbar_mode;
342 bool need_scrollbar;
344 enum {
345 NO_OVERLAP=0,
346 OVERLAP,
347 } page_mode;
348 #endif /* HAVE_LCD_BITMAP */
350 enum {
351 PAGE=0,
352 LINE,
353 } scroll_mode;
355 int autoscroll_speed;
359 struct preferences prefs;
360 struct preferences old_prefs;
362 static unsigned char *buffer;
363 static long buffer_size;
364 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
365 static int display_columns; /* number of (pixel) columns on the display */
366 static int display_lines; /* number of lines on the display */
367 static int draw_columns; /* number of (pixel) columns available for text */
368 static int par_indent_spaces; /* number of spaces to indent first paragraph */
369 static int fd;
370 static const char *file_name;
371 static long file_size;
372 static long start_position; /* position in the file after the viewer is started */
373 static bool mac_text;
374 static long file_pos; /* Position of the top of the buffer in the file */
375 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
376 static int max_line_len;
377 static unsigned char *screen_top_ptr;
378 static unsigned char *next_screen_ptr;
379 static unsigned char *next_screen_to_draw_ptr;
380 static unsigned char *next_line_ptr;
381 static const struct plugin_api* rb;
382 #ifdef HAVE_LCD_BITMAP
383 static struct font *pf;
384 #endif
387 int glyph_width(int ch)
389 if (ch == 0)
390 ch = ' ';
392 #ifdef HAVE_LCD_BITMAP
393 return rb->font_get_width(pf, ch);
394 #else
395 return 1;
396 #endif
399 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
401 unsigned char utf8_tmp[6];
402 int count;
404 if (prefs.encoding == UTF8)
405 return (unsigned char*)rb->utf8decode(str, ch);
407 count = BUFFER_OOB(str+2)? 1:2;
408 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
409 rb->utf8decode(utf8_tmp, ch);
411 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
412 return (unsigned char*)str+1;
413 else
414 return (unsigned char*)str+2;
417 bool done = false;
418 int col = 0;
420 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
421 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
422 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
423 static unsigned char* crop_at_width(const unsigned char* p)
425 int k,width;
426 unsigned short ch;
427 const unsigned char *oldp = p;
429 k=width=0;
431 while (LINE_IS_NOT_FULL) {
432 oldp = p;
433 p = get_ucs(p, &ch);
434 ADVANCE_COUNTERS(ch);
437 return (unsigned char*)oldp;
440 static unsigned char* find_first_feed(const unsigned char* p, int size)
442 int i;
444 for (i=0; i < size; i++)
445 if (p[i] == 0)
446 return (unsigned char*) p+i;
448 return NULL;
451 static unsigned char* find_last_feed(const unsigned char* p, int size)
453 int i;
455 for (i=size-1; i>=0; i--)
456 if (p[i] == 0)
457 return (unsigned char*) p+i;
459 return NULL;
462 static unsigned char* find_last_space(const unsigned char* p, int size)
464 int i, j, k;
466 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
468 if (!BUFFER_OOB(&p[size]))
469 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
470 if (p[size] == line_break[j])
471 return (unsigned char*) p+size;
473 for (i=size-1; i>=0; i--)
474 for (j=k; j < (int) sizeof(line_break); j++)
476 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
477 if (p[i] == line_break[j])
478 return (unsigned char*) p+i;
481 return NULL;
484 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
486 const unsigned char *next_line = NULL;
487 int size, i, j, k, width, search_len, spaces, newlines;
488 bool first_chars;
489 unsigned char c;
491 if (is_short != NULL)
492 *is_short = true;
494 if BUFFER_OOB(cur_line)
495 return NULL;
497 if (prefs.view_mode == WIDE) {
498 search_len = MAX_WIDTH;
500 else { /* prefs.view_mode == NARROW */
501 search_len = crop_at_width(cur_line) - cur_line;
504 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
506 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
507 /* Need to scan ahead and possibly increase search_len and size,
508 or possibly set next_line at second hard return in a row. */
509 next_line = NULL;
510 first_chars=true;
511 for (j=k=width=spaces=newlines=0; ; j++) {
512 if (BUFFER_OOB(cur_line+j))
513 return NULL;
514 if (LINE_IS_FULL) {
515 size = search_len = j;
516 break;
519 c = cur_line[j];
520 switch (c) {
521 case ' ':
522 if (prefs.line_mode == REFLOW) {
523 if (newlines > 0) {
524 size = j;
525 next_line = cur_line + size;
526 return (unsigned char*) next_line;
528 if (j==0) /* i=1 is intentional */
529 for (i=0; i<par_indent_spaces; i++)
530 ADVANCE_COUNTERS(' ');
532 if (!first_chars) spaces++;
533 break;
535 case 0:
536 if (newlines > 0) {
537 size = j;
538 next_line = cur_line + size - spaces;
539 if (next_line != cur_line)
540 return (unsigned char*) next_line;
541 break;
544 newlines++;
545 size += spaces -1;
546 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
547 return NULL;
548 search_len = size;
549 spaces = first_chars? 0:1;
550 break;
552 default:
553 if (prefs.line_mode==JOIN || newlines>0) {
554 while (spaces) {
555 spaces--;
556 ADVANCE_COUNTERS(' ');
557 if (LINE_IS_FULL) {
558 size = search_len = j;
559 break;
562 newlines=0;
563 } else if (spaces) {
564 /* REFLOW, multiple spaces between words: count only
565 * one. If more are needed, they will be added
566 * while drawing. */
567 search_len = size;
568 spaces=0;
569 ADVANCE_COUNTERS(' ');
570 if (LINE_IS_FULL) {
571 size = search_len = j;
572 break;
575 first_chars = false;
576 ADVANCE_COUNTERS(c);
577 break;
581 else {
582 /* find first hard return */
583 next_line = find_first_feed(cur_line, size);
586 if (next_line == NULL)
587 if (size == search_len) {
588 if (prefs.word_mode == WRAP) /* Find last space */
589 next_line = find_last_space(cur_line, size);
591 if (next_line == NULL)
592 next_line = crop_at_width(cur_line);
593 else
594 if (prefs.word_mode == WRAP)
595 for (i=0;
596 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
597 i++)
598 next_line++;
601 if (prefs.line_mode == EXPAND)
602 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
603 if (next_line[0] == 0)
604 if (next_line != cur_line)
605 return (unsigned char*) next_line;
607 /* If next_line is pointing to a zero, increment it; i.e.,
608 leave the terminator at the end of cur_line. If pointing
609 to a hyphen, increment only if there is room to display
610 the hyphen on current line (won't apply in WIDE mode,
611 since it's guarenteed there won't be room). */
612 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
613 if (next_line[0] == 0)/* ||
614 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
615 next_line++;
617 if (BUFFER_OOB(next_line))
618 return NULL;
620 if (is_short)
621 *is_short = false;
623 return (unsigned char*) next_line;
626 static unsigned char* find_prev_line(const unsigned char* cur_line)
628 const unsigned char *prev_line = NULL;
629 const unsigned char *p;
631 if BUFFER_OOB(cur_line)
632 return NULL;
634 /* To wrap consistently at the same places, we must
635 start with a known hard return, then work downwards.
636 We can either search backwards for a hard return,
637 or simply start wrapping downwards from top of buffer.
638 If current line is not near top of buffer, this is
639 a file with long lines (paragraphs). We would need to
640 read earlier sectors before we could decide how to
641 properly wrap the lines above the current line, but
642 it probably is not worth the disk access. Instead,
643 start with top of buffer and wrap down from there.
644 This may result in some lines wrapping at different
645 points from where they wrap when scrolling down.
646 If buffer is at top of file, start at top of buffer. */
648 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
649 prev_line = p = NULL;
650 else
651 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
652 /* Null means no line feeds in buffer above current line. */
654 if (prev_line == NULL)
655 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
656 prev_line = p = buffer;
657 /* (else return NULL and read previous block) */
659 /* Wrap downwards until too far, then use the one before. */
660 while (p < cur_line && p != NULL) {
661 prev_line = p;
662 p = find_next_line(prev_line, NULL);
665 if (BUFFER_OOB(prev_line))
666 return NULL;
668 return (unsigned char*) prev_line;
671 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
673 /* Read from file and preprocess the data */
674 /* To minimize disk access, always read on sector boundaries */
675 unsigned numread, i;
676 bool found_CR = false;
678 rb->lseek(fd, pos, SEEK_SET);
679 numread = rb->read(fd, buf, size);
680 rb->button_clear_queue(); /* clear button queue */
682 for(i = 0; i < numread; i++) {
683 switch(buf[i]) {
684 case '\r':
685 if (mac_text) {
686 buf[i] = 0;
688 else {
689 buf[i] = ' ';
690 found_CR = true;
692 break;
694 case '\n':
695 buf[i] = 0;
696 found_CR = false;
697 break;
699 case 0: /* No break between case 0 and default, intentionally */
700 buf[i] = ' ';
701 default:
702 if (found_CR) {
703 buf[i - 1] = 0;
704 found_CR = false;
705 mac_text = true;
707 break;
712 static int read_and_synch(int direction)
714 /* Read next (or prev) block, and reposition global pointers. */
715 /* direction: 1 for down (i.e., further into file), -1 for up */
716 int move_size, move_vector, offset;
717 unsigned char *fill_buf;
719 if (direction == -1) /* up */ {
720 move_size = SMALL_BLOCK_SIZE;
721 offset = 0;
722 fill_buf = TOP_SECTOR;
723 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
724 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
726 else /* down */ {
727 if (prefs.view_mode == WIDE) {
728 /* WIDE mode needs more buffer so we have to read smaller blocks */
729 move_size = SMALL_BLOCK_SIZE;
730 offset = LARGE_BLOCK_SIZE;
731 fill_buf = BOTTOM_SECTOR;
732 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
733 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
735 else {
736 move_size = LARGE_BLOCK_SIZE;
737 offset = SMALL_BLOCK_SIZE;
738 fill_buf = MID_SECTOR;
739 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
742 move_vector = direction * move_size;
743 screen_top_ptr -= move_vector;
744 file_pos += move_vector;
745 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
746 fill_buffer(file_pos + offset, fill_buf, move_size);
747 return move_vector;
750 static void viewer_scroll_up(void)
752 unsigned char *p;
754 p = find_prev_line(screen_top_ptr);
755 if (p == NULL && !BUFFER_BOF()) {
756 read_and_synch(-1);
757 p = find_prev_line(screen_top_ptr);
759 if (p != NULL)
760 screen_top_ptr = p;
763 static void viewer_scroll_down(void)
765 if (next_screen_ptr != NULL)
766 screen_top_ptr = next_line_ptr;
769 #ifdef HAVE_LCD_BITMAP
770 static void viewer_scrollbar(void) {
771 int items, min_shown, max_shown;
773 items = (int) file_size; /* (SH1 int is same as long) */
774 min_shown = (int) file_pos + (screen_top_ptr - buffer);
776 if (next_screen_ptr == NULL)
777 max_shown = items;
778 else
779 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
781 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
782 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
784 #endif
786 static void viewer_draw(int col)
788 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
789 int width, extra_spaces, indent_spaces, spaces_per_word;
790 bool multiple_spacing, line_is_short;
791 unsigned short ch;
792 unsigned char *str, *oldstr;
793 unsigned char *line_begin;
794 unsigned char *line_end;
795 unsigned char c;
796 unsigned char scratch_buffer[MAX_COLUMNS + 1];
797 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
798 unsigned char *endptr;
800 /* If col==-1 do all calculations but don't display */
801 if (col != -1) {
802 #ifdef HAVE_LCD_BITMAP
803 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
804 #else
805 left_col = 0;
806 #endif
807 rb->lcd_clear_display();
809 max_line_len = 0;
810 line_begin = line_end = screen_top_ptr;
812 for (i = 0; i < display_lines; i++) {
813 if (BUFFER_OOB(line_end))
814 break; /* Happens after display last line at BUFFER_EOF() */
816 line_begin = line_end;
817 line_end = find_next_line(line_begin, &line_is_short);
819 if (line_end == NULL) {
820 if (BUFFER_EOF()) {
821 if (i < display_lines - 1 && !BUFFER_BOF()) {
822 if (col != -1)
823 rb->lcd_clear_display();
825 for (; i < display_lines - 1; i++)
826 viewer_scroll_up();
828 line_begin = line_end = screen_top_ptr;
829 i = -1;
830 continue;
832 else {
833 line_end = buffer_end;
836 else {
837 resynch_move = read_and_synch(1); /* Read block & move ptrs */
838 line_begin -= resynch_move;
839 if (i > 0)
840 next_line_ptr -= resynch_move;
842 line_end = find_next_line(line_begin, NULL);
843 if (line_end == NULL) /* Should not really happen */
844 break;
847 line_len = line_end - line_begin;
849 /* calculate line_len */
850 str = oldstr = line_begin;
851 j = -1;
852 while (str < line_end) {
853 oldstr = str;
854 str = crop_at_width(str);
855 j++;
857 line_width = j*draw_columns;
858 while (oldstr < line_end) {
859 oldstr = get_ucs(oldstr, &ch);
860 line_width += glyph_width(ch);
863 if (prefs.line_mode == JOIN) {
864 if (line_begin[0] == 0) {
865 line_begin++;
866 if (prefs.word_mode == CHOP)
867 line_end++;
868 else
869 line_len--;
871 for (j=k=spaces=0; j < line_len; j++) {
872 if (k == MAX_COLUMNS)
873 break;
875 c = line_begin[j];
876 switch (c) {
877 case ' ':
878 spaces++;
879 break;
880 case 0:
881 spaces = 0;
882 scratch_buffer[k++] = ' ';
883 break;
884 default:
885 while (spaces) {
886 spaces--;
887 scratch_buffer[k++] = ' ';
888 if (k == MAX_COLUMNS - 1)
889 break;
891 scratch_buffer[k++] = c;
892 break;
895 if (col != -1) {
896 scratch_buffer[k] = 0;
897 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
898 prefs.encoding, draw_columns/glyph_width('i'));
899 *endptr = 0;
902 else if (prefs.line_mode == REFLOW) {
903 if (line_begin[0] == 0) {
904 line_begin++;
905 if (prefs.word_mode == CHOP)
906 line_end++;
907 else
908 line_len--;
911 indent_spaces = 0;
912 if (!line_is_short) {
913 multiple_spacing = false;
914 width=spaces=0;
915 for (str = line_begin; str < line_end; ) {
916 str = get_ucs(str, &ch);
917 switch (ch) {
918 case ' ':
919 case 0:
920 if ((str == line_begin) && (prefs.word_mode==WRAP))
921 /* special case: indent the paragraph,
922 * don't count spaces */
923 indent_spaces = par_indent_spaces;
924 else if (!multiple_spacing)
925 spaces++;
926 multiple_spacing = true;
927 break;
928 default:
929 multiple_spacing = false;
930 width += glyph_width(ch);
931 break;
934 if (multiple_spacing) spaces--;
936 if (spaces) {
937 /* total number of spaces to insert between words */
938 extra_spaces = (draw_columns-width)/glyph_width(' ')
939 - indent_spaces;
940 /* number of spaces between each word*/
941 spaces_per_word = extra_spaces / spaces;
942 /* number of words with n+1 spaces (to fill up) */
943 extra_spaces = extra_spaces % spaces;
944 if (spaces_per_word > 2) { /* too much spacing is awful */
945 spaces_per_word = 3;
946 extra_spaces = 0;
948 } else { /* this doesn't matter much... no spaces anyway */
949 spaces_per_word = extra_spaces = 0;
951 } else { /* end of a paragraph: don't fill line */
952 spaces_per_word = 1;
953 extra_spaces = 0;
956 multiple_spacing = false;
957 for (j=k=spaces=0; j < line_len; j++) {
958 if (k == MAX_COLUMNS)
959 break;
961 c = line_begin[j];
962 switch (c) {
963 case ' ':
964 case 0:
965 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
966 for (j=0; j<par_indent_spaces; j++)
967 scratch_buffer[k++] = ' ';
968 j=0;
970 else if (!multiple_spacing) {
971 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
972 scratch_buffer[k++] = ' ';
973 spaces++;
975 multiple_spacing = true;
976 break;
977 default:
978 scratch_buffer[k++] = c;
979 multiple_spacing = false;
980 break;
984 if (col != -1) {
985 scratch_buffer[k] = 0;
986 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
987 prefs.encoding, k-col);
988 *endptr = 0;
991 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
992 if (col != -1)
993 if (line_width > col) {
994 str = oldstr = line_begin;
995 k = col;
996 width = 0;
997 while( (width<draw_columns) && (oldstr<line_end) )
999 oldstr = get_ucs(oldstr, &ch);
1000 if (k > 0) {
1001 k -= glyph_width(ch);
1002 line_begin = oldstr;
1003 } else {
1004 width += glyph_width(ch);
1008 if(prefs.view_mode==WIDE)
1009 endptr = rb->iso_decode(line_begin, utf8_buffer,
1010 prefs.encoding, oldstr-line_begin);
1011 else
1012 endptr = rb->iso_decode(line_begin, utf8_buffer,
1013 prefs.encoding, line_end-line_begin);
1014 *endptr = 0;
1017 if (col != -1 && line_width > col)
1018 #ifdef HAVE_LCD_BITMAP
1019 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1020 #else
1021 rb->lcd_puts(left_col, i, utf8_buffer);
1022 #endif
1023 if (line_width > max_line_len)
1024 max_line_len = line_width;
1026 if (i == 0)
1027 next_line_ptr = line_end;
1029 next_screen_ptr = line_end;
1030 if (BUFFER_OOB(next_screen_ptr))
1031 next_screen_ptr = NULL;
1033 #ifdef HAVE_LCD_BITMAP
1034 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1036 if (prefs.need_scrollbar)
1037 viewer_scrollbar();
1038 #else
1039 next_screen_to_draw_ptr = next_screen_ptr;
1040 #endif
1042 if (col != -1)
1043 rb->lcd_update();
1046 static void viewer_top(void)
1048 /* Read top of file into buffer
1049 and point screen pointer to top */
1050 if (file_pos != 0)
1052 file_pos = 0;
1053 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1054 fill_buffer(0, buffer, buffer_size);
1057 screen_top_ptr = buffer;
1060 static void viewer_bottom(void)
1062 /* Read bottom of file into buffer
1063 and point screen pointer to bottom */
1064 long last_sectors;
1066 if (file_size > buffer_size) {
1067 /* Find last buffer in file, round up to next sector boundary */
1068 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1069 last_sectors /= SMALL_BLOCK_SIZE;
1070 last_sectors *= SMALL_BLOCK_SIZE;
1072 else {
1073 last_sectors = 0;
1076 if (file_pos != last_sectors)
1078 file_pos = last_sectors;
1079 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1080 fill_buffer(last_sectors, buffer, buffer_size);
1083 screen_top_ptr = buffer_end-1;
1086 #ifdef HAVE_LCD_BITMAP
1087 static void init_need_scrollbar(void) {
1088 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1089 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1090 viewer_draw(-1);
1091 prefs.need_scrollbar = NEED_SCROLLBAR();
1092 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1093 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1095 #else
1096 #define init_need_scrollbar()
1097 #endif
1099 static bool viewer_init(void)
1101 #ifdef HAVE_LCD_BITMAP
1103 pf = rb->font_get(FONT_UI);
1105 display_lines = LCD_HEIGHT / pf->height;
1106 draw_columns = display_columns = LCD_WIDTH;
1107 #else
1108 /* REAL fixed pitch :) all chars use up 1 cell */
1109 display_lines = 2;
1110 draw_columns = display_columns = 11;
1111 par_indent_spaces = 2;
1112 #endif
1114 fd = rb->open(file_name, O_RDONLY);
1115 if (fd==-1)
1116 return false;
1118 file_size = rb->filesize(fd);
1119 if (file_size==-1)
1120 return false;
1122 /* Init mac_text value used in processing buffer */
1123 mac_text = false;
1125 return true;
1128 static void viewer_default_settings(void)
1130 prefs.word_mode = WRAP;
1131 prefs.line_mode = NORMAL;
1132 prefs.view_mode = NARROW;
1133 prefs.scroll_mode = PAGE;
1134 #ifdef HAVE_LCD_BITMAP
1135 prefs.page_mode = NO_OVERLAP;
1136 prefs.scrollbar_mode = SB_OFF;
1137 #endif
1138 prefs.autoscroll_speed = 1;
1139 /* Set codepage to system default */
1140 prefs.encoding = rb->global_settings->default_codepage;
1143 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1145 int settings_fd, i;
1146 struct bookmark_file_data *data;
1147 struct bookmarked_file_info this_bookmark;
1149 /* read settings file */
1150 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1151 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1153 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1154 rb->close(settings_fd);
1156 else
1158 /* load default settings if there is no settings file */
1159 viewer_default_settings();
1162 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1164 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1165 data->bookmarked_files_count = 0;
1167 /* read bookmarks if file exists */
1168 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1169 if (settings_fd >= 0)
1171 /* figure out how many items to read */
1172 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1173 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1174 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1175 rb->read(settings_fd, data->bookmarks,
1176 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1177 rb->close(settings_fd);
1180 file_pos = 0;
1181 screen_top_ptr = buffer;
1183 /* check if current file is in list */
1184 for (i=0; i < data->bookmarked_files_count; i++)
1186 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1188 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1189 int screen_top = screen_pos % buffer_size;
1190 file_pos = screen_pos - screen_top;
1191 screen_top_ptr = buffer + screen_top;
1192 break;
1196 this_bookmark.file_position = file_pos;
1197 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1199 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1200 rb->strcpy(this_bookmark.filename,file_name);
1202 /* prevent potential slot overflow */
1203 if (i >= data->bookmarked_files_count)
1205 if (i < MAX_BOOKMARKED_FILES)
1206 data->bookmarked_files_count++;
1207 else
1208 i = MAX_BOOKMARKED_FILES-1;
1211 /* write bookmark file with spare slot in first position
1212 to be filled in by viewer_save_settings */
1213 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1214 if (settings_fd >=0 )
1216 /* write count */
1217 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1219 /* write the current bookmark */
1220 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1222 /* write everything that was before this bookmark */
1223 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1225 rb->close(settings_fd);
1228 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1230 if (BUFFER_OOB(screen_top_ptr))
1232 screen_top_ptr = buffer;
1235 fill_buffer(file_pos, buffer, buffer_size);
1237 /* remember the current position */
1238 start_position = file_pos + screen_top_ptr - buffer;
1240 init_need_scrollbar();
1243 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1245 int settings_fd;
1247 /* save the viewer settings if they have been changed */
1248 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1250 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1252 if (settings_fd >= 0 )
1254 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1255 rb->close(settings_fd);
1259 /* save the bookmark if the position has changed */
1260 if (file_pos + screen_top_ptr - buffer != start_position)
1262 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1264 if (settings_fd >= 0 )
1266 struct bookmarked_file_info b;
1267 b.file_position = file_pos + screen_top_ptr - buffer;
1268 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1269 rb->memset(&b.filename[0],0,MAX_PATH);
1270 rb->strcpy(b.filename,file_name);
1271 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1272 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1273 rb->close(settings_fd);
1278 static void viewer_exit(void *parameter)
1280 (void)parameter;
1282 viewer_save_settings();
1283 rb->close(fd);
1286 static int col_limit(int col)
1288 if (col < 0)
1289 col = 0;
1290 else
1291 if (col > max_line_len - 2*glyph_width('o'))
1292 col = max_line_len - 2*glyph_width('o');
1294 return col;
1297 /* settings helper functions */
1299 static bool encoding_setting(void)
1301 static const struct opt_items names[] = {
1302 {"ISO-8859-1", -1},
1303 {"ISO-8859-7", -1},
1304 {"ISO-8859-8", -1},
1305 {"CP1251", -1},
1306 {"ISO-8859-11", -1},
1307 {"ISO-8859-6", -1},
1308 {"ISO-8859-9", -1},
1309 {"ISO-8859-2", -1},
1310 {"CP1250", -1},
1311 {"SJIS", -1},
1312 {"GB-2312", -1},
1313 {"KSX-1001", -1},
1314 {"BIG5", -1},
1315 {"UTF-8", -1},
1318 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1319 sizeof(names) / sizeof(names[0]), NULL);
1322 static bool word_wrap_setting(void)
1324 static const struct opt_items names[] = {
1325 {"On", -1},
1326 {"Off (Chop Words)", -1},
1329 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1330 names, 2, NULL);
1333 static bool line_mode_setting(void)
1335 static const struct opt_items names[] = {
1336 {"Normal", -1},
1337 {"Join Lines", -1},
1338 {"Expand Lines", -1},
1339 #ifdef HAVE_LCD_BITMAP
1340 {"Reflow Lines", -1},
1341 #endif
1344 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1345 sizeof(names) / sizeof(names[0]), NULL);
1348 static bool view_mode_setting(void)
1350 static const struct opt_items names[] = {
1351 {"No (Narrow)", -1},
1352 {"Yes", -1},
1354 bool ret;
1355 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1356 names , 2, NULL);
1357 if (prefs.view_mode == NARROW)
1358 col = 0;
1359 return ret;
1362 static bool scroll_mode_setting(void)
1364 static const struct opt_items names[] = {
1365 {"Scroll by Page", -1},
1366 {"Scroll by Line", -1},
1369 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1370 names, 2, NULL);
1373 #ifdef HAVE_LCD_BITMAP
1374 static bool page_mode_setting(void)
1376 static const struct opt_items names[] = {
1377 {"No", -1},
1378 {"Yes", -1},
1381 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1382 names, 2, NULL);
1385 static bool scrollbar_setting(void)
1387 static const struct opt_items names[] = {
1388 {"Off", -1},
1389 {"On", -1}
1392 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1393 names, 2, NULL);
1395 #endif
1397 static bool autoscroll_speed_setting(void)
1399 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1400 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1403 static bool viewer_options_menu(void)
1405 int m;
1406 bool result;
1408 static const struct menu_item items[] = {
1409 {"Encoding", encoding_setting },
1410 {"Word Wrap", word_wrap_setting },
1411 {"Line Mode", line_mode_setting },
1412 {"Wide View", view_mode_setting },
1413 #ifdef HAVE_LCD_BITMAP
1414 {"Show Scrollbar", scrollbar_setting },
1415 {"Overlap Pages", page_mode_setting },
1416 #endif
1417 {"Scroll Mode", scroll_mode_setting},
1418 {"Auto-Scroll Speed", autoscroll_speed_setting },
1420 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1421 NULL, NULL, NULL, NULL);
1423 result = menu_run(m);
1424 menu_exit(m);
1425 #ifdef HAVE_LCD_BITMAP
1427 /* Show-scrollbar mode for current view-width mode */
1428 init_need_scrollbar();
1429 #endif
1430 return result;
1433 static void viewer_menu(void)
1435 int m;
1436 int result;
1437 static const struct menu_item items[] = {
1438 {"Quit", NULL },
1439 {"Viewer Options", NULL },
1440 {"Show Playback Menu", NULL },
1441 {"Return", NULL },
1444 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1445 result=menu_show(m);
1446 switch (result)
1448 case 0: /* quit */
1449 menu_exit(m);
1450 viewer_exit(NULL);
1451 done = true;
1452 break;
1453 case 1: /* change settings */
1454 done = viewer_options_menu();
1455 break;
1456 case 2: /* playback control */
1457 playback_control(rb, NULL);
1458 break;
1459 case 3: /* return */
1460 break;
1462 menu_exit(m);
1463 viewer_draw(col);
1466 enum plugin_status plugin_start(const struct plugin_api* api, const void* file)
1468 int button, i, ok;
1469 int lastbutton = BUTTON_NONE;
1470 bool autoscroll = false;
1471 long old_tick;
1473 rb = api;
1474 old_tick = *rb->current_tick;
1476 /* get the plugin buffer */
1477 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1479 if (!file)
1480 return PLUGIN_ERROR;
1482 file_name = file;
1483 ok = viewer_init();
1484 if (!ok) {
1485 rb->splash(HZ, "Error opening file.");
1486 return PLUGIN_ERROR;
1489 viewer_load_settings(); /* load the preferences and bookmark */
1491 #if LCD_DEPTH > 1
1492 rb->lcd_set_backdrop(NULL);
1493 #endif
1495 viewer_draw(col);
1497 while (!done) {
1499 if(autoscroll)
1501 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1503 viewer_scroll_down();
1504 viewer_draw(col);
1505 old_tick = *rb->current_tick;
1509 button = rb->button_get_w_tmo(HZ/10);
1510 switch (button) {
1511 case VIEWER_MENU:
1512 viewer_menu();
1513 break;
1515 case VIEWER_AUTOSCROLL:
1516 #ifdef VIEWER_AUTOSCROLL_PRE
1517 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1518 break;
1519 #endif
1520 autoscroll = !autoscroll;
1521 break;
1523 case VIEWER_PAGE_UP:
1524 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1525 if (prefs.scroll_mode == PAGE)
1527 /* Page up */
1528 #ifdef HAVE_LCD_BITMAP
1529 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1530 #else
1531 for (i = 0; i < display_lines; i++)
1532 #endif
1533 viewer_scroll_up();
1535 else
1536 viewer_scroll_up();
1537 old_tick = *rb->current_tick;
1538 viewer_draw(col);
1539 break;
1541 case VIEWER_PAGE_DOWN:
1542 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1543 if (prefs.scroll_mode == PAGE)
1545 /* Page down */
1546 if (next_screen_ptr != NULL)
1547 screen_top_ptr = next_screen_to_draw_ptr;
1549 else
1550 viewer_scroll_down();
1551 old_tick = *rb->current_tick;
1552 viewer_draw(col);
1553 break;
1555 case VIEWER_SCREEN_LEFT:
1556 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1557 if (prefs.view_mode == WIDE) {
1558 /* Screen left */
1559 col -= draw_columns;
1560 col = col_limit(col);
1562 else { /* prefs.view_mode == NARROW */
1563 /* Top of file */
1564 viewer_top();
1567 viewer_draw(col);
1568 break;
1570 case VIEWER_SCREEN_RIGHT:
1571 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1572 if (prefs.view_mode == WIDE) {
1573 /* Screen right */
1574 col += draw_columns;
1575 col = col_limit(col);
1577 else { /* prefs.view_mode == NARROW */
1578 /* Bottom of file */
1579 viewer_bottom();
1582 viewer_draw(col);
1583 break;
1585 #ifdef VIEWER_LINE_UP
1586 case VIEWER_LINE_UP:
1587 case VIEWER_LINE_UP | BUTTON_REPEAT:
1588 /* Scroll up one line */
1589 viewer_scroll_up();
1590 old_tick = *rb->current_tick;
1591 viewer_draw(col);
1592 break;
1594 case VIEWER_LINE_DOWN:
1595 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1596 /* Scroll down one line */
1597 if (next_screen_ptr != NULL)
1598 screen_top_ptr = next_line_ptr;
1599 old_tick = *rb->current_tick;
1600 viewer_draw(col);
1601 break;
1602 #endif
1603 #ifdef VIEWER_COLUMN_LEFT
1604 case VIEWER_COLUMN_LEFT:
1605 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1606 if (prefs.view_mode == WIDE) {
1607 /* Scroll left one column */
1608 col -= glyph_width('o');
1609 col = col_limit(col);
1610 viewer_draw(col);
1612 break;
1614 case VIEWER_COLUMN_RIGHT:
1615 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1616 if (prefs.view_mode == WIDE) {
1617 /* Scroll right one column */
1618 col += glyph_width('o');
1619 col = col_limit(col);
1620 viewer_draw(col);
1622 break;
1623 #endif
1625 #ifdef VIEWER_RC_QUIT
1626 case VIEWER_RC_QUIT:
1627 #endif
1628 case VIEWER_QUIT:
1629 viewer_exit(NULL);
1630 done = true;
1631 break;
1633 default:
1634 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1635 == SYS_USB_CONNECTED)
1636 return PLUGIN_USB_CONNECTED;
1637 break;
1639 if (button != BUTTON_NONE)
1641 lastbutton = button;
1642 rb->yield();
1645 return PLUGIN_OK;