Fix yellow
[kugel-rb.git] / apps / plugins / viewer.c
blobe17033fcd1e5a394bfeacfc909fd14cb30f7ba00
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 Fuze keys */
189 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
190 #define VIEWER_QUIT (BUTTON_HOME|BUTTON_REPEAT)
191 #define VIEWER_PAGE_UP BUTTON_UP
192 #define VIEWER_PAGE_DOWN BUTTON_DOWN
193 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
194 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
195 #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT
196 #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
197 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
198 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
200 /* Sansa C200 keys */
201 #elif CONFIG_KEYPAD == SANSA_C200_PAD
202 #define VIEWER_QUIT BUTTON_POWER
203 #define VIEWER_PAGE_UP BUTTON_VOL_UP
204 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
205 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
206 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
207 #define VIEWER_MENU BUTTON_SELECT
208 #define VIEWER_AUTOSCROLL BUTTON_REC
209 #define VIEWER_LINE_UP BUTTON_UP
210 #define VIEWER_LINE_DOWN BUTTON_DOWN
212 /* Sansa Clip keys */
213 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
214 #define VIEWER_QUIT BUTTON_POWER
215 #define VIEWER_PAGE_UP BUTTON_VOL_UP
216 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
217 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
218 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
219 #define VIEWER_MENU BUTTON_SELECT
220 #define VIEWER_AUTOSCROLL BUTTON_HOME
221 #define VIEWER_LINE_UP BUTTON_UP
222 #define VIEWER_LINE_DOWN BUTTON_DOWN
224 /* Sansa M200 keys */
225 #elif CONFIG_KEYPAD == SANSA_M200_PAD
226 #define VIEWER_QUIT BUTTON_POWER
227 #define VIEWER_PAGE_UP BUTTON_VOL_UP
228 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
229 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
230 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
231 #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP)
232 #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
233 #define VIEWER_LINE_UP BUTTON_UP
234 #define VIEWER_LINE_DOWN BUTTON_DOWN
236 /* iriver H10 keys */
237 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
238 #define VIEWER_QUIT BUTTON_POWER
239 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
240 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
241 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
242 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
243 #define VIEWER_MENU BUTTON_REW
244 #define VIEWER_AUTOSCROLL BUTTON_PLAY
246 /*M-Robe 500 keys */
247 #elif CONFIG_KEYPAD == MROBE500_PAD
248 #define VIEWER_QUIT BUTTON_POWER
249 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
250 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
251 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
252 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
253 #define VIEWER_MENU BUTTON_RC_HEART
254 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
256 /*Gigabeat S keys */
257 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
258 #define VIEWER_QUIT BUTTON_BACK
259 #define VIEWER_PAGE_UP BUTTON_PREV
260 #define VIEWER_PAGE_DOWN BUTTON_NEXT
261 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
262 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
263 #define VIEWER_MENU BUTTON_MENU
264 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
265 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
266 #define VIEWER_LINE_UP BUTTON_UP
267 #define VIEWER_LINE_DOWN BUTTON_DOWN
268 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
269 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
271 /*M-Robe 100 keys */
272 #elif CONFIG_KEYPAD == MROBE100_PAD
273 #define VIEWER_QUIT BUTTON_POWER
274 #define VIEWER_PAGE_UP BUTTON_UP
275 #define VIEWER_PAGE_DOWN BUTTON_DOWN
276 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
277 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
278 #define VIEWER_MENU BUTTON_MENU
279 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
281 /* iAUdio M3 keys */
282 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
283 #define VIEWER_QUIT BUTTON_RC_REC
284 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
285 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
286 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
287 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
288 #define VIEWER_MENU BUTTON_RC_MENU
289 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
290 #define VIEWER_RC_QUIT BUTTON_REC
292 /* Cowon D2 keys */
293 #elif CONFIG_KEYPAD == COWOND2_PAD
294 #define VIEWER_QUIT BUTTON_POWER
295 #define VIEWER_MENU BUTTON_MENU
297 #elif CONFIG_KEYPAD == IAUDIO67_PAD
298 #define VIEWER_QUIT BUTTON_POWER
299 #define VIEWER_PAGE_UP BUTTON_VOLUP
300 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
301 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
302 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
303 #define VIEWER_MENU BUTTON_MENU
304 #define VIEWER_AUTOSCROLL BUTTON_PLAY
305 #define VIEWER_RC_QUIT BUTTON_STOP
307 /* Creative Zen Vision:M keys */
308 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
309 #define VIEWER_QUIT BUTTON_BACK
310 #define VIEWER_PAGE_UP BUTTON_UP
311 #define VIEWER_PAGE_DOWN BUTTON_DOWN
312 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
313 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
314 #define VIEWER_MENU BUTTON_MENU
315 #define VIEWER_AUTOSCROLL BUTTON_SELECT
317 /* Philips HDD1630 keys */
318 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
319 #define VIEWER_QUIT BUTTON_POWER
320 #define VIEWER_PAGE_UP BUTTON_UP
321 #define VIEWER_PAGE_DOWN BUTTON_DOWN
322 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
323 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
324 #define VIEWER_MENU BUTTON_MENU
325 #define VIEWER_AUTOSCROLL BUTTON_VIEW
327 /* Onda VX747 keys */
328 #elif CONFIG_KEYPAD == ONDAVX747_PAD
329 #define VIEWER_QUIT BUTTON_POWER
330 #define VIEWER_MENU BUTTON_MENU
332 #else
333 #error No keymap defined!
334 #endif
336 #ifdef HAVE_TOUCHSCREEN
337 #ifndef VIEWER_QUIT
338 #define VIEWER_QUIT BUTTON_TOPLEFT
339 #endif
340 #ifndef VIEWER_PAGE_UP
341 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
342 #endif
343 #ifndef VIEWER_PAGE_DOWN
344 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
345 #endif
346 #ifndef VIEWER_SCREEN_LEFT
347 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
348 #endif
349 #ifndef VIEWER_SCREEN_RIGHT
350 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
351 #endif
352 #ifndef VIEWER_MENU
353 #define VIEWER_MENU BUTTON_TOPRIGHT
354 #endif
355 #ifndef VIEWER_AUTOSCROLL
356 #define VIEWER_AUTOSCROLL BUTTON_CENTER
357 #endif
358 #endif
360 /* stuff for the bookmarking */
361 struct bookmarked_file_info {
362 long file_position;
363 int top_ptr_pos;
364 char filename[MAX_PATH];
367 struct bookmark_file_data {
368 signed int bookmarked_files_count;
369 struct bookmarked_file_info bookmarks[];
372 struct preferences {
373 enum {
374 WRAP=0,
375 CHOP,
376 } word_mode;
378 enum {
379 NORMAL=0,
380 JOIN,
381 EXPAND,
382 REFLOW, /* won't be set on charcell LCD, must be last */
383 } line_mode;
385 enum {
386 NARROW=0,
387 WIDE,
388 } view_mode;
390 enum codepages encoding;
392 #ifdef HAVE_LCD_BITMAP
393 enum {
394 SB_OFF=0,
395 SB_ON,
396 } scrollbar_mode;
397 bool need_scrollbar;
399 enum {
400 NO_OVERLAP=0,
401 OVERLAP,
402 } page_mode;
403 #endif /* HAVE_LCD_BITMAP */
405 enum {
406 PAGE=0,
407 LINE,
408 } scroll_mode;
410 int autoscroll_speed;
414 struct preferences prefs;
415 struct preferences old_prefs;
417 static unsigned char *buffer;
418 static long buffer_size;
419 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
420 static int display_columns; /* number of (pixel) columns on the display */
421 static int display_lines; /* number of lines on the display */
422 static int draw_columns; /* number of (pixel) columns available for text */
423 static int par_indent_spaces; /* number of spaces to indent first paragraph */
424 static int fd;
425 static const char *file_name;
426 static long file_size;
427 static long start_position; /* position in the file after the viewer is started */
428 static bool mac_text;
429 static long file_pos; /* Position of the top of the buffer in the file */
430 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
431 static int max_line_len;
432 static unsigned char *screen_top_ptr;
433 static unsigned char *next_screen_ptr;
434 static unsigned char *next_screen_to_draw_ptr;
435 static unsigned char *next_line_ptr;
436 #ifdef HAVE_LCD_BITMAP
437 static struct font *pf;
438 #endif
441 int glyph_width(int ch)
443 if (ch == 0)
444 ch = ' ';
446 #ifdef HAVE_LCD_BITMAP
447 return rb->font_get_width(pf, ch);
448 #else
449 return 1;
450 #endif
453 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
455 unsigned char utf8_tmp[6];
456 int count;
458 if (prefs.encoding == UTF_8)
459 return (unsigned char*)rb->utf8decode(str, ch);
461 count = BUFFER_OOB(str+2)? 1:2;
462 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
463 rb->utf8decode(utf8_tmp, ch);
465 #ifdef HAVE_LCD_BITMAP
466 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
467 return (unsigned char*)str+1;
468 else
469 #endif
470 return (unsigned char*)str+2;
473 bool done = false;
474 int col = 0;
476 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
477 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
478 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
479 static unsigned char* crop_at_width(const unsigned char* p)
481 int k,width;
482 unsigned short ch;
483 const unsigned char *oldp = p;
485 k=width=0;
487 while (LINE_IS_NOT_FULL) {
488 oldp = p;
489 p = get_ucs(p, &ch);
490 ADVANCE_COUNTERS(ch);
493 return (unsigned char*)oldp;
496 static unsigned char* find_first_feed(const unsigned char* p, int size)
498 int i;
500 for (i=0; i < size; i++)
501 if (p[i] == 0)
502 return (unsigned char*) p+i;
504 return NULL;
507 static unsigned char* find_last_feed(const unsigned char* p, int size)
509 int i;
511 for (i=size-1; i>=0; i--)
512 if (p[i] == 0)
513 return (unsigned char*) p+i;
515 return NULL;
518 static unsigned char* find_last_space(const unsigned char* p, int size)
520 int i, j, k;
522 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
524 if (!BUFFER_OOB(&p[size]))
525 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
526 if (p[size] == line_break[j])
527 return (unsigned char*) p+size;
529 for (i=size-1; i>=0; i--)
530 for (j=k; j < (int) sizeof(line_break); j++)
532 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
533 if (p[i] == line_break[j])
534 return (unsigned char*) p+i;
537 return NULL;
540 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
542 const unsigned char *next_line = NULL;
543 int size, i, j, k, width, search_len, spaces, newlines;
544 bool first_chars;
545 unsigned char c;
547 if (is_short != NULL)
548 *is_short = true;
550 if BUFFER_OOB(cur_line)
551 return NULL;
553 if (prefs.view_mode == WIDE) {
554 search_len = MAX_WIDTH;
556 else { /* prefs.view_mode == NARROW */
557 search_len = crop_at_width(cur_line) - cur_line;
560 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
562 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
563 /* Need to scan ahead and possibly increase search_len and size,
564 or possibly set next_line at second hard return in a row. */
565 next_line = NULL;
566 first_chars=true;
567 for (j=k=width=spaces=newlines=0; ; j++) {
568 if (BUFFER_OOB(cur_line+j))
569 return NULL;
570 if (LINE_IS_FULL) {
571 size = search_len = j;
572 break;
575 c = cur_line[j];
576 switch (c) {
577 case ' ':
578 if (prefs.line_mode == REFLOW) {
579 if (newlines > 0) {
580 size = j;
581 next_line = cur_line + size;
582 return (unsigned char*) next_line;
584 if (j==0) /* i=1 is intentional */
585 for (i=0; i<par_indent_spaces; i++)
586 ADVANCE_COUNTERS(' ');
588 if (!first_chars) spaces++;
589 break;
591 case 0:
592 if (newlines > 0) {
593 size = j;
594 next_line = cur_line + size - spaces;
595 if (next_line != cur_line)
596 return (unsigned char*) next_line;
597 break;
600 newlines++;
601 size += spaces -1;
602 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
603 return NULL;
604 search_len = size;
605 spaces = first_chars? 0:1;
606 break;
608 default:
609 if (prefs.line_mode==JOIN || newlines>0) {
610 while (spaces) {
611 spaces--;
612 ADVANCE_COUNTERS(' ');
613 if (LINE_IS_FULL) {
614 size = search_len = j;
615 break;
618 newlines=0;
619 } else if (spaces) {
620 /* REFLOW, multiple spaces between words: count only
621 * one. If more are needed, they will be added
622 * while drawing. */
623 search_len = size;
624 spaces=0;
625 ADVANCE_COUNTERS(' ');
626 if (LINE_IS_FULL) {
627 size = search_len = j;
628 break;
631 first_chars = false;
632 ADVANCE_COUNTERS(c);
633 break;
637 else {
638 /* find first hard return */
639 next_line = find_first_feed(cur_line, size);
642 if (next_line == NULL)
643 if (size == search_len) {
644 if (prefs.word_mode == WRAP) /* Find last space */
645 next_line = find_last_space(cur_line, size);
647 if (next_line == NULL)
648 next_line = crop_at_width(cur_line);
649 else
650 if (prefs.word_mode == WRAP)
651 for (i=0;
652 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
653 i++)
654 next_line++;
657 if (prefs.line_mode == EXPAND)
658 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
659 if (next_line[0] == 0)
660 if (next_line != cur_line)
661 return (unsigned char*) next_line;
663 /* If next_line is pointing to a zero, increment it; i.e.,
664 leave the terminator at the end of cur_line. If pointing
665 to a hyphen, increment only if there is room to display
666 the hyphen on current line (won't apply in WIDE mode,
667 since it's guarenteed there won't be room). */
668 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
669 if (next_line[0] == 0)/* ||
670 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
671 next_line++;
673 if (BUFFER_OOB(next_line))
674 return NULL;
676 if (is_short)
677 *is_short = false;
679 return (unsigned char*) next_line;
682 static unsigned char* find_prev_line(const unsigned char* cur_line)
684 const unsigned char *prev_line = NULL;
685 const unsigned char *p;
687 if BUFFER_OOB(cur_line)
688 return NULL;
690 /* To wrap consistently at the same places, we must
691 start with a known hard return, then work downwards.
692 We can either search backwards for a hard return,
693 or simply start wrapping downwards from top of buffer.
694 If current line is not near top of buffer, this is
695 a file with long lines (paragraphs). We would need to
696 read earlier sectors before we could decide how to
697 properly wrap the lines above the current line, but
698 it probably is not worth the disk access. Instead,
699 start with top of buffer and wrap down from there.
700 This may result in some lines wrapping at different
701 points from where they wrap when scrolling down.
702 If buffer is at top of file, start at top of buffer. */
704 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
705 prev_line = p = NULL;
706 else
707 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
708 /* Null means no line feeds in buffer above current line. */
710 if (prev_line == NULL)
711 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
712 prev_line = p = buffer;
713 /* (else return NULL and read previous block) */
715 /* Wrap downwards until too far, then use the one before. */
716 while (p < cur_line && p != NULL) {
717 prev_line = p;
718 p = find_next_line(prev_line, NULL);
721 if (BUFFER_OOB(prev_line))
722 return NULL;
724 return (unsigned char*) prev_line;
727 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
729 /* Read from file and preprocess the data */
730 /* To minimize disk access, always read on sector boundaries */
731 unsigned numread, i;
732 bool found_CR = false;
734 rb->lseek(fd, pos, SEEK_SET);
735 numread = rb->read(fd, buf, size);
736 rb->button_clear_queue(); /* clear button queue */
738 for(i = 0; i < numread; i++) {
739 switch(buf[i]) {
740 case '\r':
741 if (mac_text) {
742 buf[i] = 0;
744 else {
745 buf[i] = ' ';
746 found_CR = true;
748 break;
750 case '\n':
751 buf[i] = 0;
752 found_CR = false;
753 break;
755 case 0: /* No break between case 0 and default, intentionally */
756 buf[i] = ' ';
757 default:
758 if (found_CR) {
759 buf[i - 1] = 0;
760 found_CR = false;
761 mac_text = true;
763 break;
768 static int read_and_synch(int direction)
770 /* Read next (or prev) block, and reposition global pointers. */
771 /* direction: 1 for down (i.e., further into file), -1 for up */
772 int move_size, move_vector, offset;
773 unsigned char *fill_buf;
775 if (direction == -1) /* up */ {
776 move_size = SMALL_BLOCK_SIZE;
777 offset = 0;
778 fill_buf = TOP_SECTOR;
779 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
780 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
782 else /* down */ {
783 if (prefs.view_mode == WIDE) {
784 /* WIDE mode needs more buffer so we have to read smaller blocks */
785 move_size = SMALL_BLOCK_SIZE;
786 offset = LARGE_BLOCK_SIZE;
787 fill_buf = BOTTOM_SECTOR;
788 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
789 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
791 else {
792 move_size = LARGE_BLOCK_SIZE;
793 offset = SMALL_BLOCK_SIZE;
794 fill_buf = MID_SECTOR;
795 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
798 move_vector = direction * move_size;
799 screen_top_ptr -= move_vector;
800 file_pos += move_vector;
801 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
802 fill_buffer(file_pos + offset, fill_buf, move_size);
803 return move_vector;
806 static void viewer_scroll_up(void)
808 unsigned char *p;
810 p = find_prev_line(screen_top_ptr);
811 if (p == NULL && !BUFFER_BOF()) {
812 read_and_synch(-1);
813 p = find_prev_line(screen_top_ptr);
815 if (p != NULL)
816 screen_top_ptr = p;
819 static void viewer_scroll_down(void)
821 if (next_screen_ptr != NULL)
822 screen_top_ptr = next_line_ptr;
825 #ifdef HAVE_LCD_BITMAP
826 static void viewer_scrollbar(void) {
827 int items, min_shown, max_shown;
829 items = (int) file_size; /* (SH1 int is same as long) */
830 min_shown = (int) file_pos + (screen_top_ptr - buffer);
832 if (next_screen_ptr == NULL)
833 max_shown = items;
834 else
835 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
837 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
838 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
840 #endif
842 static void viewer_draw(int col)
844 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
845 int width, extra_spaces, indent_spaces, spaces_per_word;
846 bool multiple_spacing, line_is_short;
847 unsigned short ch;
848 unsigned char *str, *oldstr;
849 unsigned char *line_begin;
850 unsigned char *line_end;
851 unsigned char c;
852 unsigned char scratch_buffer[MAX_COLUMNS + 1];
853 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
854 unsigned char *endptr;
856 /* If col==-1 do all calculations but don't display */
857 if (col != -1) {
858 #ifdef HAVE_LCD_BITMAP
859 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
860 #else
861 left_col = 0;
862 #endif
863 rb->lcd_clear_display();
865 max_line_len = 0;
866 line_begin = line_end = screen_top_ptr;
868 for (i = 0; i < display_lines; i++) {
869 if (BUFFER_OOB(line_end))
870 break; /* Happens after display last line at BUFFER_EOF() */
872 line_begin = line_end;
873 line_end = find_next_line(line_begin, &line_is_short);
875 if (line_end == NULL) {
876 if (BUFFER_EOF()) {
877 if (i < display_lines - 1 && !BUFFER_BOF()) {
878 if (col != -1)
879 rb->lcd_clear_display();
881 for (; i < display_lines - 1; i++)
882 viewer_scroll_up();
884 line_begin = line_end = screen_top_ptr;
885 i = -1;
886 continue;
888 else {
889 line_end = buffer_end;
892 else {
893 resynch_move = read_and_synch(1); /* Read block & move ptrs */
894 line_begin -= resynch_move;
895 if (i > 0)
896 next_line_ptr -= resynch_move;
898 line_end = find_next_line(line_begin, NULL);
899 if (line_end == NULL) /* Should not really happen */
900 break;
903 line_len = line_end - line_begin;
905 /* calculate line_len */
906 str = oldstr = line_begin;
907 j = -1;
908 while (str < line_end) {
909 oldstr = str;
910 str = crop_at_width(str);
911 j++;
913 line_width = j*draw_columns;
914 while (oldstr < line_end) {
915 oldstr = get_ucs(oldstr, &ch);
916 line_width += glyph_width(ch);
919 if (prefs.line_mode == JOIN) {
920 if (line_begin[0] == 0) {
921 line_begin++;
922 if (prefs.word_mode == CHOP)
923 line_end++;
924 else
925 line_len--;
927 for (j=k=spaces=0; j < line_len; j++) {
928 if (k == MAX_COLUMNS)
929 break;
931 c = line_begin[j];
932 switch (c) {
933 case ' ':
934 spaces++;
935 break;
936 case 0:
937 spaces = 0;
938 scratch_buffer[k++] = ' ';
939 break;
940 default:
941 while (spaces) {
942 spaces--;
943 scratch_buffer[k++] = ' ';
944 if (k == MAX_COLUMNS - 1)
945 break;
947 scratch_buffer[k++] = c;
948 break;
951 if (col != -1) {
952 scratch_buffer[k] = 0;
953 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
954 prefs.encoding, draw_columns/glyph_width('i'));
955 *endptr = 0;
958 else if (prefs.line_mode == REFLOW) {
959 if (line_begin[0] == 0) {
960 line_begin++;
961 if (prefs.word_mode == CHOP)
962 line_end++;
963 else
964 line_len--;
967 indent_spaces = 0;
968 if (!line_is_short) {
969 multiple_spacing = false;
970 width=spaces=0;
971 for (str = line_begin; str < line_end; ) {
972 str = get_ucs(str, &ch);
973 switch (ch) {
974 case ' ':
975 case 0:
976 if ((str == line_begin) && (prefs.word_mode==WRAP))
977 /* special case: indent the paragraph,
978 * don't count spaces */
979 indent_spaces = par_indent_spaces;
980 else if (!multiple_spacing)
981 spaces++;
982 multiple_spacing = true;
983 break;
984 default:
985 multiple_spacing = false;
986 width += glyph_width(ch);
987 break;
990 if (multiple_spacing) spaces--;
992 if (spaces) {
993 /* total number of spaces to insert between words */
994 extra_spaces = (draw_columns-width)/glyph_width(' ')
995 - indent_spaces;
996 /* number of spaces between each word*/
997 spaces_per_word = extra_spaces / spaces;
998 /* number of words with n+1 spaces (to fill up) */
999 extra_spaces = extra_spaces % spaces;
1000 if (spaces_per_word > 2) { /* too much spacing is awful */
1001 spaces_per_word = 3;
1002 extra_spaces = 0;
1004 } else { /* this doesn't matter much... no spaces anyway */
1005 spaces_per_word = extra_spaces = 0;
1007 } else { /* end of a paragraph: don't fill line */
1008 spaces_per_word = 1;
1009 extra_spaces = 0;
1012 multiple_spacing = false;
1013 for (j=k=spaces=0; j < line_len; j++) {
1014 if (k == MAX_COLUMNS)
1015 break;
1017 c = line_begin[j];
1018 switch (c) {
1019 case ' ':
1020 case 0:
1021 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1022 for (j=0; j<par_indent_spaces; j++)
1023 scratch_buffer[k++] = ' ';
1024 j=0;
1026 else if (!multiple_spacing) {
1027 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1028 scratch_buffer[k++] = ' ';
1029 spaces++;
1031 multiple_spacing = true;
1032 break;
1033 default:
1034 scratch_buffer[k++] = c;
1035 multiple_spacing = false;
1036 break;
1040 if (col != -1) {
1041 scratch_buffer[k] = 0;
1042 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1043 prefs.encoding, k-col);
1044 *endptr = 0;
1047 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1048 if (col != -1)
1049 if (line_width > col) {
1050 str = oldstr = line_begin;
1051 k = col;
1052 width = 0;
1053 while( (width<draw_columns) && (oldstr<line_end) )
1055 oldstr = get_ucs(oldstr, &ch);
1056 if (k > 0) {
1057 k -= glyph_width(ch);
1058 line_begin = oldstr;
1059 } else {
1060 width += glyph_width(ch);
1064 if(prefs.view_mode==WIDE)
1065 endptr = rb->iso_decode(line_begin, utf8_buffer,
1066 prefs.encoding, oldstr-line_begin);
1067 else
1068 endptr = rb->iso_decode(line_begin, utf8_buffer,
1069 prefs.encoding, line_end-line_begin);
1070 *endptr = 0;
1073 if (col != -1 && line_width > col)
1074 #ifdef HAVE_LCD_BITMAP
1075 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1076 #else
1077 rb->lcd_puts(left_col, i, utf8_buffer);
1078 #endif
1079 if (line_width > max_line_len)
1080 max_line_len = line_width;
1082 if (i == 0)
1083 next_line_ptr = line_end;
1085 next_screen_ptr = line_end;
1086 if (BUFFER_OOB(next_screen_ptr))
1087 next_screen_ptr = NULL;
1089 #ifdef HAVE_LCD_BITMAP
1090 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1092 if (prefs.need_scrollbar)
1093 viewer_scrollbar();
1094 #else
1095 next_screen_to_draw_ptr = next_screen_ptr;
1096 #endif
1098 if (col != -1)
1099 rb->lcd_update();
1102 static void viewer_top(void)
1104 /* Read top of file into buffer
1105 and point screen pointer to top */
1106 if (file_pos != 0)
1108 file_pos = 0;
1109 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1110 fill_buffer(0, buffer, buffer_size);
1113 screen_top_ptr = buffer;
1116 static void viewer_bottom(void)
1118 /* Read bottom of file into buffer
1119 and point screen pointer to bottom */
1120 long last_sectors;
1122 if (file_size > buffer_size) {
1123 /* Find last buffer in file, round up to next sector boundary */
1124 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1125 last_sectors /= SMALL_BLOCK_SIZE;
1126 last_sectors *= SMALL_BLOCK_SIZE;
1128 else {
1129 last_sectors = 0;
1132 if (file_pos != last_sectors)
1134 file_pos = last_sectors;
1135 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1136 fill_buffer(last_sectors, buffer, buffer_size);
1139 screen_top_ptr = buffer_end-1;
1142 #ifdef HAVE_LCD_BITMAP
1143 static void init_need_scrollbar(void) {
1144 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1145 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1146 viewer_draw(-1);
1147 prefs.need_scrollbar = NEED_SCROLLBAR();
1148 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1149 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1151 #else
1152 #define init_need_scrollbar()
1153 #endif
1155 static bool viewer_init(void)
1157 #ifdef HAVE_LCD_BITMAP
1159 pf = rb->font_get(FONT_UI);
1161 display_lines = LCD_HEIGHT / pf->height;
1162 draw_columns = display_columns = LCD_WIDTH;
1163 #else
1164 /* REAL fixed pitch :) all chars use up 1 cell */
1165 display_lines = 2;
1166 draw_columns = display_columns = 11;
1167 par_indent_spaces = 2;
1168 #endif
1170 fd = rb->open(file_name, O_RDONLY);
1171 if (fd==-1)
1172 return false;
1174 file_size = rb->filesize(fd);
1175 if (file_size==-1)
1176 return false;
1178 /* Init mac_text value used in processing buffer */
1179 mac_text = false;
1181 return true;
1184 static void viewer_default_settings(void)
1186 prefs.word_mode = WRAP;
1187 prefs.line_mode = NORMAL;
1188 prefs.view_mode = NARROW;
1189 prefs.scroll_mode = PAGE;
1190 #ifdef HAVE_LCD_BITMAP
1191 prefs.page_mode = NO_OVERLAP;
1192 prefs.scrollbar_mode = SB_OFF;
1193 #endif
1194 prefs.autoscroll_speed = 1;
1195 /* Set codepage to system default */
1196 prefs.encoding = rb->global_settings->default_codepage;
1199 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1201 int settings_fd, i;
1202 struct bookmark_file_data *data;
1203 struct bookmarked_file_info this_bookmark;
1205 /* read settings file */
1206 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1207 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1209 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1210 rb->close(settings_fd);
1212 else
1214 /* load default settings if there is no settings file */
1215 viewer_default_settings();
1218 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1220 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1221 data->bookmarked_files_count = 0;
1223 /* read bookmarks if file exists */
1224 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1225 if (settings_fd >= 0)
1227 /* figure out how many items to read */
1228 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1229 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1230 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1231 rb->read(settings_fd, data->bookmarks,
1232 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1233 rb->close(settings_fd);
1236 file_pos = 0;
1237 screen_top_ptr = buffer;
1239 /* check if current file is in list */
1240 for (i=0; i < data->bookmarked_files_count; i++)
1242 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1244 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1245 int screen_top = screen_pos % buffer_size;
1246 file_pos = screen_pos - screen_top;
1247 screen_top_ptr = buffer + screen_top;
1248 break;
1252 this_bookmark.file_position = file_pos;
1253 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1255 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1256 rb->strcpy(this_bookmark.filename,file_name);
1258 /* prevent potential slot overflow */
1259 if (i >= data->bookmarked_files_count)
1261 if (i < MAX_BOOKMARKED_FILES)
1262 data->bookmarked_files_count++;
1263 else
1264 i = MAX_BOOKMARKED_FILES-1;
1267 /* write bookmark file with spare slot in first position
1268 to be filled in by viewer_save_settings */
1269 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1270 if (settings_fd >=0 )
1272 /* write count */
1273 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1275 /* write the current bookmark */
1276 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1278 /* write everything that was before this bookmark */
1279 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1281 rb->close(settings_fd);
1284 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1286 if (BUFFER_OOB(screen_top_ptr))
1288 screen_top_ptr = buffer;
1291 fill_buffer(file_pos, buffer, buffer_size);
1293 /* remember the current position */
1294 start_position = file_pos + screen_top_ptr - buffer;
1296 init_need_scrollbar();
1299 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1301 int settings_fd;
1303 /* save the viewer settings if they have been changed */
1304 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1306 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1308 if (settings_fd >= 0 )
1310 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1311 rb->close(settings_fd);
1315 /* save the bookmark if the position has changed */
1316 if (file_pos + screen_top_ptr - buffer != start_position)
1318 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1320 if (settings_fd >= 0 )
1322 struct bookmarked_file_info b;
1323 b.file_position = file_pos + screen_top_ptr - buffer;
1324 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1325 rb->memset(&b.filename[0],0,MAX_PATH);
1326 rb->strcpy(b.filename,file_name);
1327 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1328 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1329 rb->close(settings_fd);
1334 static void viewer_exit(void *parameter)
1336 (void)parameter;
1338 viewer_save_settings();
1339 rb->close(fd);
1342 static int col_limit(int col)
1344 if (col < 0)
1345 col = 0;
1346 else
1347 if (col > max_line_len - 2*glyph_width('o'))
1348 col = max_line_len - 2*glyph_width('o');
1350 return col;
1353 /* settings helper functions */
1355 static bool encoding_setting(void)
1357 static struct opt_items names[NUM_CODEPAGES];
1358 int idx;
1360 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1362 names[idx].string = rb->get_codepage_name(idx);
1363 names[idx].voice_id = -1;
1366 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1367 sizeof(names) / sizeof(names[0]), NULL);
1370 static bool word_wrap_setting(void)
1372 static const struct opt_items names[] = {
1373 {"On", -1},
1374 {"Off (Chop Words)", -1},
1377 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1378 names, 2, NULL);
1381 static bool line_mode_setting(void)
1383 static const struct opt_items names[] = {
1384 {"Normal", -1},
1385 {"Join Lines", -1},
1386 {"Expand Lines", -1},
1387 #ifdef HAVE_LCD_BITMAP
1388 {"Reflow Lines", -1},
1389 #endif
1392 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1393 sizeof(names) / sizeof(names[0]), NULL);
1396 static bool view_mode_setting(void)
1398 static const struct opt_items names[] = {
1399 {"No (Narrow)", -1},
1400 {"Yes", -1},
1402 bool ret;
1403 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1404 names , 2, NULL);
1405 if (prefs.view_mode == NARROW)
1406 col = 0;
1407 return ret;
1410 static bool scroll_mode_setting(void)
1412 static const struct opt_items names[] = {
1413 {"Scroll by Page", -1},
1414 {"Scroll by Line", -1},
1417 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1418 names, 2, NULL);
1421 #ifdef HAVE_LCD_BITMAP
1422 static bool page_mode_setting(void)
1424 static const struct opt_items names[] = {
1425 {"No", -1},
1426 {"Yes", -1},
1429 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1430 names, 2, NULL);
1433 static bool scrollbar_setting(void)
1435 static const struct opt_items names[] = {
1436 {"Off", -1},
1437 {"On", -1}
1440 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1441 names, 2, NULL);
1443 #endif
1445 static bool autoscroll_speed_setting(void)
1447 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1448 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1451 static bool viewer_options_menu(void)
1453 int m;
1454 bool result;
1456 static const struct menu_item items[] = {
1457 {"Encoding", encoding_setting },
1458 {"Word Wrap", word_wrap_setting },
1459 {"Line Mode", line_mode_setting },
1460 {"Wide View", view_mode_setting },
1461 #ifdef HAVE_LCD_BITMAP
1462 {"Show Scrollbar", scrollbar_setting },
1463 {"Overlap Pages", page_mode_setting },
1464 #endif
1465 {"Scroll Mode", scroll_mode_setting},
1466 {"Auto-Scroll Speed", autoscroll_speed_setting },
1468 m = menu_init(items, sizeof(items) / sizeof(*items),
1469 NULL, NULL, NULL, NULL);
1471 result = menu_run(m);
1472 menu_exit(m);
1473 #ifdef HAVE_LCD_BITMAP
1475 /* Show-scrollbar mode for current view-width mode */
1476 init_need_scrollbar();
1477 #endif
1478 return result;
1481 static void viewer_menu(void)
1483 int m;
1484 int result;
1485 static const struct menu_item items[] = {
1486 {"Quit", NULL },
1487 {"Viewer Options", NULL },
1488 {"Show Playback Menu", NULL },
1489 {"Return", NULL },
1492 m = menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1493 result=menu_show(m);
1494 switch (result)
1496 case 0: /* quit */
1497 menu_exit(m);
1498 viewer_exit(NULL);
1499 done = true;
1500 break;
1501 case 1: /* change settings */
1502 done = viewer_options_menu();
1503 break;
1504 case 2: /* playback control */
1505 playback_control(NULL);
1506 break;
1507 case 3: /* return */
1508 break;
1510 menu_exit(m);
1511 viewer_draw(col);
1514 enum plugin_status plugin_start(const void* file)
1516 int button, i, ok;
1517 int lastbutton = BUTTON_NONE;
1518 bool autoscroll = false;
1519 long old_tick;
1521 old_tick = *rb->current_tick;
1523 /* get the plugin buffer */
1524 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1526 if (!file)
1527 return PLUGIN_ERROR;
1529 file_name = file;
1530 ok = viewer_init();
1531 if (!ok) {
1532 rb->splash(HZ, "Error opening file.");
1533 return PLUGIN_ERROR;
1536 viewer_load_settings(); /* load the preferences and bookmark */
1538 #if LCD_DEPTH > 1
1539 rb->lcd_set_backdrop(NULL);
1540 #endif
1542 viewer_draw(col);
1544 while (!done) {
1546 if(autoscroll)
1548 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1550 viewer_scroll_down();
1551 viewer_draw(col);
1552 old_tick = *rb->current_tick;
1556 button = rb->button_get_w_tmo(HZ/10);
1557 switch (button) {
1558 case VIEWER_MENU:
1559 viewer_menu();
1560 break;
1562 case VIEWER_AUTOSCROLL:
1563 #ifdef VIEWER_AUTOSCROLL_PRE
1564 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1565 break;
1566 #endif
1567 autoscroll = !autoscroll;
1568 break;
1570 case VIEWER_PAGE_UP:
1571 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1572 if (prefs.scroll_mode == PAGE)
1574 /* Page up */
1575 #ifdef HAVE_LCD_BITMAP
1576 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1577 #else
1578 for (i = 0; i < display_lines; i++)
1579 #endif
1580 viewer_scroll_up();
1582 else
1583 viewer_scroll_up();
1584 old_tick = *rb->current_tick;
1585 viewer_draw(col);
1586 break;
1588 case VIEWER_PAGE_DOWN:
1589 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1590 if (prefs.scroll_mode == PAGE)
1592 /* Page down */
1593 if (next_screen_ptr != NULL)
1594 screen_top_ptr = next_screen_to_draw_ptr;
1596 else
1597 viewer_scroll_down();
1598 old_tick = *rb->current_tick;
1599 viewer_draw(col);
1600 break;
1602 case VIEWER_SCREEN_LEFT:
1603 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1604 if (prefs.view_mode == WIDE) {
1605 /* Screen left */
1606 col -= draw_columns;
1607 col = col_limit(col);
1609 else { /* prefs.view_mode == NARROW */
1610 /* Top of file */
1611 viewer_top();
1614 viewer_draw(col);
1615 break;
1617 case VIEWER_SCREEN_RIGHT:
1618 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1619 if (prefs.view_mode == WIDE) {
1620 /* Screen right */
1621 col += draw_columns;
1622 col = col_limit(col);
1624 else { /* prefs.view_mode == NARROW */
1625 /* Bottom of file */
1626 viewer_bottom();
1629 viewer_draw(col);
1630 break;
1632 #ifdef VIEWER_LINE_UP
1633 case VIEWER_LINE_UP:
1634 case VIEWER_LINE_UP | BUTTON_REPEAT:
1635 /* Scroll up one line */
1636 viewer_scroll_up();
1637 old_tick = *rb->current_tick;
1638 viewer_draw(col);
1639 break;
1641 case VIEWER_LINE_DOWN:
1642 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1643 /* Scroll down one line */
1644 if (next_screen_ptr != NULL)
1645 screen_top_ptr = next_line_ptr;
1646 old_tick = *rb->current_tick;
1647 viewer_draw(col);
1648 break;
1649 #endif
1650 #ifdef VIEWER_COLUMN_LEFT
1651 case VIEWER_COLUMN_LEFT:
1652 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1653 if (prefs.view_mode == WIDE) {
1654 /* Scroll left one column */
1655 col -= glyph_width('o');
1656 col = col_limit(col);
1657 viewer_draw(col);
1659 break;
1661 case VIEWER_COLUMN_RIGHT:
1662 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1663 if (prefs.view_mode == WIDE) {
1664 /* Scroll right one column */
1665 col += glyph_width('o');
1666 col = col_limit(col);
1667 viewer_draw(col);
1669 break;
1670 #endif
1672 #ifdef VIEWER_RC_QUIT
1673 case VIEWER_RC_QUIT:
1674 #endif
1675 case VIEWER_QUIT:
1676 viewer_exit(NULL);
1677 done = true;
1678 break;
1680 default:
1681 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1682 == SYS_USB_CONNECTED)
1683 return PLUGIN_USB_CONNECTED;
1684 break;
1686 if (button != BUTTON_NONE)
1688 lastbutton = button;
1689 rb->yield();
1692 return PLUGIN_OK;