Minor corrections to the .colours file editing; added .colours to the list of support...
[kugel-rb.git] / apps / plugins / viewer.c
blobf01afbba8c500a29cdeb965823a15a272f332bf7
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_POWER
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 #else
328 #error No keymap defined!
329 #endif
331 #ifdef HAVE_TOUCHSCREEN
332 #ifndef VIEWER_QUIT
333 #define VIEWER_QUIT BUTTON_TOPLEFT
334 #endif
335 #ifndef VIEWER_PAGE_UP
336 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
337 #endif
338 #ifndef VIEWER_PAGE_DOWN
339 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
340 #endif
341 #ifndef VIEWER_SCREEN_LEFT
342 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
343 #endif
344 #ifndef VIEWER_SCREEN_RIGHT
345 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
346 #endif
347 #ifndef VIEWER_MENU
348 #define VIEWER_MENU BUTTON_TOPRIGHT
349 #endif
350 #ifndef VIEWER_AUTOSCROLL
351 #define VIEWER_AUTOSCROLL BUTTON_CENTER
352 #endif
353 #endif
355 /* stuff for the bookmarking */
356 struct bookmarked_file_info {
357 long file_position;
358 int top_ptr_pos;
359 char filename[MAX_PATH];
362 struct bookmark_file_data {
363 signed int bookmarked_files_count;
364 struct bookmarked_file_info bookmarks[];
367 struct preferences {
368 enum {
369 WRAP=0,
370 CHOP,
371 } word_mode;
373 enum {
374 NORMAL=0,
375 JOIN,
376 EXPAND,
377 REFLOW, /* won't be set on charcell LCD, must be last */
378 } line_mode;
380 enum {
381 NARROW=0,
382 WIDE,
383 } view_mode;
385 enum codepages encoding;
387 #ifdef HAVE_LCD_BITMAP
388 enum {
389 SB_OFF=0,
390 SB_ON,
391 } scrollbar_mode;
392 bool need_scrollbar;
394 enum {
395 NO_OVERLAP=0,
396 OVERLAP,
397 } page_mode;
398 #endif /* HAVE_LCD_BITMAP */
400 enum {
401 PAGE=0,
402 LINE,
403 } scroll_mode;
405 int autoscroll_speed;
409 struct preferences prefs;
410 struct preferences old_prefs;
412 static unsigned char *buffer;
413 static long buffer_size;
414 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
415 static int display_columns; /* number of (pixel) columns on the display */
416 static int display_lines; /* number of lines on the display */
417 static int draw_columns; /* number of (pixel) columns available for text */
418 static int par_indent_spaces; /* number of spaces to indent first paragraph */
419 static int fd;
420 static const char *file_name;
421 static long file_size;
422 static long start_position; /* position in the file after the viewer is started */
423 static bool mac_text;
424 static long file_pos; /* Position of the top of the buffer in the file */
425 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
426 static int max_line_len;
427 static unsigned char *screen_top_ptr;
428 static unsigned char *next_screen_ptr;
429 static unsigned char *next_screen_to_draw_ptr;
430 static unsigned char *next_line_ptr;
431 #ifdef HAVE_LCD_BITMAP
432 static struct font *pf;
433 #endif
436 int glyph_width(int ch)
438 if (ch == 0)
439 ch = ' ';
441 #ifdef HAVE_LCD_BITMAP
442 return rb->font_get_width(pf, ch);
443 #else
444 return 1;
445 #endif
448 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
450 unsigned char utf8_tmp[6];
451 int count;
453 if (prefs.encoding == UTF_8)
454 return (unsigned char*)rb->utf8decode(str, ch);
456 count = BUFFER_OOB(str+2)? 1:2;
457 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
458 rb->utf8decode(utf8_tmp, ch);
460 #ifdef HAVE_LCD_BITMAP
461 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
462 return (unsigned char*)str+1;
463 else
464 #endif
465 return (unsigned char*)str+2;
468 bool done = false;
469 int col = 0;
471 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
472 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
473 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
474 static unsigned char* crop_at_width(const unsigned char* p)
476 int k,width;
477 unsigned short ch;
478 const unsigned char *oldp = p;
480 k=width=0;
482 while (LINE_IS_NOT_FULL) {
483 oldp = p;
484 p = get_ucs(p, &ch);
485 ADVANCE_COUNTERS(ch);
488 return (unsigned char*)oldp;
491 static unsigned char* find_first_feed(const unsigned char* p, int size)
493 int i;
495 for (i=0; i < size; i++)
496 if (p[i] == 0)
497 return (unsigned char*) p+i;
499 return NULL;
502 static unsigned char* find_last_feed(const unsigned char* p, int size)
504 int i;
506 for (i=size-1; i>=0; i--)
507 if (p[i] == 0)
508 return (unsigned char*) p+i;
510 return NULL;
513 static unsigned char* find_last_space(const unsigned char* p, int size)
515 int i, j, k;
517 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
519 if (!BUFFER_OOB(&p[size]))
520 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
521 if (p[size] == line_break[j])
522 return (unsigned char*) p+size;
524 for (i=size-1; i>=0; i--)
525 for (j=k; j < (int) sizeof(line_break); j++)
527 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
528 if (p[i] == line_break[j])
529 return (unsigned char*) p+i;
532 return NULL;
535 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
537 const unsigned char *next_line = NULL;
538 int size, i, j, k, width, search_len, spaces, newlines;
539 bool first_chars;
540 unsigned char c;
542 if (is_short != NULL)
543 *is_short = true;
545 if BUFFER_OOB(cur_line)
546 return NULL;
548 if (prefs.view_mode == WIDE) {
549 search_len = MAX_WIDTH;
551 else { /* prefs.view_mode == NARROW */
552 search_len = crop_at_width(cur_line) - cur_line;
555 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
557 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
558 /* Need to scan ahead and possibly increase search_len and size,
559 or possibly set next_line at second hard return in a row. */
560 next_line = NULL;
561 first_chars=true;
562 for (j=k=width=spaces=newlines=0; ; j++) {
563 if (BUFFER_OOB(cur_line+j))
564 return NULL;
565 if (LINE_IS_FULL) {
566 size = search_len = j;
567 break;
570 c = cur_line[j];
571 switch (c) {
572 case ' ':
573 if (prefs.line_mode == REFLOW) {
574 if (newlines > 0) {
575 size = j;
576 next_line = cur_line + size;
577 return (unsigned char*) next_line;
579 if (j==0) /* i=1 is intentional */
580 for (i=0; i<par_indent_spaces; i++)
581 ADVANCE_COUNTERS(' ');
583 if (!first_chars) spaces++;
584 break;
586 case 0:
587 if (newlines > 0) {
588 size = j;
589 next_line = cur_line + size - spaces;
590 if (next_line != cur_line)
591 return (unsigned char*) next_line;
592 break;
595 newlines++;
596 size += spaces -1;
597 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
598 return NULL;
599 search_len = size;
600 spaces = first_chars? 0:1;
601 break;
603 default:
604 if (prefs.line_mode==JOIN || newlines>0) {
605 while (spaces) {
606 spaces--;
607 ADVANCE_COUNTERS(' ');
608 if (LINE_IS_FULL) {
609 size = search_len = j;
610 break;
613 newlines=0;
614 } else if (spaces) {
615 /* REFLOW, multiple spaces between words: count only
616 * one. If more are needed, they will be added
617 * while drawing. */
618 search_len = size;
619 spaces=0;
620 ADVANCE_COUNTERS(' ');
621 if (LINE_IS_FULL) {
622 size = search_len = j;
623 break;
626 first_chars = false;
627 ADVANCE_COUNTERS(c);
628 break;
632 else {
633 /* find first hard return */
634 next_line = find_first_feed(cur_line, size);
637 if (next_line == NULL)
638 if (size == search_len) {
639 if (prefs.word_mode == WRAP) /* Find last space */
640 next_line = find_last_space(cur_line, size);
642 if (next_line == NULL)
643 next_line = crop_at_width(cur_line);
644 else
645 if (prefs.word_mode == WRAP)
646 for (i=0;
647 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
648 i++)
649 next_line++;
652 if (prefs.line_mode == EXPAND)
653 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
654 if (next_line[0] == 0)
655 if (next_line != cur_line)
656 return (unsigned char*) next_line;
658 /* If next_line is pointing to a zero, increment it; i.e.,
659 leave the terminator at the end of cur_line. If pointing
660 to a hyphen, increment only if there is room to display
661 the hyphen on current line (won't apply in WIDE mode,
662 since it's guarenteed there won't be room). */
663 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
664 if (next_line[0] == 0)/* ||
665 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
666 next_line++;
668 if (BUFFER_OOB(next_line))
669 return NULL;
671 if (is_short)
672 *is_short = false;
674 return (unsigned char*) next_line;
677 static unsigned char* find_prev_line(const unsigned char* cur_line)
679 const unsigned char *prev_line = NULL;
680 const unsigned char *p;
682 if BUFFER_OOB(cur_line)
683 return NULL;
685 /* To wrap consistently at the same places, we must
686 start with a known hard return, then work downwards.
687 We can either search backwards for a hard return,
688 or simply start wrapping downwards from top of buffer.
689 If current line is not near top of buffer, this is
690 a file with long lines (paragraphs). We would need to
691 read earlier sectors before we could decide how to
692 properly wrap the lines above the current line, but
693 it probably is not worth the disk access. Instead,
694 start with top of buffer and wrap down from there.
695 This may result in some lines wrapping at different
696 points from where they wrap when scrolling down.
697 If buffer is at top of file, start at top of buffer. */
699 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
700 prev_line = p = NULL;
701 else
702 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
703 /* Null means no line feeds in buffer above current line. */
705 if (prev_line == NULL)
706 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
707 prev_line = p = buffer;
708 /* (else return NULL and read previous block) */
710 /* Wrap downwards until too far, then use the one before. */
711 while (p < cur_line && p != NULL) {
712 prev_line = p;
713 p = find_next_line(prev_line, NULL);
716 if (BUFFER_OOB(prev_line))
717 return NULL;
719 return (unsigned char*) prev_line;
722 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
724 /* Read from file and preprocess the data */
725 /* To minimize disk access, always read on sector boundaries */
726 unsigned numread, i;
727 bool found_CR = false;
729 rb->lseek(fd, pos, SEEK_SET);
730 numread = rb->read(fd, buf, size);
731 rb->button_clear_queue(); /* clear button queue */
733 for(i = 0; i < numread; i++) {
734 switch(buf[i]) {
735 case '\r':
736 if (mac_text) {
737 buf[i] = 0;
739 else {
740 buf[i] = ' ';
741 found_CR = true;
743 break;
745 case '\n':
746 buf[i] = 0;
747 found_CR = false;
748 break;
750 case 0: /* No break between case 0 and default, intentionally */
751 buf[i] = ' ';
752 default:
753 if (found_CR) {
754 buf[i - 1] = 0;
755 found_CR = false;
756 mac_text = true;
758 break;
763 static int read_and_synch(int direction)
765 /* Read next (or prev) block, and reposition global pointers. */
766 /* direction: 1 for down (i.e., further into file), -1 for up */
767 int move_size, move_vector, offset;
768 unsigned char *fill_buf;
770 if (direction == -1) /* up */ {
771 move_size = SMALL_BLOCK_SIZE;
772 offset = 0;
773 fill_buf = TOP_SECTOR;
774 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
775 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
777 else /* down */ {
778 if (prefs.view_mode == WIDE) {
779 /* WIDE mode needs more buffer so we have to read smaller blocks */
780 move_size = SMALL_BLOCK_SIZE;
781 offset = LARGE_BLOCK_SIZE;
782 fill_buf = BOTTOM_SECTOR;
783 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
784 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
786 else {
787 move_size = LARGE_BLOCK_SIZE;
788 offset = SMALL_BLOCK_SIZE;
789 fill_buf = MID_SECTOR;
790 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
793 move_vector = direction * move_size;
794 screen_top_ptr -= move_vector;
795 file_pos += move_vector;
796 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
797 fill_buffer(file_pos + offset, fill_buf, move_size);
798 return move_vector;
801 static void viewer_scroll_up(void)
803 unsigned char *p;
805 p = find_prev_line(screen_top_ptr);
806 if (p == NULL && !BUFFER_BOF()) {
807 read_and_synch(-1);
808 p = find_prev_line(screen_top_ptr);
810 if (p != NULL)
811 screen_top_ptr = p;
814 static void viewer_scroll_down(void)
816 if (next_screen_ptr != NULL)
817 screen_top_ptr = next_line_ptr;
820 #ifdef HAVE_LCD_BITMAP
821 static void viewer_scrollbar(void) {
822 int items, min_shown, max_shown;
824 items = (int) file_size; /* (SH1 int is same as long) */
825 min_shown = (int) file_pos + (screen_top_ptr - buffer);
827 if (next_screen_ptr == NULL)
828 max_shown = items;
829 else
830 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
832 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
833 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
835 #endif
837 static void viewer_draw(int col)
839 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
840 int width, extra_spaces, indent_spaces, spaces_per_word;
841 bool multiple_spacing, line_is_short;
842 unsigned short ch;
843 unsigned char *str, *oldstr;
844 unsigned char *line_begin;
845 unsigned char *line_end;
846 unsigned char c;
847 unsigned char scratch_buffer[MAX_COLUMNS + 1];
848 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
849 unsigned char *endptr;
851 /* If col==-1 do all calculations but don't display */
852 if (col != -1) {
853 #ifdef HAVE_LCD_BITMAP
854 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
855 #else
856 left_col = 0;
857 #endif
858 rb->lcd_clear_display();
860 max_line_len = 0;
861 line_begin = line_end = screen_top_ptr;
863 for (i = 0; i < display_lines; i++) {
864 if (BUFFER_OOB(line_end))
865 break; /* Happens after display last line at BUFFER_EOF() */
867 line_begin = line_end;
868 line_end = find_next_line(line_begin, &line_is_short);
870 if (line_end == NULL) {
871 if (BUFFER_EOF()) {
872 if (i < display_lines - 1 && !BUFFER_BOF()) {
873 if (col != -1)
874 rb->lcd_clear_display();
876 for (; i < display_lines - 1; i++)
877 viewer_scroll_up();
879 line_begin = line_end = screen_top_ptr;
880 i = -1;
881 continue;
883 else {
884 line_end = buffer_end;
887 else {
888 resynch_move = read_and_synch(1); /* Read block & move ptrs */
889 line_begin -= resynch_move;
890 if (i > 0)
891 next_line_ptr -= resynch_move;
893 line_end = find_next_line(line_begin, NULL);
894 if (line_end == NULL) /* Should not really happen */
895 break;
898 line_len = line_end - line_begin;
900 /* calculate line_len */
901 str = oldstr = line_begin;
902 j = -1;
903 while (str < line_end) {
904 oldstr = str;
905 str = crop_at_width(str);
906 j++;
908 line_width = j*draw_columns;
909 while (oldstr < line_end) {
910 oldstr = get_ucs(oldstr, &ch);
911 line_width += glyph_width(ch);
914 if (prefs.line_mode == JOIN) {
915 if (line_begin[0] == 0) {
916 line_begin++;
917 if (prefs.word_mode == CHOP)
918 line_end++;
919 else
920 line_len--;
922 for (j=k=spaces=0; j < line_len; j++) {
923 if (k == MAX_COLUMNS)
924 break;
926 c = line_begin[j];
927 switch (c) {
928 case ' ':
929 spaces++;
930 break;
931 case 0:
932 spaces = 0;
933 scratch_buffer[k++] = ' ';
934 break;
935 default:
936 while (spaces) {
937 spaces--;
938 scratch_buffer[k++] = ' ';
939 if (k == MAX_COLUMNS - 1)
940 break;
942 scratch_buffer[k++] = c;
943 break;
946 if (col != -1) {
947 scratch_buffer[k] = 0;
948 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
949 prefs.encoding, draw_columns/glyph_width('i'));
950 *endptr = 0;
953 else if (prefs.line_mode == REFLOW) {
954 if (line_begin[0] == 0) {
955 line_begin++;
956 if (prefs.word_mode == CHOP)
957 line_end++;
958 else
959 line_len--;
962 indent_spaces = 0;
963 if (!line_is_short) {
964 multiple_spacing = false;
965 width=spaces=0;
966 for (str = line_begin; str < line_end; ) {
967 str = get_ucs(str, &ch);
968 switch (ch) {
969 case ' ':
970 case 0:
971 if ((str == line_begin) && (prefs.word_mode==WRAP))
972 /* special case: indent the paragraph,
973 * don't count spaces */
974 indent_spaces = par_indent_spaces;
975 else if (!multiple_spacing)
976 spaces++;
977 multiple_spacing = true;
978 break;
979 default:
980 multiple_spacing = false;
981 width += glyph_width(ch);
982 break;
985 if (multiple_spacing) spaces--;
987 if (spaces) {
988 /* total number of spaces to insert between words */
989 extra_spaces = (draw_columns-width)/glyph_width(' ')
990 - indent_spaces;
991 /* number of spaces between each word*/
992 spaces_per_word = extra_spaces / spaces;
993 /* number of words with n+1 spaces (to fill up) */
994 extra_spaces = extra_spaces % spaces;
995 if (spaces_per_word > 2) { /* too much spacing is awful */
996 spaces_per_word = 3;
997 extra_spaces = 0;
999 } else { /* this doesn't matter much... no spaces anyway */
1000 spaces_per_word = extra_spaces = 0;
1002 } else { /* end of a paragraph: don't fill line */
1003 spaces_per_word = 1;
1004 extra_spaces = 0;
1007 multiple_spacing = false;
1008 for (j=k=spaces=0; j < line_len; j++) {
1009 if (k == MAX_COLUMNS)
1010 break;
1012 c = line_begin[j];
1013 switch (c) {
1014 case ' ':
1015 case 0:
1016 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1017 for (j=0; j<par_indent_spaces; j++)
1018 scratch_buffer[k++] = ' ';
1019 j=0;
1021 else if (!multiple_spacing) {
1022 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1023 scratch_buffer[k++] = ' ';
1024 spaces++;
1026 multiple_spacing = true;
1027 break;
1028 default:
1029 scratch_buffer[k++] = c;
1030 multiple_spacing = false;
1031 break;
1035 if (col != -1) {
1036 scratch_buffer[k] = 0;
1037 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1038 prefs.encoding, k-col);
1039 *endptr = 0;
1042 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1043 if (col != -1)
1044 if (line_width > col) {
1045 str = oldstr = line_begin;
1046 k = col;
1047 width = 0;
1048 while( (width<draw_columns) && (oldstr<line_end) )
1050 oldstr = get_ucs(oldstr, &ch);
1051 if (k > 0) {
1052 k -= glyph_width(ch);
1053 line_begin = oldstr;
1054 } else {
1055 width += glyph_width(ch);
1059 if(prefs.view_mode==WIDE)
1060 endptr = rb->iso_decode(line_begin, utf8_buffer,
1061 prefs.encoding, oldstr-line_begin);
1062 else
1063 endptr = rb->iso_decode(line_begin, utf8_buffer,
1064 prefs.encoding, line_end-line_begin);
1065 *endptr = 0;
1068 if (col != -1 && line_width > col)
1069 #ifdef HAVE_LCD_BITMAP
1070 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1071 #else
1072 rb->lcd_puts(left_col, i, utf8_buffer);
1073 #endif
1074 if (line_width > max_line_len)
1075 max_line_len = line_width;
1077 if (i == 0)
1078 next_line_ptr = line_end;
1080 next_screen_ptr = line_end;
1081 if (BUFFER_OOB(next_screen_ptr))
1082 next_screen_ptr = NULL;
1084 #ifdef HAVE_LCD_BITMAP
1085 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1087 if (prefs.need_scrollbar)
1088 viewer_scrollbar();
1089 #else
1090 next_screen_to_draw_ptr = next_screen_ptr;
1091 #endif
1093 if (col != -1)
1094 rb->lcd_update();
1097 static void viewer_top(void)
1099 /* Read top of file into buffer
1100 and point screen pointer to top */
1101 if (file_pos != 0)
1103 file_pos = 0;
1104 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1105 fill_buffer(0, buffer, buffer_size);
1108 screen_top_ptr = buffer;
1111 static void viewer_bottom(void)
1113 /* Read bottom of file into buffer
1114 and point screen pointer to bottom */
1115 long last_sectors;
1117 if (file_size > buffer_size) {
1118 /* Find last buffer in file, round up to next sector boundary */
1119 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1120 last_sectors /= SMALL_BLOCK_SIZE;
1121 last_sectors *= SMALL_BLOCK_SIZE;
1123 else {
1124 last_sectors = 0;
1127 if (file_pos != last_sectors)
1129 file_pos = last_sectors;
1130 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1131 fill_buffer(last_sectors, buffer, buffer_size);
1134 screen_top_ptr = buffer_end-1;
1137 #ifdef HAVE_LCD_BITMAP
1138 static void init_need_scrollbar(void) {
1139 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1140 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1141 viewer_draw(-1);
1142 prefs.need_scrollbar = NEED_SCROLLBAR();
1143 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1144 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1146 #else
1147 #define init_need_scrollbar()
1148 #endif
1150 static bool viewer_init(void)
1152 #ifdef HAVE_LCD_BITMAP
1154 pf = rb->font_get(FONT_UI);
1156 display_lines = LCD_HEIGHT / pf->height;
1157 draw_columns = display_columns = LCD_WIDTH;
1158 #else
1159 /* REAL fixed pitch :) all chars use up 1 cell */
1160 display_lines = 2;
1161 draw_columns = display_columns = 11;
1162 par_indent_spaces = 2;
1163 #endif
1165 fd = rb->open(file_name, O_RDONLY);
1166 if (fd==-1)
1167 return false;
1169 file_size = rb->filesize(fd);
1170 if (file_size==-1)
1171 return false;
1173 /* Init mac_text value used in processing buffer */
1174 mac_text = false;
1176 return true;
1179 static void viewer_default_settings(void)
1181 prefs.word_mode = WRAP;
1182 prefs.line_mode = NORMAL;
1183 prefs.view_mode = NARROW;
1184 prefs.scroll_mode = PAGE;
1185 #ifdef HAVE_LCD_BITMAP
1186 prefs.page_mode = NO_OVERLAP;
1187 prefs.scrollbar_mode = SB_OFF;
1188 #endif
1189 prefs.autoscroll_speed = 1;
1190 /* Set codepage to system default */
1191 prefs.encoding = rb->global_settings->default_codepage;
1194 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1196 int settings_fd, i;
1197 struct bookmark_file_data *data;
1198 struct bookmarked_file_info this_bookmark;
1200 /* read settings file */
1201 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1202 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1204 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1205 rb->close(settings_fd);
1207 else
1209 /* load default settings if there is no settings file */
1210 viewer_default_settings();
1213 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1215 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1216 data->bookmarked_files_count = 0;
1218 /* read bookmarks if file exists */
1219 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1220 if (settings_fd >= 0)
1222 /* figure out how many items to read */
1223 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1224 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1225 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1226 rb->read(settings_fd, data->bookmarks,
1227 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1228 rb->close(settings_fd);
1231 file_pos = 0;
1232 screen_top_ptr = buffer;
1234 /* check if current file is in list */
1235 for (i=0; i < data->bookmarked_files_count; i++)
1237 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1239 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1240 int screen_top = screen_pos % buffer_size;
1241 file_pos = screen_pos - screen_top;
1242 screen_top_ptr = buffer + screen_top;
1243 break;
1247 this_bookmark.file_position = file_pos;
1248 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1250 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1251 rb->strcpy(this_bookmark.filename,file_name);
1253 /* prevent potential slot overflow */
1254 if (i >= data->bookmarked_files_count)
1256 if (i < MAX_BOOKMARKED_FILES)
1257 data->bookmarked_files_count++;
1258 else
1259 i = MAX_BOOKMARKED_FILES-1;
1262 /* write bookmark file with spare slot in first position
1263 to be filled in by viewer_save_settings */
1264 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1265 if (settings_fd >=0 )
1267 /* write count */
1268 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1270 /* write the current bookmark */
1271 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1273 /* write everything that was before this bookmark */
1274 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1276 rb->close(settings_fd);
1279 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1281 if (BUFFER_OOB(screen_top_ptr))
1283 screen_top_ptr = buffer;
1286 fill_buffer(file_pos, buffer, buffer_size);
1288 /* remember the current position */
1289 start_position = file_pos + screen_top_ptr - buffer;
1291 init_need_scrollbar();
1294 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1296 int settings_fd;
1298 /* save the viewer settings if they have been changed */
1299 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1301 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1303 if (settings_fd >= 0 )
1305 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1306 rb->close(settings_fd);
1310 /* save the bookmark if the position has changed */
1311 if (file_pos + screen_top_ptr - buffer != start_position)
1313 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1315 if (settings_fd >= 0 )
1317 struct bookmarked_file_info b;
1318 b.file_position = file_pos + screen_top_ptr - buffer;
1319 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1320 rb->memset(&b.filename[0],0,MAX_PATH);
1321 rb->strcpy(b.filename,file_name);
1322 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1323 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1324 rb->close(settings_fd);
1329 static void viewer_exit(void *parameter)
1331 (void)parameter;
1333 viewer_save_settings();
1334 rb->close(fd);
1337 static int col_limit(int col)
1339 if (col < 0)
1340 col = 0;
1341 else
1342 if (col > max_line_len - 2*glyph_width('o'))
1343 col = max_line_len - 2*glyph_width('o');
1345 return col;
1348 /* settings helper functions */
1350 static bool encoding_setting(void)
1352 static struct opt_items names[NUM_CODEPAGES];
1353 int idx;
1355 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1357 names[idx].string = rb->get_codepage_name(idx);
1358 names[idx].voice_id = -1;
1361 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1362 sizeof(names) / sizeof(names[0]), NULL);
1365 static bool word_wrap_setting(void)
1367 static const struct opt_items names[] = {
1368 {"On", -1},
1369 {"Off (Chop Words)", -1},
1372 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1373 names, 2, NULL);
1376 static bool line_mode_setting(void)
1378 static const struct opt_items names[] = {
1379 {"Normal", -1},
1380 {"Join Lines", -1},
1381 {"Expand Lines", -1},
1382 #ifdef HAVE_LCD_BITMAP
1383 {"Reflow Lines", -1},
1384 #endif
1387 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1388 sizeof(names) / sizeof(names[0]), NULL);
1391 static bool view_mode_setting(void)
1393 static const struct opt_items names[] = {
1394 {"No (Narrow)", -1},
1395 {"Yes", -1},
1397 bool ret;
1398 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1399 names , 2, NULL);
1400 if (prefs.view_mode == NARROW)
1401 col = 0;
1402 return ret;
1405 static bool scroll_mode_setting(void)
1407 static const struct opt_items names[] = {
1408 {"Scroll by Page", -1},
1409 {"Scroll by Line", -1},
1412 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1413 names, 2, NULL);
1416 #ifdef HAVE_LCD_BITMAP
1417 static bool page_mode_setting(void)
1419 static const struct opt_items names[] = {
1420 {"No", -1},
1421 {"Yes", -1},
1424 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1425 names, 2, NULL);
1428 static bool scrollbar_setting(void)
1430 static const struct opt_items names[] = {
1431 {"Off", -1},
1432 {"On", -1}
1435 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1436 names, 2, NULL);
1438 #endif
1440 static bool autoscroll_speed_setting(void)
1442 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1443 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1446 static bool viewer_options_menu(void)
1448 int m;
1449 bool result;
1451 static const struct menu_item items[] = {
1452 {"Encoding", encoding_setting },
1453 {"Word Wrap", word_wrap_setting },
1454 {"Line Mode", line_mode_setting },
1455 {"Wide View", view_mode_setting },
1456 #ifdef HAVE_LCD_BITMAP
1457 {"Show Scrollbar", scrollbar_setting },
1458 {"Overlap Pages", page_mode_setting },
1459 #endif
1460 {"Scroll Mode", scroll_mode_setting},
1461 {"Auto-Scroll Speed", autoscroll_speed_setting },
1463 m = menu_init(items, sizeof(items) / sizeof(*items),
1464 NULL, NULL, NULL, NULL);
1466 result = menu_run(m);
1467 menu_exit(m);
1468 #ifdef HAVE_LCD_BITMAP
1470 /* Show-scrollbar mode for current view-width mode */
1471 init_need_scrollbar();
1472 #endif
1473 return result;
1476 static void viewer_menu(void)
1478 int m;
1479 int result;
1480 static const struct menu_item items[] = {
1481 {"Quit", NULL },
1482 {"Viewer Options", NULL },
1483 {"Show Playback Menu", NULL },
1484 {"Return", NULL },
1487 m = menu_init(items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1488 result=menu_show(m);
1489 switch (result)
1491 case 0: /* quit */
1492 menu_exit(m);
1493 viewer_exit(NULL);
1494 done = true;
1495 break;
1496 case 1: /* change settings */
1497 done = viewer_options_menu();
1498 break;
1499 case 2: /* playback control */
1500 playback_control(NULL);
1501 break;
1502 case 3: /* return */
1503 break;
1505 menu_exit(m);
1506 viewer_draw(col);
1509 enum plugin_status plugin_start(const void* file)
1511 int button, i, ok;
1512 int lastbutton = BUTTON_NONE;
1513 bool autoscroll = false;
1514 long old_tick;
1516 old_tick = *rb->current_tick;
1518 /* get the plugin buffer */
1519 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1521 if (!file)
1522 return PLUGIN_ERROR;
1524 file_name = file;
1525 ok = viewer_init();
1526 if (!ok) {
1527 rb->splash(HZ, "Error opening file.");
1528 return PLUGIN_ERROR;
1531 viewer_load_settings(); /* load the preferences and bookmark */
1533 #if LCD_DEPTH > 1
1534 rb->lcd_set_backdrop(NULL);
1535 #endif
1537 viewer_draw(col);
1539 while (!done) {
1541 if(autoscroll)
1543 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1545 viewer_scroll_down();
1546 viewer_draw(col);
1547 old_tick = *rb->current_tick;
1551 button = rb->button_get_w_tmo(HZ/10);
1552 switch (button) {
1553 case VIEWER_MENU:
1554 viewer_menu();
1555 break;
1557 case VIEWER_AUTOSCROLL:
1558 #ifdef VIEWER_AUTOSCROLL_PRE
1559 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1560 break;
1561 #endif
1562 autoscroll = !autoscroll;
1563 break;
1565 case VIEWER_PAGE_UP:
1566 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1567 if (prefs.scroll_mode == PAGE)
1569 /* Page up */
1570 #ifdef HAVE_LCD_BITMAP
1571 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1572 #else
1573 for (i = 0; i < display_lines; i++)
1574 #endif
1575 viewer_scroll_up();
1577 else
1578 viewer_scroll_up();
1579 old_tick = *rb->current_tick;
1580 viewer_draw(col);
1581 break;
1583 case VIEWER_PAGE_DOWN:
1584 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1585 if (prefs.scroll_mode == PAGE)
1587 /* Page down */
1588 if (next_screen_ptr != NULL)
1589 screen_top_ptr = next_screen_to_draw_ptr;
1591 else
1592 viewer_scroll_down();
1593 old_tick = *rb->current_tick;
1594 viewer_draw(col);
1595 break;
1597 case VIEWER_SCREEN_LEFT:
1598 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1599 if (prefs.view_mode == WIDE) {
1600 /* Screen left */
1601 col -= draw_columns;
1602 col = col_limit(col);
1604 else { /* prefs.view_mode == NARROW */
1605 /* Top of file */
1606 viewer_top();
1609 viewer_draw(col);
1610 break;
1612 case VIEWER_SCREEN_RIGHT:
1613 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1614 if (prefs.view_mode == WIDE) {
1615 /* Screen right */
1616 col += draw_columns;
1617 col = col_limit(col);
1619 else { /* prefs.view_mode == NARROW */
1620 /* Bottom of file */
1621 viewer_bottom();
1624 viewer_draw(col);
1625 break;
1627 #ifdef VIEWER_LINE_UP
1628 case VIEWER_LINE_UP:
1629 case VIEWER_LINE_UP | BUTTON_REPEAT:
1630 /* Scroll up one line */
1631 viewer_scroll_up();
1632 old_tick = *rb->current_tick;
1633 viewer_draw(col);
1634 break;
1636 case VIEWER_LINE_DOWN:
1637 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1638 /* Scroll down one line */
1639 if (next_screen_ptr != NULL)
1640 screen_top_ptr = next_line_ptr;
1641 old_tick = *rb->current_tick;
1642 viewer_draw(col);
1643 break;
1644 #endif
1645 #ifdef VIEWER_COLUMN_LEFT
1646 case VIEWER_COLUMN_LEFT:
1647 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1648 if (prefs.view_mode == WIDE) {
1649 /* Scroll left one column */
1650 col -= glyph_width('o');
1651 col = col_limit(col);
1652 viewer_draw(col);
1654 break;
1656 case VIEWER_COLUMN_RIGHT:
1657 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1658 if (prefs.view_mode == WIDE) {
1659 /* Scroll right one column */
1660 col += glyph_width('o');
1661 col = col_limit(col);
1662 viewer_draw(col);
1664 break;
1665 #endif
1667 #ifdef VIEWER_RC_QUIT
1668 case VIEWER_RC_QUIT:
1669 #endif
1670 case VIEWER_QUIT:
1671 viewer_exit(NULL);
1672 done = true;
1673 break;
1675 default:
1676 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1677 == SYS_USB_CONNECTED)
1678 return PLUGIN_USB_CONNECTED;
1679 break;
1681 if (button != BUTTON_NONE)
1683 lastbutton = button;
1684 rb->yield();
1687 return PLUGIN_OK;