fix wrong score recording.
[kugel-rb.git] / apps / plugins / viewer.c
blob9db19d41582b29341ea67cdb911864b5eb936393
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"
26 PLUGIN_HEADER
28 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
29 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
31 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
32 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
33 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
34 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
35 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
36 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
37 #define TOP_SECTOR buffer
38 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
39 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
40 #define SCROLLBAR_WIDTH 6
42 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
44 /* Out-Of-Bounds test for any pointer to data in the buffer */
45 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
47 /* Does the buffer contain the beginning of the file? */
48 #define BUFFER_BOF() (file_pos==0)
50 /* Does the buffer contain the end of the file? */
51 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
53 /* Formula for the endpoint address outside of buffer data */
54 #define BUFFER_END() \
55 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
57 /* Is the entire file being shown in one screen? */
58 #define ONE_SCREEN_FITS_ALL() \
59 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
61 /* Is a scrollbar called for on the current screen? */
62 #define NEED_SCROLLBAR() \
63 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
65 /* variable button definitions */
67 /* Recorder keys */
68 #if CONFIG_KEYPAD == RECORDER_PAD
69 #define VIEWER_QUIT BUTTON_OFF
70 #define VIEWER_PAGE_UP BUTTON_UP
71 #define VIEWER_PAGE_DOWN BUTTON_DOWN
72 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
73 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
74 #define VIEWER_MENU BUTTON_F1
75 #define VIEWER_AUTOSCROLL BUTTON_PLAY
76 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
77 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
78 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
79 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
81 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
82 #define VIEWER_QUIT BUTTON_OFF
83 #define VIEWER_PAGE_UP BUTTON_UP
84 #define VIEWER_PAGE_DOWN BUTTON_DOWN
85 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
86 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
87 #define VIEWER_MENU BUTTON_F1
88 #define VIEWER_AUTOSCROLL BUTTON_SELECT
89 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
90 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
91 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
92 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
94 /* Ondio keys */
95 #elif CONFIG_KEYPAD == ONDIO_PAD
96 #define VIEWER_QUIT BUTTON_OFF
97 #define VIEWER_PAGE_UP BUTTON_UP
98 #define VIEWER_PAGE_DOWN BUTTON_DOWN
99 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
100 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
101 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
102 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
103 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
105 /* Player keys */
106 #elif CONFIG_KEYPAD == PLAYER_PAD
107 #define VIEWER_QUIT BUTTON_STOP
108 #define VIEWER_PAGE_UP BUTTON_LEFT
109 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
110 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
111 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
112 #define VIEWER_MENU BUTTON_MENU
113 #define VIEWER_AUTOSCROLL BUTTON_PLAY
115 /* iRiver H1x0 && H3x0 keys */
116 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
117 (CONFIG_KEYPAD == IRIVER_H300_PAD)
118 #define VIEWER_QUIT BUTTON_OFF
119 #define VIEWER_PAGE_UP BUTTON_UP
120 #define VIEWER_PAGE_DOWN BUTTON_DOWN
121 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
122 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
123 #define VIEWER_MENU BUTTON_MODE
124 #define VIEWER_AUTOSCROLL BUTTON_SELECT
125 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
126 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
127 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
128 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
130 #define VIEWER_RC_QUIT BUTTON_RC_STOP
132 /* iPods */
133 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
135 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
136 #define VIEWER_QUIT_PRE BUTTON_SELECT
137 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
138 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
139 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
140 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
141 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
142 #define VIEWER_MENU BUTTON_MENU
143 #define VIEWER_AUTOSCROLL BUTTON_PLAY
145 /* iFP7xx keys */
146 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
147 #define VIEWER_QUIT BUTTON_PLAY
148 #define VIEWER_PAGE_UP BUTTON_UP
149 #define VIEWER_PAGE_DOWN BUTTON_DOWN
150 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
151 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
152 #define VIEWER_MENU BUTTON_MODE
153 #define VIEWER_AUTOSCROLL BUTTON_SELECT
155 /* iAudio X5 keys */
156 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
157 #define VIEWER_QUIT BUTTON_POWER
158 #define VIEWER_PAGE_UP BUTTON_UP
159 #define VIEWER_PAGE_DOWN BUTTON_DOWN
160 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
161 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
162 #define VIEWER_MENU BUTTON_SELECT
163 #define VIEWER_AUTOSCROLL BUTTON_PLAY
165 /* GIGABEAT keys */
166 #elif CONFIG_KEYPAD == GIGABEAT_PAD
167 #define VIEWER_QUIT BUTTON_POWER
168 #define VIEWER_PAGE_UP BUTTON_UP
169 #define VIEWER_PAGE_DOWN BUTTON_DOWN
170 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
171 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
172 #define VIEWER_MENU BUTTON_MENU
173 #define VIEWER_AUTOSCROLL BUTTON_A
175 /* Sansa E200 keys */
176 #elif CONFIG_KEYPAD == SANSA_E200_PAD
177 #define VIEWER_QUIT BUTTON_POWER
178 #define VIEWER_PAGE_UP BUTTON_UP
179 #define VIEWER_PAGE_DOWN BUTTON_DOWN
180 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
181 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
182 #define VIEWER_MENU BUTTON_SELECT
183 #define VIEWER_AUTOSCROLL BUTTON_REC
184 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
185 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
187 /* Sansa Fuze keys */
188 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
189 #define VIEWER_QUIT (BUTTON_HOME|BUTTON_REPEAT)
190 #define VIEWER_PAGE_UP BUTTON_UP
191 #define VIEWER_PAGE_DOWN BUTTON_DOWN
192 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
193 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
194 #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT
195 #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
196 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
197 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
199 /* Sansa C200 keys */
200 #elif CONFIG_KEYPAD == SANSA_C200_PAD
201 #define VIEWER_QUIT BUTTON_POWER
202 #define VIEWER_PAGE_UP BUTTON_VOL_UP
203 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
204 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
205 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
206 #define VIEWER_MENU BUTTON_SELECT
207 #define VIEWER_AUTOSCROLL BUTTON_REC
208 #define VIEWER_LINE_UP BUTTON_UP
209 #define VIEWER_LINE_DOWN BUTTON_DOWN
211 /* Sansa Clip keys */
212 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
213 #define VIEWER_QUIT BUTTON_POWER
214 #define VIEWER_PAGE_UP BUTTON_VOL_UP
215 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
216 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
217 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
218 #define VIEWER_MENU BUTTON_SELECT
219 #define VIEWER_AUTOSCROLL BUTTON_HOME
220 #define VIEWER_LINE_UP BUTTON_UP
221 #define VIEWER_LINE_DOWN BUTTON_DOWN
223 /* Sansa M200 keys */
224 #elif CONFIG_KEYPAD == SANSA_M200_PAD
225 #define VIEWER_QUIT BUTTON_POWER
226 #define VIEWER_PAGE_UP BUTTON_VOL_UP
227 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
228 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
229 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
230 #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP)
231 #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
232 #define VIEWER_LINE_UP BUTTON_UP
233 #define VIEWER_LINE_DOWN BUTTON_DOWN
235 /* iriver H10 keys */
236 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
237 #define VIEWER_QUIT BUTTON_POWER
238 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
239 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
240 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
241 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
242 #define VIEWER_MENU BUTTON_REW
243 #define VIEWER_AUTOSCROLL BUTTON_PLAY
245 /*M-Robe 500 keys */
246 #elif CONFIG_KEYPAD == MROBE500_PAD
247 #define VIEWER_QUIT BUTTON_POWER
248 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
249 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
250 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
251 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
252 #define VIEWER_MENU BUTTON_RC_HEART
253 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
255 /*Gigabeat S keys */
256 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
257 #define VIEWER_QUIT BUTTON_BACK
258 #define VIEWER_PAGE_UP BUTTON_PREV
259 #define VIEWER_PAGE_DOWN BUTTON_NEXT
260 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
261 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
262 #define VIEWER_MENU BUTTON_MENU
263 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
264 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
265 #define VIEWER_LINE_UP BUTTON_UP
266 #define VIEWER_LINE_DOWN BUTTON_DOWN
267 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
268 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
270 /*M-Robe 100 keys */
271 #elif CONFIG_KEYPAD == MROBE100_PAD
272 #define VIEWER_QUIT BUTTON_POWER
273 #define VIEWER_PAGE_UP BUTTON_UP
274 #define VIEWER_PAGE_DOWN BUTTON_DOWN
275 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
276 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
277 #define VIEWER_MENU BUTTON_MENU
278 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
280 /* iAUdio M3 keys */
281 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
282 #define VIEWER_QUIT BUTTON_RC_REC
283 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
284 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
285 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
286 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
287 #define VIEWER_MENU BUTTON_RC_MENU
288 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
289 #define VIEWER_RC_QUIT BUTTON_REC
291 /* Cowon D2 keys */
292 #elif CONFIG_KEYPAD == COWOND2_PAD
293 #define VIEWER_QUIT BUTTON_POWER
294 #define VIEWER_MENU BUTTON_MENU
296 #elif CONFIG_KEYPAD == IAUDIO67_PAD
297 #define VIEWER_QUIT BUTTON_POWER
298 #define VIEWER_PAGE_UP BUTTON_VOLUP
299 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
300 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
301 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
302 #define VIEWER_MENU BUTTON_MENU
303 #define VIEWER_AUTOSCROLL BUTTON_PLAY
304 #define VIEWER_RC_QUIT BUTTON_STOP
306 /* Creative Zen Vision:M keys */
307 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
308 #define VIEWER_QUIT BUTTON_BACK
309 #define VIEWER_PAGE_UP BUTTON_UP
310 #define VIEWER_PAGE_DOWN BUTTON_DOWN
311 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
312 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
313 #define VIEWER_MENU BUTTON_MENU
314 #define VIEWER_AUTOSCROLL BUTTON_SELECT
316 /* Philips HDD1630 keys */
317 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
318 #define VIEWER_QUIT BUTTON_POWER
319 #define VIEWER_PAGE_UP BUTTON_UP
320 #define VIEWER_PAGE_DOWN BUTTON_DOWN
321 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
322 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
323 #define VIEWER_MENU BUTTON_MENU
324 #define VIEWER_AUTOSCROLL BUTTON_VIEW
326 /* Onda VX747 keys */
327 #elif CONFIG_KEYPAD == ONDAVX747_PAD
328 #define VIEWER_QUIT BUTTON_POWER
329 #define VIEWER_MENU BUTTON_MENU
331 #else
332 #error No keymap defined!
333 #endif
335 #ifdef HAVE_TOUCHSCREEN
336 #ifndef VIEWER_QUIT
337 #define VIEWER_QUIT BUTTON_TOPLEFT
338 #endif
339 #ifndef VIEWER_PAGE_UP
340 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
341 #endif
342 #ifndef VIEWER_PAGE_DOWN
343 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
344 #endif
345 #ifndef VIEWER_SCREEN_LEFT
346 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
347 #endif
348 #ifndef VIEWER_SCREEN_RIGHT
349 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
350 #endif
351 #ifndef VIEWER_MENU
352 #define VIEWER_MENU BUTTON_TOPRIGHT
353 #endif
354 #ifndef VIEWER_AUTOSCROLL
355 #define VIEWER_AUTOSCROLL BUTTON_CENTER
356 #endif
357 #endif
359 /* stuff for the bookmarking */
360 struct bookmarked_file_info {
361 long file_position;
362 int top_ptr_pos;
363 char filename[MAX_PATH];
366 struct bookmark_file_data {
367 signed int bookmarked_files_count;
368 struct bookmarked_file_info bookmarks[];
371 struct preferences {
372 enum {
373 WRAP=0,
374 CHOP,
375 } word_mode;
377 enum {
378 NORMAL=0,
379 JOIN,
380 EXPAND,
381 REFLOW, /* won't be set on charcell LCD, must be last */
382 } line_mode;
384 enum {
385 NARROW=0,
386 WIDE,
387 } view_mode;
389 enum codepages encoding;
391 #ifdef HAVE_LCD_BITMAP
392 enum {
393 SB_OFF=0,
394 SB_ON,
395 } scrollbar_mode;
396 bool need_scrollbar;
398 enum {
399 NO_OVERLAP=0,
400 OVERLAP,
401 } page_mode;
402 #endif /* HAVE_LCD_BITMAP */
404 enum {
405 PAGE=0,
406 LINE,
407 } scroll_mode;
409 int autoscroll_speed;
413 struct preferences prefs;
414 struct preferences old_prefs;
416 static unsigned char *buffer;
417 static long buffer_size;
418 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
419 static int display_columns; /* number of (pixel) columns on the display */
420 static int display_lines; /* number of lines on the display */
421 static int draw_columns; /* number of (pixel) columns available for text */
422 static int par_indent_spaces; /* number of spaces to indent first paragraph */
423 static int fd;
424 static const char *file_name;
425 static long file_size;
426 static long start_position; /* position in the file after the viewer is started */
427 static bool mac_text;
428 static long file_pos; /* Position of the top of the buffer in the file */
429 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
430 static int max_line_len;
431 static unsigned char *screen_top_ptr;
432 static unsigned char *next_screen_ptr;
433 static unsigned char *next_screen_to_draw_ptr;
434 static unsigned char *next_line_ptr;
435 #ifdef HAVE_LCD_BITMAP
436 static struct font *pf;
437 #endif
440 int glyph_width(int ch)
442 if (ch == 0)
443 ch = ' ';
445 #ifdef HAVE_LCD_BITMAP
446 return rb->font_get_width(pf, ch);
447 #else
448 return 1;
449 #endif
452 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
454 unsigned char utf8_tmp[6];
455 int count;
457 if (prefs.encoding == UTF_8)
458 return (unsigned char*)rb->utf8decode(str, ch);
460 count = BUFFER_OOB(str+2)? 1:2;
461 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
462 rb->utf8decode(utf8_tmp, ch);
464 #ifdef HAVE_LCD_BITMAP
465 if (prefs.encoding >= SJIS && *str >= 0x80
466 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
467 return (unsigned char*)str+2;
468 else
469 #endif
470 return (unsigned char*)str+1;
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 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
1452 NULL, NULL, Icon_NOICON);
1453 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
1454 NULL, NULL, Icon_NOICON);
1455 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
1456 NULL, NULL, Icon_NOICON);
1457 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
1458 NULL, NULL, Icon_NOICON);
1459 #ifdef HAVE_LCD_BITMAP
1460 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
1461 NULL, NULL, Icon_NOICON);
1462 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
1463 NULL, NULL, Icon_NOICON);
1464 #endif
1465 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
1466 NULL, NULL, Icon_NOICON);
1467 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
1468 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
1469 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
1470 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
1471 #ifdef HAVE_LCD_BITMAP
1472 &scrollbar_item, &page_mode_item,
1473 #endif
1474 &scroll_mode_item, &autoscroll_speed_item);
1476 static bool viewer_options_menu(void)
1478 bool result;
1479 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
1481 #ifdef HAVE_LCD_BITMAP
1482 /* Show-scrollbar mode for current view-width mode */
1483 init_need_scrollbar();
1484 #endif
1485 return result;
1488 static void viewer_menu(void)
1490 int result;
1491 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
1492 "Return", "Viewer Options",
1493 "Show Playback Menu", "Quit");
1495 result = rb->do_menu(&menu, NULL, NULL, false);
1496 switch (result)
1498 case 0: /* return */
1499 break;
1500 case 1: /* change settings */
1501 done = viewer_options_menu();
1502 break;
1503 case 2: /* playback control */
1504 playback_control(NULL);
1505 break;
1506 case 3: /* quit */
1507 viewer_exit(NULL);
1508 done = true;
1509 break;
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;