Bump plugin API version. This should have been done in r24587. Also, because the...
[kugel-rb.git] / apps / plugins / viewer.c
blob88b43c30b3568a2bc4de7dbcc4dab2aa52421efb
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include <ctype.h>
23 #include "lib/playback_control.h"
25 PLUGIN_HEADER
27 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
28 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
30 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
31 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
32 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
33 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
34 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
35 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
36 #define TOP_SECTOR buffer
37 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
38 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
39 #undef SCROLLBAR_WIDTH
40 #define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width
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 == COWON_D2_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 /* Philips SA9200 keys */
327 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
328 #define VIEWER_QUIT BUTTON_POWER
329 #define VIEWER_PAGE_UP BUTTON_UP
330 #define VIEWER_PAGE_DOWN BUTTON_DOWN
331 #define VIEWER_SCREEN_LEFT BUTTON_PREV
332 #define VIEWER_SCREEN_RIGHT BUTTON_NEXT
333 #define VIEWER_MENU BUTTON_MENU
334 #define VIEWER_AUTOSCROLL BUTTON_PLAY
336 /* Onda VX747 keys */
337 #elif CONFIG_KEYPAD == ONDAVX747_PAD
338 #define VIEWER_QUIT BUTTON_POWER
339 #define VIEWER_MENU BUTTON_MENU
341 /* Onda VX777 keys */
342 #elif CONFIG_KEYPAD == ONDAVX777_PAD
343 #define VIEWER_QUIT BUTTON_POWER
345 /* SAMSUNG YH-820 / YH-920 / YH-925 keys */
346 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
347 #define VIEWER_QUIT BUTTON_REC
348 #define VIEWER_PAGE_UP BUTTON_UP
349 #define VIEWER_PAGE_DOWN BUTTON_DOWN
350 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
351 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
352 #define VIEWER_MENU BUTTON_PLAY
353 #define VIEWER_AUTOSCROLL BUTTON_REW
355 #else
356 #error No keymap defined!
357 #endif
359 #ifdef HAVE_TOUCHSCREEN
360 #ifndef VIEWER_QUIT
361 #define VIEWER_QUIT BUTTON_TOPLEFT
362 #endif
363 #ifndef VIEWER_PAGE_UP
364 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
365 #endif
366 #ifndef VIEWER_PAGE_DOWN
367 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
368 #endif
369 #ifndef VIEWER_SCREEN_LEFT
370 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
371 #endif
372 #ifndef VIEWER_SCREEN_RIGHT
373 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
374 #endif
375 #ifndef VIEWER_MENU
376 #define VIEWER_MENU BUTTON_TOPRIGHT
377 #endif
378 #ifndef VIEWER_AUTOSCROLL
379 #define VIEWER_AUTOSCROLL BUTTON_CENTER
380 #endif
381 #endif
383 /* stuff for the bookmarking */
384 struct bookmarked_file_info {
385 long file_position;
386 int top_ptr_pos;
387 char filename[MAX_PATH];
390 struct bookmark_file_data {
391 signed int bookmarked_files_count;
392 struct bookmarked_file_info bookmarks[];
395 struct preferences {
396 enum {
397 WRAP=0,
398 CHOP,
399 } word_mode;
401 enum {
402 NORMAL=0,
403 JOIN,
404 EXPAND,
405 REFLOW, /* won't be set on charcell LCD, must be last */
406 } line_mode;
408 enum {
409 NARROW=0,
410 WIDE,
411 } view_mode;
413 enum codepages encoding;
415 #ifdef HAVE_LCD_BITMAP
416 enum {
417 SB_OFF=0,
418 SB_ON,
419 } scrollbar_mode;
420 bool need_scrollbar;
422 enum {
423 NO_OVERLAP=0,
424 OVERLAP,
425 } page_mode;
426 #endif /* HAVE_LCD_BITMAP */
428 enum {
429 PAGE=0,
430 LINE,
431 } scroll_mode;
433 int autoscroll_speed;
436 struct preferences prefs;
437 struct preferences old_prefs;
439 static unsigned char *buffer;
440 static long buffer_size;
441 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
442 static int display_columns; /* number of (pixel) columns on the display */
443 static int display_lines; /* number of lines on the display */
444 static int draw_columns; /* number of (pixel) columns available for text */
445 static int par_indent_spaces; /* number of spaces to indent first paragraph */
446 static int fd;
447 static const char *file_name;
448 static long file_size;
449 static long start_position; /* position in the file after the viewer is started */
450 static bool mac_text;
451 static long file_pos; /* Position of the top of the buffer in the file */
452 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
453 static int max_line_len;
454 static unsigned char *screen_top_ptr;
455 static unsigned char *next_screen_ptr;
456 static unsigned char *next_screen_to_draw_ptr;
457 static unsigned char *next_line_ptr;
458 #ifdef HAVE_LCD_BITMAP
459 static struct font *pf;
460 #endif
463 int glyph_width(int ch)
465 if (ch == 0)
466 ch = ' ';
468 #ifdef HAVE_LCD_BITMAP
469 return rb->font_get_width(pf, ch);
470 #else
471 return 1;
472 #endif
475 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
477 unsigned char utf8_tmp[6];
478 int count;
480 if (prefs.encoding == UTF_8)
481 return (unsigned char*)rb->utf8decode(str, ch);
483 count = BUFFER_OOB(str+2)? 1:2;
484 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
485 rb->utf8decode(utf8_tmp, ch);
487 #ifdef HAVE_LCD_BITMAP
488 if (prefs.encoding >= SJIS && *str >= 0x80
489 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
490 return (unsigned char*)str+2;
491 else
492 #endif
493 return (unsigned char*)str+1;
496 bool done = false;
497 int col = 0;
499 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
500 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
501 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
502 static unsigned char* crop_at_width(const unsigned char* p)
504 int k,width;
505 unsigned short ch;
506 const unsigned char *oldp = p;
508 k=width=0;
510 while (LINE_IS_NOT_FULL) {
511 oldp = p;
512 p = get_ucs(p, &ch);
513 ADVANCE_COUNTERS(ch);
516 return (unsigned char*)oldp;
519 static unsigned char* find_first_feed(const unsigned char* p, int size)
521 int i;
523 for (i=0; i < size; i++)
524 if (p[i] == 0)
525 return (unsigned char*) p+i;
527 return NULL;
530 static unsigned char* find_last_feed(const unsigned char* p, int size)
532 int i;
534 for (i=size-1; i>=0; i--)
535 if (p[i] == 0)
536 return (unsigned char*) p+i;
538 return NULL;
541 static unsigned char* find_last_space(const unsigned char* p, int size)
543 int i, j, k;
545 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
547 if (!BUFFER_OOB(&p[size]))
548 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
549 if (p[size] == line_break[j])
550 return (unsigned char*) p+size;
552 for (i=size-1; i>=0; i--)
553 for (j=k; j < (int) sizeof(line_break); j++)
555 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
556 if (p[i] == line_break[j])
557 return (unsigned char*) p+i;
560 return NULL;
563 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
565 const unsigned char *next_line = NULL;
566 int size, i, j, k, width, search_len, spaces, newlines;
567 bool first_chars;
568 unsigned char c;
570 if (is_short != NULL)
571 *is_short = true;
573 if BUFFER_OOB(cur_line)
574 return NULL;
576 if (prefs.view_mode == WIDE) {
577 search_len = MAX_WIDTH;
579 else { /* prefs.view_mode == NARROW */
580 search_len = crop_at_width(cur_line) - cur_line;
583 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
585 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
586 /* Need to scan ahead and possibly increase search_len and size,
587 or possibly set next_line at second hard return in a row. */
588 next_line = NULL;
589 first_chars=true;
590 for (j=k=width=spaces=newlines=0; ; j++) {
591 if (BUFFER_OOB(cur_line+j))
592 return NULL;
593 if (LINE_IS_FULL) {
594 size = search_len = j;
595 break;
598 c = cur_line[j];
599 switch (c) {
600 case ' ':
601 if (prefs.line_mode == REFLOW) {
602 if (newlines > 0) {
603 size = j;
604 next_line = cur_line + size;
605 return (unsigned char*) next_line;
607 if (j==0) /* i=1 is intentional */
608 for (i=0; i<par_indent_spaces; i++)
609 ADVANCE_COUNTERS(' ');
611 if (!first_chars) spaces++;
612 break;
614 case 0:
615 if (newlines > 0) {
616 size = j;
617 next_line = cur_line + size - spaces;
618 if (next_line != cur_line)
619 return (unsigned char*) next_line;
620 break;
623 newlines++;
624 size += spaces -1;
625 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
626 return NULL;
627 search_len = size;
628 spaces = first_chars? 0:1;
629 break;
631 default:
632 if (prefs.line_mode==JOIN || newlines>0) {
633 while (spaces) {
634 spaces--;
635 ADVANCE_COUNTERS(' ');
636 if (LINE_IS_FULL) {
637 size = search_len = j;
638 break;
641 newlines=0;
642 } else if (spaces) {
643 /* REFLOW, multiple spaces between words: count only
644 * one. If more are needed, they will be added
645 * while drawing. */
646 search_len = size;
647 spaces=0;
648 ADVANCE_COUNTERS(' ');
649 if (LINE_IS_FULL) {
650 size = search_len = j;
651 break;
654 first_chars = false;
655 ADVANCE_COUNTERS(c);
656 break;
660 else {
661 /* find first hard return */
662 next_line = find_first_feed(cur_line, size);
665 if (next_line == NULL)
666 if (size == search_len) {
667 if (prefs.word_mode == WRAP) /* Find last space */
668 next_line = find_last_space(cur_line, size);
670 if (next_line == NULL)
671 next_line = crop_at_width(cur_line);
672 else
673 if (prefs.word_mode == WRAP)
674 for (i=0;
675 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
676 i++)
677 next_line++;
680 if (prefs.line_mode == EXPAND)
681 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
682 if (next_line[0] == 0)
683 if (next_line != cur_line)
684 return (unsigned char*) next_line;
686 /* If next_line is pointing to a zero, increment it; i.e.,
687 leave the terminator at the end of cur_line. If pointing
688 to a hyphen, increment only if there is room to display
689 the hyphen on current line (won't apply in WIDE mode,
690 since it's guarenteed there won't be room). */
691 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
692 if (next_line[0] == 0)/* ||
693 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
694 next_line++;
696 if (BUFFER_OOB(next_line))
697 return NULL;
699 if (is_short)
700 *is_short = false;
702 return (unsigned char*) next_line;
705 static unsigned char* find_prev_line(const unsigned char* cur_line)
707 const unsigned char *prev_line = NULL;
708 const unsigned char *p;
710 if BUFFER_OOB(cur_line)
711 return NULL;
713 /* To wrap consistently at the same places, we must
714 start with a known hard return, then work downwards.
715 We can either search backwards for a hard return,
716 or simply start wrapping downwards from top of buffer.
717 If current line is not near top of buffer, this is
718 a file with long lines (paragraphs). We would need to
719 read earlier sectors before we could decide how to
720 properly wrap the lines above the current line, but
721 it probably is not worth the disk access. Instead,
722 start with top of buffer and wrap down from there.
723 This may result in some lines wrapping at different
724 points from where they wrap when scrolling down.
725 If buffer is at top of file, start at top of buffer. */
727 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
728 prev_line = p = NULL;
729 else
730 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
731 /* Null means no line feeds in buffer above current line. */
733 if (prev_line == NULL)
734 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
735 prev_line = p = buffer;
736 /* (else return NULL and read previous block) */
738 /* Wrap downwards until too far, then use the one before. */
739 while (p < cur_line && p != NULL) {
740 prev_line = p;
741 p = find_next_line(prev_line, NULL);
744 if (BUFFER_OOB(prev_line))
745 return NULL;
747 return (unsigned char*) prev_line;
750 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
752 /* Read from file and preprocess the data */
753 /* To minimize disk access, always read on sector boundaries */
754 unsigned numread, i;
755 bool found_CR = false;
757 rb->lseek(fd, pos, SEEK_SET);
758 numread = rb->read(fd, buf, size);
759 rb->button_clear_queue(); /* clear button queue */
761 for(i = 0; i < numread; i++) {
762 switch(buf[i]) {
763 case '\r':
764 if (mac_text) {
765 buf[i] = 0;
767 else {
768 buf[i] = ' ';
769 found_CR = true;
771 break;
773 case '\n':
774 buf[i] = 0;
775 found_CR = false;
776 break;
778 case 0: /* No break between case 0 and default, intentionally */
779 buf[i] = ' ';
780 default:
781 if (found_CR) {
782 buf[i - 1] = 0;
783 found_CR = false;
784 mac_text = true;
786 break;
791 static int read_and_synch(int direction)
793 /* Read next (or prev) block, and reposition global pointers. */
794 /* direction: 1 for down (i.e., further into file), -1 for up */
795 int move_size, move_vector, offset;
796 unsigned char *fill_buf;
798 if (direction == -1) /* up */ {
799 move_size = SMALL_BLOCK_SIZE;
800 offset = 0;
801 fill_buf = TOP_SECTOR;
802 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
803 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
805 else /* down */ {
806 if (prefs.view_mode == WIDE) {
807 /* WIDE mode needs more buffer so we have to read smaller blocks */
808 move_size = SMALL_BLOCK_SIZE;
809 offset = LARGE_BLOCK_SIZE;
810 fill_buf = BOTTOM_SECTOR;
811 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
812 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
814 else {
815 move_size = LARGE_BLOCK_SIZE;
816 offset = SMALL_BLOCK_SIZE;
817 fill_buf = MID_SECTOR;
818 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
821 move_vector = direction * move_size;
822 screen_top_ptr -= move_vector;
823 file_pos += move_vector;
824 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
825 fill_buffer(file_pos + offset, fill_buf, move_size);
826 return move_vector;
829 static void viewer_scroll_up(void)
831 unsigned char *p;
833 p = find_prev_line(screen_top_ptr);
834 if (p == NULL && !BUFFER_BOF()) {
835 read_and_synch(-1);
836 p = find_prev_line(screen_top_ptr);
838 if (p != NULL)
839 screen_top_ptr = p;
842 static void viewer_scroll_down(void)
844 if (next_screen_ptr != NULL)
845 screen_top_ptr = next_line_ptr;
848 #ifdef HAVE_LCD_BITMAP
849 static void viewer_scrollbar(void) {
850 int items, min_shown, max_shown;
852 items = (int) file_size; /* (SH1 int is same as long) */
853 min_shown = (int) file_pos + (screen_top_ptr - buffer);
855 if (next_screen_ptr == NULL)
856 max_shown = items;
857 else
858 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
860 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
861 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
863 #endif
865 static void viewer_draw(int col)
867 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
868 int width, extra_spaces, indent_spaces, spaces_per_word;
869 bool multiple_spacing, line_is_short;
870 unsigned short ch;
871 unsigned char *str, *oldstr;
872 unsigned char *line_begin;
873 unsigned char *line_end;
874 unsigned char c;
875 unsigned char scratch_buffer[MAX_COLUMNS + 1];
876 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
877 unsigned char *endptr;
879 /* If col==-1 do all calculations but don't display */
880 if (col != -1) {
881 #ifdef HAVE_LCD_BITMAP
882 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
883 #else
884 left_col = 0;
885 #endif
886 rb->lcd_clear_display();
888 max_line_len = 0;
889 line_begin = line_end = screen_top_ptr;
891 for (i = 0; i < display_lines; i++) {
892 if (BUFFER_OOB(line_end))
893 break; /* Happens after display last line at BUFFER_EOF() */
895 line_begin = line_end;
896 line_end = find_next_line(line_begin, &line_is_short);
898 if (line_end == NULL) {
899 if (BUFFER_EOF()) {
900 if (i < display_lines - 1 && !BUFFER_BOF()) {
901 if (col != -1)
902 rb->lcd_clear_display();
904 for (; i < display_lines - 1; i++)
905 viewer_scroll_up();
907 line_begin = line_end = screen_top_ptr;
908 i = -1;
909 continue;
911 else {
912 line_end = buffer_end;
915 else {
916 resynch_move = read_and_synch(1); /* Read block & move ptrs */
917 line_begin -= resynch_move;
918 if (i > 0)
919 next_line_ptr -= resynch_move;
921 line_end = find_next_line(line_begin, NULL);
922 if (line_end == NULL) /* Should not really happen */
923 break;
926 line_len = line_end - line_begin;
928 /* calculate line_len */
929 str = oldstr = line_begin;
930 j = -1;
931 while (str < line_end) {
932 oldstr = str;
933 str = crop_at_width(str);
934 j++;
936 line_width = j*draw_columns;
937 while (oldstr < line_end) {
938 oldstr = get_ucs(oldstr, &ch);
939 line_width += glyph_width(ch);
942 if (prefs.line_mode == JOIN) {
943 if (line_begin[0] == 0) {
944 line_begin++;
945 if (prefs.word_mode == CHOP)
946 line_end++;
947 else
948 line_len--;
950 for (j=k=spaces=0; j < line_len; j++) {
951 if (k == MAX_COLUMNS)
952 break;
954 c = line_begin[j];
955 switch (c) {
956 case ' ':
957 spaces++;
958 break;
959 case 0:
960 spaces = 0;
961 scratch_buffer[k++] = ' ';
962 break;
963 default:
964 while (spaces) {
965 spaces--;
966 scratch_buffer[k++] = ' ';
967 if (k == MAX_COLUMNS - 1)
968 break;
970 scratch_buffer[k++] = c;
971 break;
974 if (col != -1) {
975 scratch_buffer[k] = 0;
976 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
977 prefs.encoding, draw_columns/glyph_width('i'));
978 *endptr = 0;
981 else if (prefs.line_mode == REFLOW) {
982 if (line_begin[0] == 0) {
983 line_begin++;
984 if (prefs.word_mode == CHOP)
985 line_end++;
986 else
987 line_len--;
990 indent_spaces = 0;
991 if (!line_is_short) {
992 multiple_spacing = false;
993 width=spaces=0;
994 for (str = line_begin; str < line_end; ) {
995 str = get_ucs(str, &ch);
996 switch (ch) {
997 case ' ':
998 case 0:
999 if ((str == line_begin) && (prefs.word_mode==WRAP))
1000 /* special case: indent the paragraph,
1001 * don't count spaces */
1002 indent_spaces = par_indent_spaces;
1003 else if (!multiple_spacing)
1004 spaces++;
1005 multiple_spacing = true;
1006 break;
1007 default:
1008 multiple_spacing = false;
1009 width += glyph_width(ch);
1010 break;
1013 if (multiple_spacing) spaces--;
1015 if (spaces) {
1016 /* total number of spaces to insert between words */
1017 extra_spaces = (draw_columns-width)/glyph_width(' ')
1018 - indent_spaces;
1019 /* number of spaces between each word*/
1020 spaces_per_word = extra_spaces / spaces;
1021 /* number of words with n+1 spaces (to fill up) */
1022 extra_spaces = extra_spaces % spaces;
1023 if (spaces_per_word > 2) { /* too much spacing is awful */
1024 spaces_per_word = 3;
1025 extra_spaces = 0;
1027 } else { /* this doesn't matter much... no spaces anyway */
1028 spaces_per_word = extra_spaces = 0;
1030 } else { /* end of a paragraph: don't fill line */
1031 spaces_per_word = 1;
1032 extra_spaces = 0;
1035 multiple_spacing = false;
1036 for (j=k=spaces=0; j < line_len; j++) {
1037 if (k == MAX_COLUMNS)
1038 break;
1040 c = line_begin[j];
1041 switch (c) {
1042 case ' ':
1043 case 0:
1044 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1045 for (j=0; j<par_indent_spaces; j++)
1046 scratch_buffer[k++] = ' ';
1047 j=0;
1049 else if (!multiple_spacing) {
1050 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1051 scratch_buffer[k++] = ' ';
1052 spaces++;
1054 multiple_spacing = true;
1055 break;
1056 default:
1057 scratch_buffer[k++] = c;
1058 multiple_spacing = false;
1059 break;
1063 if (col != -1) {
1064 scratch_buffer[k] = 0;
1065 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1066 prefs.encoding, k-col);
1067 *endptr = 0;
1070 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1071 if (col != -1)
1072 if (line_width > col) {
1073 str = oldstr = line_begin;
1074 k = col;
1075 width = 0;
1076 while( (width<draw_columns) && (oldstr<line_end) )
1078 oldstr = get_ucs(oldstr, &ch);
1079 if (k > 0) {
1080 k -= glyph_width(ch);
1081 line_begin = oldstr;
1082 } else {
1083 width += glyph_width(ch);
1087 if(prefs.view_mode==WIDE)
1088 endptr = rb->iso_decode(line_begin, utf8_buffer,
1089 prefs.encoding, oldstr-line_begin);
1090 else
1091 endptr = rb->iso_decode(line_begin, utf8_buffer,
1092 prefs.encoding, line_end-line_begin);
1093 *endptr = 0;
1096 if (col != -1 && line_width > col)
1097 #ifdef HAVE_LCD_BITMAP
1098 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1099 #else
1100 rb->lcd_puts(left_col, i, utf8_buffer);
1101 #endif
1102 if (line_width > max_line_len)
1103 max_line_len = line_width;
1105 if (i == 0)
1106 next_line_ptr = line_end;
1108 next_screen_ptr = line_end;
1109 if (BUFFER_OOB(next_screen_ptr))
1110 next_screen_ptr = NULL;
1112 #ifdef HAVE_LCD_BITMAP
1113 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1115 if (prefs.need_scrollbar)
1116 viewer_scrollbar();
1117 #else
1118 next_screen_to_draw_ptr = next_screen_ptr;
1119 #endif
1121 if (col != -1)
1122 rb->lcd_update();
1125 static void viewer_top(void)
1127 /* Read top of file into buffer
1128 and point screen pointer to top */
1129 if (file_pos != 0)
1131 file_pos = 0;
1132 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1133 fill_buffer(0, buffer, buffer_size);
1136 screen_top_ptr = buffer;
1139 static void viewer_bottom(void)
1141 /* Read bottom of file into buffer
1142 and point screen pointer to bottom */
1143 long last_sectors;
1145 if (file_size > buffer_size) {
1146 /* Find last buffer in file, round up to next sector boundary */
1147 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1148 last_sectors /= SMALL_BLOCK_SIZE;
1149 last_sectors *= SMALL_BLOCK_SIZE;
1151 else {
1152 last_sectors = 0;
1155 if (file_pos != last_sectors)
1157 file_pos = last_sectors;
1158 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1159 fill_buffer(last_sectors, buffer, buffer_size);
1162 screen_top_ptr = buffer_end-1;
1165 #ifdef HAVE_LCD_BITMAP
1166 static void init_need_scrollbar(void) {
1167 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1168 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1169 viewer_draw(-1);
1170 prefs.need_scrollbar = NEED_SCROLLBAR();
1171 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1172 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1174 #else
1175 #define init_need_scrollbar()
1176 #endif
1178 static bool viewer_init(void)
1180 #ifdef HAVE_LCD_BITMAP
1182 pf = rb->font_get(FONT_UI);
1184 display_lines = LCD_HEIGHT / pf->height;
1185 draw_columns = display_columns = LCD_WIDTH;
1186 #else
1187 /* REAL fixed pitch :) all chars use up 1 cell */
1188 display_lines = 2;
1189 draw_columns = display_columns = 11;
1190 par_indent_spaces = 2;
1191 #endif
1193 fd = rb->open(file_name, O_RDONLY);
1194 if (fd < 0)
1195 return false;
1197 file_size = rb->filesize(fd);
1198 if (file_size==-1)
1199 return false;
1201 /* Init mac_text value used in processing buffer */
1202 mac_text = false;
1204 return true;
1207 static void viewer_default_settings(void)
1209 prefs.word_mode = WRAP;
1210 prefs.line_mode = NORMAL;
1211 prefs.view_mode = NARROW;
1212 prefs.scroll_mode = PAGE;
1213 #ifdef HAVE_LCD_BITMAP
1214 prefs.page_mode = NO_OVERLAP;
1215 prefs.scrollbar_mode = SB_OFF;
1216 #endif
1217 prefs.autoscroll_speed = 1;
1218 /* Set codepage to system default */
1219 prefs.encoding = rb->global_settings->default_codepage;
1222 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1224 int settings_fd, i;
1225 struct bookmark_file_data *data;
1226 struct bookmarked_file_info this_bookmark;
1228 /* read settings file */
1229 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1230 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1232 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1233 rb->close(settings_fd);
1235 else
1237 /* load default settings if there is no settings file */
1238 viewer_default_settings();
1241 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1243 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1244 data->bookmarked_files_count = 0;
1246 /* read bookmarks if file exists */
1247 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1248 if (settings_fd >= 0)
1250 /* figure out how many items to read */
1251 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1252 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1253 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1254 rb->read(settings_fd, data->bookmarks,
1255 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1256 rb->close(settings_fd);
1259 file_pos = 0;
1260 screen_top_ptr = buffer;
1262 /* check if current file is in list */
1263 for (i=0; i < data->bookmarked_files_count; i++)
1265 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1267 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1268 int screen_top = screen_pos % buffer_size;
1269 file_pos = screen_pos - screen_top;
1270 screen_top_ptr = buffer + screen_top;
1271 break;
1275 this_bookmark.file_position = file_pos;
1276 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1278 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1279 rb->strcpy(this_bookmark.filename,file_name);
1281 /* prevent potential slot overflow */
1282 if (i >= data->bookmarked_files_count)
1284 if (i < MAX_BOOKMARKED_FILES)
1285 data->bookmarked_files_count++;
1286 else
1287 i = MAX_BOOKMARKED_FILES-1;
1290 /* write bookmark file with spare slot in first position
1291 to be filled in by viewer_save_settings */
1292 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1293 if (settings_fd >=0 )
1295 /* write count */
1296 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1298 /* write the current bookmark */
1299 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1301 /* write everything that was before this bookmark */
1302 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1304 rb->close(settings_fd);
1307 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1309 if (BUFFER_OOB(screen_top_ptr))
1311 screen_top_ptr = buffer;
1314 fill_buffer(file_pos, buffer, buffer_size);
1316 /* remember the current position */
1317 start_position = file_pos + screen_top_ptr - buffer;
1319 init_need_scrollbar();
1322 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1324 int settings_fd;
1326 /* save the viewer settings if they have been changed */
1327 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1329 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1331 if (settings_fd >= 0 )
1333 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1334 rb->close(settings_fd);
1338 /* save the bookmark if the position has changed */
1339 if (file_pos + screen_top_ptr - buffer != start_position)
1341 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1343 if (settings_fd >= 0 )
1345 struct bookmarked_file_info b;
1346 b.file_position = file_pos + screen_top_ptr - buffer;
1347 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1348 rb->memset(&b.filename[0],0,MAX_PATH);
1349 rb->strcpy(b.filename,file_name);
1350 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1351 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1352 rb->close(settings_fd);
1357 static void viewer_exit(void *parameter)
1359 (void)parameter;
1361 viewer_save_settings();
1362 rb->close(fd);
1365 static int col_limit(int col)
1367 if (col < 0)
1368 col = 0;
1369 else
1370 if (col > max_line_len - 2*glyph_width('o'))
1371 col = max_line_len - 2*glyph_width('o');
1373 return col;
1376 /* settings helper functions */
1378 static bool encoding_setting(void)
1380 static struct opt_items names[NUM_CODEPAGES];
1381 int idx;
1383 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1385 names[idx].string = rb->get_codepage_name(idx);
1386 names[idx].voice_id = -1;
1389 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1390 sizeof(names) / sizeof(names[0]), NULL);
1393 static bool word_wrap_setting(void)
1395 static const struct opt_items names[] = {
1396 {"On", -1},
1397 {"Off (Chop Words)", -1},
1400 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1401 names, 2, NULL);
1404 static bool line_mode_setting(void)
1406 static const struct opt_items names[] = {
1407 {"Normal", -1},
1408 {"Join Lines", -1},
1409 {"Expand Lines", -1},
1410 #ifdef HAVE_LCD_BITMAP
1411 {"Reflow Lines", -1},
1412 #endif
1415 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1416 sizeof(names) / sizeof(names[0]), NULL);
1419 static bool view_mode_setting(void)
1421 static const struct opt_items names[] = {
1422 {"No (Narrow)", -1},
1423 {"Yes", -1},
1425 bool ret;
1426 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1427 names , 2, NULL);
1428 if (prefs.view_mode == NARROW)
1429 col = 0;
1430 return ret;
1433 static bool scroll_mode_setting(void)
1435 static const struct opt_items names[] = {
1436 {"Scroll by Page", -1},
1437 {"Scroll by Line", -1},
1440 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1441 names, 2, NULL);
1444 #ifdef HAVE_LCD_BITMAP
1445 static bool page_mode_setting(void)
1447 static const struct opt_items names[] = {
1448 {"No", -1},
1449 {"Yes", -1},
1452 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1453 names, 2, NULL);
1456 static bool scrollbar_setting(void)
1458 static const struct opt_items names[] = {
1459 {"Off", -1},
1460 {"On", -1}
1463 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1464 names, 2, NULL);
1466 #endif
1468 static bool autoscroll_speed_setting(void)
1470 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1471 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1474 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
1475 NULL, NULL, Icon_NOICON);
1476 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
1477 NULL, NULL, Icon_NOICON);
1478 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
1479 NULL, NULL, Icon_NOICON);
1480 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
1481 NULL, NULL, Icon_NOICON);
1482 #ifdef HAVE_LCD_BITMAP
1483 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
1484 NULL, NULL, Icon_NOICON);
1485 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
1486 NULL, NULL, Icon_NOICON);
1487 #endif
1488 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
1489 NULL, NULL, Icon_NOICON);
1490 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
1491 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
1492 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
1493 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
1494 #ifdef HAVE_LCD_BITMAP
1495 &scrollbar_item, &page_mode_item,
1496 #endif
1497 &scroll_mode_item, &autoscroll_speed_item);
1499 static bool viewer_options_menu(void)
1501 bool result;
1502 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
1504 #ifdef HAVE_LCD_BITMAP
1505 /* Show-scrollbar mode for current view-width mode */
1506 init_need_scrollbar();
1507 #endif
1508 return result;
1511 static void viewer_menu(void)
1513 int result;
1514 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
1515 "Return", "Viewer Options",
1516 "Show Playback Menu", "Quit");
1518 result = rb->do_menu(&menu, NULL, NULL, false);
1519 switch (result)
1521 case 0: /* return */
1522 break;
1523 case 1: /* change settings */
1524 done = viewer_options_menu();
1525 break;
1526 case 2: /* playback control */
1527 playback_control(NULL);
1528 break;
1529 case 3: /* quit */
1530 viewer_exit(NULL);
1531 done = true;
1532 break;
1534 viewer_draw(col);
1537 enum plugin_status plugin_start(const void* file)
1539 int button, i, ok;
1540 int lastbutton = BUTTON_NONE;
1541 bool autoscroll = false;
1542 long old_tick;
1544 old_tick = *rb->current_tick;
1546 /* get the plugin buffer */
1547 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1549 if (!file)
1550 return PLUGIN_ERROR;
1552 file_name = file;
1553 ok = viewer_init();
1554 if (!ok) {
1555 rb->splash(HZ, "Error opening file.");
1556 return PLUGIN_ERROR;
1559 viewer_load_settings(); /* load the preferences and bookmark */
1561 #if LCD_DEPTH > 1
1562 rb->lcd_set_backdrop(NULL);
1563 #endif
1565 viewer_draw(col);
1567 while (!done) {
1569 if(autoscroll)
1571 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1573 viewer_scroll_down();
1574 viewer_draw(col);
1575 old_tick = *rb->current_tick;
1579 button = rb->button_get_w_tmo(HZ/10);
1580 switch (button) {
1581 case VIEWER_MENU:
1582 viewer_menu();
1583 break;
1585 case VIEWER_AUTOSCROLL:
1586 #ifdef VIEWER_AUTOSCROLL_PRE
1587 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1588 break;
1589 #endif
1590 autoscroll = !autoscroll;
1591 break;
1593 case VIEWER_PAGE_UP:
1594 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1595 if (prefs.scroll_mode == PAGE)
1597 /* Page up */
1598 #ifdef HAVE_LCD_BITMAP
1599 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1600 #else
1601 for (i = 0; i < display_lines; i++)
1602 #endif
1603 viewer_scroll_up();
1605 else
1606 viewer_scroll_up();
1607 old_tick = *rb->current_tick;
1608 viewer_draw(col);
1609 break;
1611 case VIEWER_PAGE_DOWN:
1612 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1613 if (prefs.scroll_mode == PAGE)
1615 /* Page down */
1616 if (next_screen_ptr != NULL)
1617 screen_top_ptr = next_screen_to_draw_ptr;
1619 else
1620 viewer_scroll_down();
1621 old_tick = *rb->current_tick;
1622 viewer_draw(col);
1623 break;
1625 case VIEWER_SCREEN_LEFT:
1626 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1627 if (prefs.view_mode == WIDE) {
1628 /* Screen left */
1629 col -= draw_columns;
1630 col = col_limit(col);
1632 else { /* prefs.view_mode == NARROW */
1633 /* Top of file */
1634 viewer_top();
1637 viewer_draw(col);
1638 break;
1640 case VIEWER_SCREEN_RIGHT:
1641 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1642 if (prefs.view_mode == WIDE) {
1643 /* Screen right */
1644 col += draw_columns;
1645 col = col_limit(col);
1647 else { /* prefs.view_mode == NARROW */
1648 /* Bottom of file */
1649 viewer_bottom();
1652 viewer_draw(col);
1653 break;
1655 #ifdef VIEWER_LINE_UP
1656 case VIEWER_LINE_UP:
1657 case VIEWER_LINE_UP | BUTTON_REPEAT:
1658 /* Scroll up one line */
1659 viewer_scroll_up();
1660 old_tick = *rb->current_tick;
1661 viewer_draw(col);
1662 break;
1664 case VIEWER_LINE_DOWN:
1665 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1666 /* Scroll down one line */
1667 if (next_screen_ptr != NULL)
1668 screen_top_ptr = next_line_ptr;
1669 old_tick = *rb->current_tick;
1670 viewer_draw(col);
1671 break;
1672 #endif
1673 #ifdef VIEWER_COLUMN_LEFT
1674 case VIEWER_COLUMN_LEFT:
1675 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1676 if (prefs.view_mode == WIDE) {
1677 /* Scroll left one column */
1678 col -= glyph_width('o');
1679 col = col_limit(col);
1680 viewer_draw(col);
1682 break;
1684 case VIEWER_COLUMN_RIGHT:
1685 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1686 if (prefs.view_mode == WIDE) {
1687 /* Scroll right one column */
1688 col += glyph_width('o');
1689 col = col_limit(col);
1690 viewer_draw(col);
1692 break;
1693 #endif
1695 #ifdef VIEWER_RC_QUIT
1696 case VIEWER_RC_QUIT:
1697 #endif
1698 case VIEWER_QUIT:
1699 viewer_exit(NULL);
1700 done = true;
1701 break;
1703 default:
1704 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1705 == SYS_USB_CONNECTED)
1706 return PLUGIN_USB_CONNECTED;
1707 break;
1709 if (button != BUTTON_NONE)
1711 lastbutton = button;
1712 rb->yield();
1715 return PLUGIN_OK;