New makefile solution: A single invocation of 'make' to build the entire tree. Fully...
[kugel-rb.git] / apps / plugins / viewer.c
blobe85a979bd597bfc20783684740977b3a075580e9
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 "lib/playback_control.h"
25 #include "lib/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 #elif CONFIG_KEYPAD == IAUDIO67_PAD
262 #define VIEWER_QUIT BUTTON_POWER
263 #define VIEWER_PAGE_UP BUTTON_VOLUP
264 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
265 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
266 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
267 #define VIEWER_MENU BUTTON_MENU
268 #define VIEWER_AUTOSCROLL BUTTON_PLAY
269 #define VIEWER_RC_QUIT BUTTON_STOP
271 #else
272 #error No keymap defined!
273 #endif
275 #ifdef HAVE_TOUCHSCREEN
276 #ifndef VIEWER_QUIT
277 #define VIEWER_QUIT BUTTON_TOPLEFT
278 #endif
279 #ifndef VIEWER_PAGE_UP
280 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
281 #endif
282 #ifndef VIEWER_PAGE_DOWN
283 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
284 #endif
285 #ifndef VIEWER_SCREEN_LEFT
286 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
287 #endif
288 #ifndef VIEWER_SCREEN_RIGHT
289 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
290 #endif
291 #ifndef VIEWER_MENU
292 #define VIEWER_MENU BUTTON_TOPRIGHT
293 #endif
294 #ifndef VIEWER_AUTOSCROLL
295 #define VIEWER_AUTOSCROLL BUTTON_CENTER
296 #endif
297 #endif
299 /* stuff for the bookmarking */
300 struct bookmarked_file_info {
301 long file_position;
302 int top_ptr_pos;
303 char filename[MAX_PATH];
306 struct bookmark_file_data {
307 signed int bookmarked_files_count;
308 struct bookmarked_file_info bookmarks[];
311 struct preferences {
312 enum {
313 WRAP=0,
314 CHOP,
315 } word_mode;
317 enum {
318 NORMAL=0,
319 JOIN,
320 EXPAND,
321 REFLOW, /* won't be set on charcell LCD, must be last */
322 } line_mode;
324 enum {
325 NARROW=0,
326 WIDE,
327 } view_mode;
329 enum {
330 ISO_8859_1=0,
331 ISO_8859_7,
332 ISO_8859_8,
333 CP1251,
334 ISO_8859_11,
335 ISO_8859_6,
336 ISO_8859_9,
337 ISO_8859_2,
338 CP1250,
339 SJIS,
340 GB2312,
341 KSX1001,
342 BIG5,
343 UTF8,
344 ENCODINGS
345 } encoding; /* FIXME: What should default encoding be? */
347 #ifdef HAVE_LCD_BITMAP
348 enum {
349 SB_OFF=0,
350 SB_ON,
351 } scrollbar_mode;
352 bool need_scrollbar;
354 enum {
355 NO_OVERLAP=0,
356 OVERLAP,
357 } page_mode;
358 #endif /* HAVE_LCD_BITMAP */
360 enum {
361 PAGE=0,
362 LINE,
363 } scroll_mode;
365 int autoscroll_speed;
369 struct preferences prefs;
370 struct preferences old_prefs;
372 static unsigned char *buffer;
373 static long buffer_size;
374 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
375 static int display_columns; /* number of (pixel) columns on the display */
376 static int display_lines; /* number of lines on the display */
377 static int draw_columns; /* number of (pixel) columns available for text */
378 static int par_indent_spaces; /* number of spaces to indent first paragraph */
379 static int fd;
380 static const char *file_name;
381 static long file_size;
382 static long start_position; /* position in the file after the viewer is started */
383 static bool mac_text;
384 static long file_pos; /* Position of the top of the buffer in the file */
385 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
386 static int max_line_len;
387 static unsigned char *screen_top_ptr;
388 static unsigned char *next_screen_ptr;
389 static unsigned char *next_screen_to_draw_ptr;
390 static unsigned char *next_line_ptr;
391 static const struct plugin_api* rb;
392 #ifdef HAVE_LCD_BITMAP
393 static struct font *pf;
394 #endif
397 int glyph_width(int ch)
399 if (ch == 0)
400 ch = ' ';
402 #ifdef HAVE_LCD_BITMAP
403 return rb->font_get_width(pf, ch);
404 #else
405 return 1;
406 #endif
409 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
411 unsigned char utf8_tmp[6];
412 int count;
414 if (prefs.encoding == UTF8)
415 return (unsigned char*)rb->utf8decode(str, ch);
417 count = BUFFER_OOB(str+2)? 1:2;
418 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
419 rb->utf8decode(utf8_tmp, ch);
421 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
422 return (unsigned char*)str+1;
423 else
424 return (unsigned char*)str+2;
427 bool done = false;
428 int col = 0;
430 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
431 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
432 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
433 static unsigned char* crop_at_width(const unsigned char* p)
435 int k,width;
436 unsigned short ch;
437 const unsigned char *oldp = p;
439 k=width=0;
441 while (LINE_IS_NOT_FULL) {
442 oldp = p;
443 p = get_ucs(p, &ch);
444 ADVANCE_COUNTERS(ch);
447 return (unsigned char*)oldp;
450 static unsigned char* find_first_feed(const unsigned char* p, int size)
452 int i;
454 for (i=0; i < size; i++)
455 if (p[i] == 0)
456 return (unsigned char*) p+i;
458 return NULL;
461 static unsigned char* find_last_feed(const unsigned char* p, int size)
463 int i;
465 for (i=size-1; i>=0; i--)
466 if (p[i] == 0)
467 return (unsigned char*) p+i;
469 return NULL;
472 static unsigned char* find_last_space(const unsigned char* p, int size)
474 int i, j, k;
476 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
478 if (!BUFFER_OOB(&p[size]))
479 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
480 if (p[size] == line_break[j])
481 return (unsigned char*) p+size;
483 for (i=size-1; i>=0; i--)
484 for (j=k; j < (int) sizeof(line_break); j++)
486 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
487 if (p[i] == line_break[j])
488 return (unsigned char*) p+i;
491 return NULL;
494 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
496 const unsigned char *next_line = NULL;
497 int size, i, j, k, width, search_len, spaces, newlines;
498 bool first_chars;
499 unsigned char c;
501 if (is_short != NULL)
502 *is_short = true;
504 if BUFFER_OOB(cur_line)
505 return NULL;
507 if (prefs.view_mode == WIDE) {
508 search_len = MAX_WIDTH;
510 else { /* prefs.view_mode == NARROW */
511 search_len = crop_at_width(cur_line) - cur_line;
514 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
516 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
517 /* Need to scan ahead and possibly increase search_len and size,
518 or possibly set next_line at second hard return in a row. */
519 next_line = NULL;
520 first_chars=true;
521 for (j=k=width=spaces=newlines=0; ; j++) {
522 if (BUFFER_OOB(cur_line+j))
523 return NULL;
524 if (LINE_IS_FULL) {
525 size = search_len = j;
526 break;
529 c = cur_line[j];
530 switch (c) {
531 case ' ':
532 if (prefs.line_mode == REFLOW) {
533 if (newlines > 0) {
534 size = j;
535 next_line = cur_line + size;
536 return (unsigned char*) next_line;
538 if (j==0) /* i=1 is intentional */
539 for (i=0; i<par_indent_spaces; i++)
540 ADVANCE_COUNTERS(' ');
542 if (!first_chars) spaces++;
543 break;
545 case 0:
546 if (newlines > 0) {
547 size = j;
548 next_line = cur_line + size - spaces;
549 if (next_line != cur_line)
550 return (unsigned char*) next_line;
551 break;
554 newlines++;
555 size += spaces -1;
556 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
557 return NULL;
558 search_len = size;
559 spaces = first_chars? 0:1;
560 break;
562 default:
563 if (prefs.line_mode==JOIN || newlines>0) {
564 while (spaces) {
565 spaces--;
566 ADVANCE_COUNTERS(' ');
567 if (LINE_IS_FULL) {
568 size = search_len = j;
569 break;
572 newlines=0;
573 } else if (spaces) {
574 /* REFLOW, multiple spaces between words: count only
575 * one. If more are needed, they will be added
576 * while drawing. */
577 search_len = size;
578 spaces=0;
579 ADVANCE_COUNTERS(' ');
580 if (LINE_IS_FULL) {
581 size = search_len = j;
582 break;
585 first_chars = false;
586 ADVANCE_COUNTERS(c);
587 break;
591 else {
592 /* find first hard return */
593 next_line = find_first_feed(cur_line, size);
596 if (next_line == NULL)
597 if (size == search_len) {
598 if (prefs.word_mode == WRAP) /* Find last space */
599 next_line = find_last_space(cur_line, size);
601 if (next_line == NULL)
602 next_line = crop_at_width(cur_line);
603 else
604 if (prefs.word_mode == WRAP)
605 for (i=0;
606 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
607 i++)
608 next_line++;
611 if (prefs.line_mode == EXPAND)
612 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
613 if (next_line[0] == 0)
614 if (next_line != cur_line)
615 return (unsigned char*) next_line;
617 /* If next_line is pointing to a zero, increment it; i.e.,
618 leave the terminator at the end of cur_line. If pointing
619 to a hyphen, increment only if there is room to display
620 the hyphen on current line (won't apply in WIDE mode,
621 since it's guarenteed there won't be room). */
622 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
623 if (next_line[0] == 0)/* ||
624 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
625 next_line++;
627 if (BUFFER_OOB(next_line))
628 return NULL;
630 if (is_short)
631 *is_short = false;
633 return (unsigned char*) next_line;
636 static unsigned char* find_prev_line(const unsigned char* cur_line)
638 const unsigned char *prev_line = NULL;
639 const unsigned char *p;
641 if BUFFER_OOB(cur_line)
642 return NULL;
644 /* To wrap consistently at the same places, we must
645 start with a known hard return, then work downwards.
646 We can either search backwards for a hard return,
647 or simply start wrapping downwards from top of buffer.
648 If current line is not near top of buffer, this is
649 a file with long lines (paragraphs). We would need to
650 read earlier sectors before we could decide how to
651 properly wrap the lines above the current line, but
652 it probably is not worth the disk access. Instead,
653 start with top of buffer and wrap down from there.
654 This may result in some lines wrapping at different
655 points from where they wrap when scrolling down.
656 If buffer is at top of file, start at top of buffer. */
658 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
659 prev_line = p = NULL;
660 else
661 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
662 /* Null means no line feeds in buffer above current line. */
664 if (prev_line == NULL)
665 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
666 prev_line = p = buffer;
667 /* (else return NULL and read previous block) */
669 /* Wrap downwards until too far, then use the one before. */
670 while (p < cur_line && p != NULL) {
671 prev_line = p;
672 p = find_next_line(prev_line, NULL);
675 if (BUFFER_OOB(prev_line))
676 return NULL;
678 return (unsigned char*) prev_line;
681 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
683 /* Read from file and preprocess the data */
684 /* To minimize disk access, always read on sector boundaries */
685 unsigned numread, i;
686 bool found_CR = false;
688 rb->lseek(fd, pos, SEEK_SET);
689 numread = rb->read(fd, buf, size);
690 rb->button_clear_queue(); /* clear button queue */
692 for(i = 0; i < numread; i++) {
693 switch(buf[i]) {
694 case '\r':
695 if (mac_text) {
696 buf[i] = 0;
698 else {
699 buf[i] = ' ';
700 found_CR = true;
702 break;
704 case '\n':
705 buf[i] = 0;
706 found_CR = false;
707 break;
709 case 0: /* No break between case 0 and default, intentionally */
710 buf[i] = ' ';
711 default:
712 if (found_CR) {
713 buf[i - 1] = 0;
714 found_CR = false;
715 mac_text = true;
717 break;
722 static int read_and_synch(int direction)
724 /* Read next (or prev) block, and reposition global pointers. */
725 /* direction: 1 for down (i.e., further into file), -1 for up */
726 int move_size, move_vector, offset;
727 unsigned char *fill_buf;
729 if (direction == -1) /* up */ {
730 move_size = SMALL_BLOCK_SIZE;
731 offset = 0;
732 fill_buf = TOP_SECTOR;
733 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
734 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
736 else /* down */ {
737 if (prefs.view_mode == WIDE) {
738 /* WIDE mode needs more buffer so we have to read smaller blocks */
739 move_size = SMALL_BLOCK_SIZE;
740 offset = LARGE_BLOCK_SIZE;
741 fill_buf = BOTTOM_SECTOR;
742 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
743 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
745 else {
746 move_size = LARGE_BLOCK_SIZE;
747 offset = SMALL_BLOCK_SIZE;
748 fill_buf = MID_SECTOR;
749 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
752 move_vector = direction * move_size;
753 screen_top_ptr -= move_vector;
754 file_pos += move_vector;
755 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
756 fill_buffer(file_pos + offset, fill_buf, move_size);
757 return move_vector;
760 static void viewer_scroll_up(void)
762 unsigned char *p;
764 p = find_prev_line(screen_top_ptr);
765 if (p == NULL && !BUFFER_BOF()) {
766 read_and_synch(-1);
767 p = find_prev_line(screen_top_ptr);
769 if (p != NULL)
770 screen_top_ptr = p;
773 static void viewer_scroll_down(void)
775 if (next_screen_ptr != NULL)
776 screen_top_ptr = next_line_ptr;
779 #ifdef HAVE_LCD_BITMAP
780 static void viewer_scrollbar(void) {
781 int items, min_shown, max_shown;
783 items = (int) file_size; /* (SH1 int is same as long) */
784 min_shown = (int) file_pos + (screen_top_ptr - buffer);
786 if (next_screen_ptr == NULL)
787 max_shown = items;
788 else
789 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
791 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
792 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
794 #endif
796 static void viewer_draw(int col)
798 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
799 int width, extra_spaces, indent_spaces, spaces_per_word;
800 bool multiple_spacing, line_is_short;
801 unsigned short ch;
802 unsigned char *str, *oldstr;
803 unsigned char *line_begin;
804 unsigned char *line_end;
805 unsigned char c;
806 unsigned char scratch_buffer[MAX_COLUMNS + 1];
807 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
808 unsigned char *endptr;
810 /* If col==-1 do all calculations but don't display */
811 if (col != -1) {
812 #ifdef HAVE_LCD_BITMAP
813 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
814 #else
815 left_col = 0;
816 #endif
817 rb->lcd_clear_display();
819 max_line_len = 0;
820 line_begin = line_end = screen_top_ptr;
822 for (i = 0; i < display_lines; i++) {
823 if (BUFFER_OOB(line_end))
824 break; /* Happens after display last line at BUFFER_EOF() */
826 line_begin = line_end;
827 line_end = find_next_line(line_begin, &line_is_short);
829 if (line_end == NULL) {
830 if (BUFFER_EOF()) {
831 if (i < display_lines - 1 && !BUFFER_BOF()) {
832 if (col != -1)
833 rb->lcd_clear_display();
835 for (; i < display_lines - 1; i++)
836 viewer_scroll_up();
838 line_begin = line_end = screen_top_ptr;
839 i = -1;
840 continue;
842 else {
843 line_end = buffer_end;
846 else {
847 resynch_move = read_and_synch(1); /* Read block & move ptrs */
848 line_begin -= resynch_move;
849 if (i > 0)
850 next_line_ptr -= resynch_move;
852 line_end = find_next_line(line_begin, NULL);
853 if (line_end == NULL) /* Should not really happen */
854 break;
857 line_len = line_end - line_begin;
859 /* calculate line_len */
860 str = oldstr = line_begin;
861 j = -1;
862 while (str < line_end) {
863 oldstr = str;
864 str = crop_at_width(str);
865 j++;
867 line_width = j*draw_columns;
868 while (oldstr < line_end) {
869 oldstr = get_ucs(oldstr, &ch);
870 line_width += glyph_width(ch);
873 if (prefs.line_mode == JOIN) {
874 if (line_begin[0] == 0) {
875 line_begin++;
876 if (prefs.word_mode == CHOP)
877 line_end++;
878 else
879 line_len--;
881 for (j=k=spaces=0; j < line_len; j++) {
882 if (k == MAX_COLUMNS)
883 break;
885 c = line_begin[j];
886 switch (c) {
887 case ' ':
888 spaces++;
889 break;
890 case 0:
891 spaces = 0;
892 scratch_buffer[k++] = ' ';
893 break;
894 default:
895 while (spaces) {
896 spaces--;
897 scratch_buffer[k++] = ' ';
898 if (k == MAX_COLUMNS - 1)
899 break;
901 scratch_buffer[k++] = c;
902 break;
905 if (col != -1) {
906 scratch_buffer[k] = 0;
907 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
908 prefs.encoding, draw_columns/glyph_width('i'));
909 *endptr = 0;
912 else if (prefs.line_mode == REFLOW) {
913 if (line_begin[0] == 0) {
914 line_begin++;
915 if (prefs.word_mode == CHOP)
916 line_end++;
917 else
918 line_len--;
921 indent_spaces = 0;
922 if (!line_is_short) {
923 multiple_spacing = false;
924 width=spaces=0;
925 for (str = line_begin; str < line_end; ) {
926 str = get_ucs(str, &ch);
927 switch (ch) {
928 case ' ':
929 case 0:
930 if ((str == line_begin) && (prefs.word_mode==WRAP))
931 /* special case: indent the paragraph,
932 * don't count spaces */
933 indent_spaces = par_indent_spaces;
934 else if (!multiple_spacing)
935 spaces++;
936 multiple_spacing = true;
937 break;
938 default:
939 multiple_spacing = false;
940 width += glyph_width(ch);
941 break;
944 if (multiple_spacing) spaces--;
946 if (spaces) {
947 /* total number of spaces to insert between words */
948 extra_spaces = (draw_columns-width)/glyph_width(' ')
949 - indent_spaces;
950 /* number of spaces between each word*/
951 spaces_per_word = extra_spaces / spaces;
952 /* number of words with n+1 spaces (to fill up) */
953 extra_spaces = extra_spaces % spaces;
954 if (spaces_per_word > 2) { /* too much spacing is awful */
955 spaces_per_word = 3;
956 extra_spaces = 0;
958 } else { /* this doesn't matter much... no spaces anyway */
959 spaces_per_word = extra_spaces = 0;
961 } else { /* end of a paragraph: don't fill line */
962 spaces_per_word = 1;
963 extra_spaces = 0;
966 multiple_spacing = false;
967 for (j=k=spaces=0; j < line_len; j++) {
968 if (k == MAX_COLUMNS)
969 break;
971 c = line_begin[j];
972 switch (c) {
973 case ' ':
974 case 0:
975 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
976 for (j=0; j<par_indent_spaces; j++)
977 scratch_buffer[k++] = ' ';
978 j=0;
980 else if (!multiple_spacing) {
981 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
982 scratch_buffer[k++] = ' ';
983 spaces++;
985 multiple_spacing = true;
986 break;
987 default:
988 scratch_buffer[k++] = c;
989 multiple_spacing = false;
990 break;
994 if (col != -1) {
995 scratch_buffer[k] = 0;
996 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
997 prefs.encoding, k-col);
998 *endptr = 0;
1001 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1002 if (col != -1)
1003 if (line_width > col) {
1004 str = oldstr = line_begin;
1005 k = col;
1006 width = 0;
1007 while( (width<draw_columns) && (oldstr<line_end) )
1009 oldstr = get_ucs(oldstr, &ch);
1010 if (k > 0) {
1011 k -= glyph_width(ch);
1012 line_begin = oldstr;
1013 } else {
1014 width += glyph_width(ch);
1018 if(prefs.view_mode==WIDE)
1019 endptr = rb->iso_decode(line_begin, utf8_buffer,
1020 prefs.encoding, oldstr-line_begin);
1021 else
1022 endptr = rb->iso_decode(line_begin, utf8_buffer,
1023 prefs.encoding, line_end-line_begin);
1024 *endptr = 0;
1027 if (col != -1 && line_width > col)
1028 #ifdef HAVE_LCD_BITMAP
1029 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1030 #else
1031 rb->lcd_puts(left_col, i, utf8_buffer);
1032 #endif
1033 if (line_width > max_line_len)
1034 max_line_len = line_width;
1036 if (i == 0)
1037 next_line_ptr = line_end;
1039 next_screen_ptr = line_end;
1040 if (BUFFER_OOB(next_screen_ptr))
1041 next_screen_ptr = NULL;
1043 #ifdef HAVE_LCD_BITMAP
1044 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1046 if (prefs.need_scrollbar)
1047 viewer_scrollbar();
1048 #else
1049 next_screen_to_draw_ptr = next_screen_ptr;
1050 #endif
1052 if (col != -1)
1053 rb->lcd_update();
1056 static void viewer_top(void)
1058 /* Read top of file into buffer
1059 and point screen pointer to top */
1060 if (file_pos != 0)
1062 file_pos = 0;
1063 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1064 fill_buffer(0, buffer, buffer_size);
1067 screen_top_ptr = buffer;
1070 static void viewer_bottom(void)
1072 /* Read bottom of file into buffer
1073 and point screen pointer to bottom */
1074 long last_sectors;
1076 if (file_size > buffer_size) {
1077 /* Find last buffer in file, round up to next sector boundary */
1078 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1079 last_sectors /= SMALL_BLOCK_SIZE;
1080 last_sectors *= SMALL_BLOCK_SIZE;
1082 else {
1083 last_sectors = 0;
1086 if (file_pos != last_sectors)
1088 file_pos = last_sectors;
1089 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1090 fill_buffer(last_sectors, buffer, buffer_size);
1093 screen_top_ptr = buffer_end-1;
1096 #ifdef HAVE_LCD_BITMAP
1097 static void init_need_scrollbar(void) {
1098 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1099 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1100 viewer_draw(-1);
1101 prefs.need_scrollbar = NEED_SCROLLBAR();
1102 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1103 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1105 #else
1106 #define init_need_scrollbar()
1107 #endif
1109 static bool viewer_init(void)
1111 #ifdef HAVE_LCD_BITMAP
1113 pf = rb->font_get(FONT_UI);
1115 display_lines = LCD_HEIGHT / pf->height;
1116 draw_columns = display_columns = LCD_WIDTH;
1117 #else
1118 /* REAL fixed pitch :) all chars use up 1 cell */
1119 display_lines = 2;
1120 draw_columns = display_columns = 11;
1121 par_indent_spaces = 2;
1122 #endif
1124 fd = rb->open(file_name, O_RDONLY);
1125 if (fd==-1)
1126 return false;
1128 file_size = rb->filesize(fd);
1129 if (file_size==-1)
1130 return false;
1132 /* Init mac_text value used in processing buffer */
1133 mac_text = false;
1135 return true;
1138 static void viewer_default_settings(void)
1140 prefs.word_mode = WRAP;
1141 prefs.line_mode = NORMAL;
1142 prefs.view_mode = NARROW;
1143 prefs.scroll_mode = PAGE;
1144 #ifdef HAVE_LCD_BITMAP
1145 prefs.page_mode = NO_OVERLAP;
1146 prefs.scrollbar_mode = SB_OFF;
1147 #endif
1148 prefs.autoscroll_speed = 1;
1149 /* Set codepage to system default */
1150 prefs.encoding = rb->global_settings->default_codepage;
1153 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1155 int settings_fd, i;
1156 struct bookmark_file_data *data;
1157 struct bookmarked_file_info this_bookmark;
1159 /* read settings file */
1160 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1161 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1163 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1164 rb->close(settings_fd);
1166 else
1168 /* load default settings if there is no settings file */
1169 viewer_default_settings();
1172 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1174 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1175 data->bookmarked_files_count = 0;
1177 /* read bookmarks if file exists */
1178 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1179 if (settings_fd >= 0)
1181 /* figure out how many items to read */
1182 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1183 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1184 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1185 rb->read(settings_fd, data->bookmarks,
1186 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1187 rb->close(settings_fd);
1190 file_pos = 0;
1191 screen_top_ptr = buffer;
1193 /* check if current file is in list */
1194 for (i=0; i < data->bookmarked_files_count; i++)
1196 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1198 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1199 int screen_top = screen_pos % buffer_size;
1200 file_pos = screen_pos - screen_top;
1201 screen_top_ptr = buffer + screen_top;
1202 break;
1206 this_bookmark.file_position = file_pos;
1207 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1209 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1210 rb->strcpy(this_bookmark.filename,file_name);
1212 /* prevent potential slot overflow */
1213 if (i >= data->bookmarked_files_count)
1215 if (i < MAX_BOOKMARKED_FILES)
1216 data->bookmarked_files_count++;
1217 else
1218 i = MAX_BOOKMARKED_FILES-1;
1221 /* write bookmark file with spare slot in first position
1222 to be filled in by viewer_save_settings */
1223 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1224 if (settings_fd >=0 )
1226 /* write count */
1227 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1229 /* write the current bookmark */
1230 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1232 /* write everything that was before this bookmark */
1233 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1235 rb->close(settings_fd);
1238 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1240 if (BUFFER_OOB(screen_top_ptr))
1242 screen_top_ptr = buffer;
1245 fill_buffer(file_pos, buffer, buffer_size);
1247 /* remember the current position */
1248 start_position = file_pos + screen_top_ptr - buffer;
1250 init_need_scrollbar();
1253 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1255 int settings_fd;
1257 /* save the viewer settings if they have been changed */
1258 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1260 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1262 if (settings_fd >= 0 )
1264 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1265 rb->close(settings_fd);
1269 /* save the bookmark if the position has changed */
1270 if (file_pos + screen_top_ptr - buffer != start_position)
1272 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1274 if (settings_fd >= 0 )
1276 struct bookmarked_file_info b;
1277 b.file_position = file_pos + screen_top_ptr - buffer;
1278 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1279 rb->memset(&b.filename[0],0,MAX_PATH);
1280 rb->strcpy(b.filename,file_name);
1281 rb->PREFIX(lseek)(settings_fd,sizeof(signed int),SEEK_SET);
1282 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1283 rb->close(settings_fd);
1288 static void viewer_exit(void *parameter)
1290 (void)parameter;
1292 viewer_save_settings();
1293 rb->close(fd);
1296 static int col_limit(int col)
1298 if (col < 0)
1299 col = 0;
1300 else
1301 if (col > max_line_len - 2*glyph_width('o'))
1302 col = max_line_len - 2*glyph_width('o');
1304 return col;
1307 /* settings helper functions */
1309 static bool encoding_setting(void)
1311 static const struct opt_items names[] = {
1312 {"ISO-8859-1", -1},
1313 {"ISO-8859-7", -1},
1314 {"ISO-8859-8", -1},
1315 {"CP1251", -1},
1316 {"ISO-8859-11", -1},
1317 {"ISO-8859-6", -1},
1318 {"ISO-8859-9", -1},
1319 {"ISO-8859-2", -1},
1320 {"CP1250", -1},
1321 {"SJIS", -1},
1322 {"GB-2312", -1},
1323 {"KSX-1001", -1},
1324 {"BIG5", -1},
1325 {"UTF-8", -1},
1328 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1329 sizeof(names) / sizeof(names[0]), NULL);
1332 static bool word_wrap_setting(void)
1334 static const struct opt_items names[] = {
1335 {"On", -1},
1336 {"Off (Chop Words)", -1},
1339 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1340 names, 2, NULL);
1343 static bool line_mode_setting(void)
1345 static const struct opt_items names[] = {
1346 {"Normal", -1},
1347 {"Join Lines", -1},
1348 {"Expand Lines", -1},
1349 #ifdef HAVE_LCD_BITMAP
1350 {"Reflow Lines", -1},
1351 #endif
1354 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1355 sizeof(names) / sizeof(names[0]), NULL);
1358 static bool view_mode_setting(void)
1360 static const struct opt_items names[] = {
1361 {"No (Narrow)", -1},
1362 {"Yes", -1},
1364 bool ret;
1365 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1366 names , 2, NULL);
1367 if (prefs.view_mode == NARROW)
1368 col = 0;
1369 return ret;
1372 static bool scroll_mode_setting(void)
1374 static const struct opt_items names[] = {
1375 {"Scroll by Page", -1},
1376 {"Scroll by Line", -1},
1379 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1380 names, 2, NULL);
1383 #ifdef HAVE_LCD_BITMAP
1384 static bool page_mode_setting(void)
1386 static const struct opt_items names[] = {
1387 {"No", -1},
1388 {"Yes", -1},
1391 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1392 names, 2, NULL);
1395 static bool scrollbar_setting(void)
1397 static const struct opt_items names[] = {
1398 {"Off", -1},
1399 {"On", -1}
1402 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1403 names, 2, NULL);
1405 #endif
1407 static bool autoscroll_speed_setting(void)
1409 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1410 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1413 static bool viewer_options_menu(void)
1415 int m;
1416 bool result;
1418 static const struct menu_item items[] = {
1419 {"Encoding", encoding_setting },
1420 {"Word Wrap", word_wrap_setting },
1421 {"Line Mode", line_mode_setting },
1422 {"Wide View", view_mode_setting },
1423 #ifdef HAVE_LCD_BITMAP
1424 {"Show Scrollbar", scrollbar_setting },
1425 {"Overlap Pages", page_mode_setting },
1426 #endif
1427 {"Scroll Mode", scroll_mode_setting},
1428 {"Auto-Scroll Speed", autoscroll_speed_setting },
1430 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1431 NULL, NULL, NULL, NULL);
1433 result = menu_run(m);
1434 menu_exit(m);
1435 #ifdef HAVE_LCD_BITMAP
1437 /* Show-scrollbar mode for current view-width mode */
1438 init_need_scrollbar();
1439 #endif
1440 return result;
1443 static void viewer_menu(void)
1445 int m;
1446 int result;
1447 static const struct menu_item items[] = {
1448 {"Quit", NULL },
1449 {"Viewer Options", NULL },
1450 {"Show Playback Menu", NULL },
1451 {"Return", NULL },
1454 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1455 result=menu_show(m);
1456 switch (result)
1458 case 0: /* quit */
1459 menu_exit(m);
1460 viewer_exit(NULL);
1461 done = true;
1462 break;
1463 case 1: /* change settings */
1464 done = viewer_options_menu();
1465 break;
1466 case 2: /* playback control */
1467 playback_control(rb, NULL);
1468 break;
1469 case 3: /* return */
1470 break;
1472 menu_exit(m);
1473 viewer_draw(col);
1476 enum plugin_status plugin_start(const struct plugin_api* api, const void* file)
1478 int button, i, ok;
1479 int lastbutton = BUTTON_NONE;
1480 bool autoscroll = false;
1481 long old_tick;
1483 rb = api;
1484 old_tick = *rb->current_tick;
1486 /* get the plugin buffer */
1487 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1489 if (!file)
1490 return PLUGIN_ERROR;
1492 file_name = file;
1493 ok = viewer_init();
1494 if (!ok) {
1495 rb->splash(HZ, "Error opening file.");
1496 return PLUGIN_ERROR;
1499 viewer_load_settings(); /* load the preferences and bookmark */
1501 #if LCD_DEPTH > 1
1502 rb->lcd_set_backdrop(NULL);
1503 #endif
1505 viewer_draw(col);
1507 while (!done) {
1509 if(autoscroll)
1511 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1513 viewer_scroll_down();
1514 viewer_draw(col);
1515 old_tick = *rb->current_tick;
1519 button = rb->button_get_w_tmo(HZ/10);
1520 switch (button) {
1521 case VIEWER_MENU:
1522 viewer_menu();
1523 break;
1525 case VIEWER_AUTOSCROLL:
1526 #ifdef VIEWER_AUTOSCROLL_PRE
1527 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1528 break;
1529 #endif
1530 autoscroll = !autoscroll;
1531 break;
1533 case VIEWER_PAGE_UP:
1534 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1535 if (prefs.scroll_mode == PAGE)
1537 /* Page up */
1538 #ifdef HAVE_LCD_BITMAP
1539 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1540 #else
1541 for (i = 0; i < display_lines; i++)
1542 #endif
1543 viewer_scroll_up();
1545 else
1546 viewer_scroll_up();
1547 old_tick = *rb->current_tick;
1548 viewer_draw(col);
1549 break;
1551 case VIEWER_PAGE_DOWN:
1552 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1553 if (prefs.scroll_mode == PAGE)
1555 /* Page down */
1556 if (next_screen_ptr != NULL)
1557 screen_top_ptr = next_screen_to_draw_ptr;
1559 else
1560 viewer_scroll_down();
1561 old_tick = *rb->current_tick;
1562 viewer_draw(col);
1563 break;
1565 case VIEWER_SCREEN_LEFT:
1566 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1567 if (prefs.view_mode == WIDE) {
1568 /* Screen left */
1569 col -= draw_columns;
1570 col = col_limit(col);
1572 else { /* prefs.view_mode == NARROW */
1573 /* Top of file */
1574 viewer_top();
1577 viewer_draw(col);
1578 break;
1580 case VIEWER_SCREEN_RIGHT:
1581 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1582 if (prefs.view_mode == WIDE) {
1583 /* Screen right */
1584 col += draw_columns;
1585 col = col_limit(col);
1587 else { /* prefs.view_mode == NARROW */
1588 /* Bottom of file */
1589 viewer_bottom();
1592 viewer_draw(col);
1593 break;
1595 #ifdef VIEWER_LINE_UP
1596 case VIEWER_LINE_UP:
1597 case VIEWER_LINE_UP | BUTTON_REPEAT:
1598 /* Scroll up one line */
1599 viewer_scroll_up();
1600 old_tick = *rb->current_tick;
1601 viewer_draw(col);
1602 break;
1604 case VIEWER_LINE_DOWN:
1605 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1606 /* Scroll down one line */
1607 if (next_screen_ptr != NULL)
1608 screen_top_ptr = next_line_ptr;
1609 old_tick = *rb->current_tick;
1610 viewer_draw(col);
1611 break;
1612 #endif
1613 #ifdef VIEWER_COLUMN_LEFT
1614 case VIEWER_COLUMN_LEFT:
1615 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1616 if (prefs.view_mode == WIDE) {
1617 /* Scroll left one column */
1618 col -= glyph_width('o');
1619 col = col_limit(col);
1620 viewer_draw(col);
1622 break;
1624 case VIEWER_COLUMN_RIGHT:
1625 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1626 if (prefs.view_mode == WIDE) {
1627 /* Scroll right one column */
1628 col += glyph_width('o');
1629 col = col_limit(col);
1630 viewer_draw(col);
1632 break;
1633 #endif
1635 #ifdef VIEWER_RC_QUIT
1636 case VIEWER_RC_QUIT:
1637 #endif
1638 case VIEWER_QUIT:
1639 viewer_exit(NULL);
1640 done = true;
1641 break;
1643 default:
1644 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1645 == SYS_USB_CONNECTED)
1646 return PLUGIN_USB_CONNECTED;
1647 break;
1649 if (button != BUTTON_NONE)
1651 lastbutton = button;
1652 rb->yield();
1655 return PLUGIN_OK;