Enable calendar plugin for non-rtc targets (FS#10786 by Teruaki Kawashima)
[kugel-rb.git] / apps / plugins / viewer.c
blob046c76523a53204e3c0dee212450eb3ae60843e2
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "plugin.h"
23 #include <ctype.h>
24 #include "lib/playback_control.h"
26 PLUGIN_HEADER
28 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
29 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
31 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
32 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
33 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
34 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
35 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
36 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
37 #define TOP_SECTOR buffer
38 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
39 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
40 #undef SCROLLBAR_WIDTH
41 #define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width
43 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
45 /* Out-Of-Bounds test for any pointer to data in the buffer */
46 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
48 /* Does the buffer contain the beginning of the file? */
49 #define BUFFER_BOF() (file_pos==0)
51 /* Does the buffer contain the end of the file? */
52 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
54 /* Formula for the endpoint address outside of buffer data */
55 #define BUFFER_END() \
56 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
58 /* Is the entire file being shown in one screen? */
59 #define ONE_SCREEN_FITS_ALL() \
60 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
62 /* Is a scrollbar called for on the current screen? */
63 #define NEED_SCROLLBAR() \
64 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
66 /* variable button definitions */
68 /* Recorder keys */
69 #if CONFIG_KEYPAD == RECORDER_PAD
70 #define VIEWER_QUIT BUTTON_OFF
71 #define VIEWER_PAGE_UP BUTTON_UP
72 #define VIEWER_PAGE_DOWN BUTTON_DOWN
73 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
74 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
75 #define VIEWER_MENU BUTTON_F1
76 #define VIEWER_AUTOSCROLL BUTTON_PLAY
77 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
78 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
79 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
80 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
82 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
83 #define VIEWER_QUIT BUTTON_OFF
84 #define VIEWER_PAGE_UP BUTTON_UP
85 #define VIEWER_PAGE_DOWN BUTTON_DOWN
86 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
87 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
88 #define VIEWER_MENU BUTTON_F1
89 #define VIEWER_AUTOSCROLL BUTTON_SELECT
90 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
91 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
92 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
93 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
95 /* Ondio keys */
96 #elif CONFIG_KEYPAD == ONDIO_PAD
97 #define VIEWER_QUIT BUTTON_OFF
98 #define VIEWER_PAGE_UP BUTTON_UP
99 #define VIEWER_PAGE_DOWN BUTTON_DOWN
100 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
101 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
102 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
103 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
104 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
106 /* Player keys */
107 #elif CONFIG_KEYPAD == PLAYER_PAD
108 #define VIEWER_QUIT BUTTON_STOP
109 #define VIEWER_PAGE_UP BUTTON_LEFT
110 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
111 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
112 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
113 #define VIEWER_MENU BUTTON_MENU
114 #define VIEWER_AUTOSCROLL BUTTON_PLAY
116 /* iRiver H1x0 && H3x0 keys */
117 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
118 (CONFIG_KEYPAD == IRIVER_H300_PAD)
119 #define VIEWER_QUIT BUTTON_OFF
120 #define VIEWER_PAGE_UP BUTTON_UP
121 #define VIEWER_PAGE_DOWN BUTTON_DOWN
122 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
123 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
124 #define VIEWER_MENU BUTTON_MODE
125 #define VIEWER_AUTOSCROLL BUTTON_SELECT
126 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
127 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
128 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
129 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
131 #define VIEWER_RC_QUIT BUTTON_RC_STOP
133 /* iPods */
134 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
135 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
136 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
137 #define VIEWER_QUIT_PRE BUTTON_SELECT
138 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
139 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
140 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
141 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
142 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
143 #define VIEWER_MENU BUTTON_MENU
144 #define VIEWER_AUTOSCROLL BUTTON_PLAY
146 /* iFP7xx keys */
147 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
148 #define VIEWER_QUIT BUTTON_PLAY
149 #define VIEWER_PAGE_UP BUTTON_UP
150 #define VIEWER_PAGE_DOWN BUTTON_DOWN
151 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
152 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
153 #define VIEWER_MENU BUTTON_MODE
154 #define VIEWER_AUTOSCROLL BUTTON_SELECT
156 /* iAudio X5 keys */
157 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
158 #define VIEWER_QUIT BUTTON_POWER
159 #define VIEWER_PAGE_UP BUTTON_UP
160 #define VIEWER_PAGE_DOWN BUTTON_DOWN
161 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
162 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
163 #define VIEWER_MENU BUTTON_SELECT
164 #define VIEWER_AUTOSCROLL BUTTON_PLAY
166 /* GIGABEAT keys */
167 #elif CONFIG_KEYPAD == GIGABEAT_PAD
168 #define VIEWER_QUIT BUTTON_POWER
169 #define VIEWER_PAGE_UP BUTTON_UP
170 #define VIEWER_PAGE_DOWN BUTTON_DOWN
171 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
172 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
173 #define VIEWER_MENU BUTTON_MENU
174 #define VIEWER_AUTOSCROLL BUTTON_A
176 /* Sansa E200 keys */
177 #elif CONFIG_KEYPAD == SANSA_E200_PAD
178 #define VIEWER_QUIT BUTTON_POWER
179 #define VIEWER_PAGE_UP BUTTON_UP
180 #define VIEWER_PAGE_DOWN BUTTON_DOWN
181 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
182 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
183 #define VIEWER_MENU BUTTON_SELECT
184 #define VIEWER_AUTOSCROLL BUTTON_REC
185 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
186 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
188 /* Sansa Fuze keys */
189 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
190 #define VIEWER_QUIT (BUTTON_HOME|BUTTON_REPEAT)
191 #define VIEWER_PAGE_UP BUTTON_UP
192 #define VIEWER_PAGE_DOWN BUTTON_DOWN
193 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
194 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
195 #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT
196 #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
197 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
198 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
200 /* Sansa C200 keys */
201 #elif CONFIG_KEYPAD == SANSA_C200_PAD
202 #define VIEWER_QUIT BUTTON_POWER
203 #define VIEWER_PAGE_UP BUTTON_VOL_UP
204 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
205 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
206 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
207 #define VIEWER_MENU BUTTON_SELECT
208 #define VIEWER_AUTOSCROLL BUTTON_REC
209 #define VIEWER_LINE_UP BUTTON_UP
210 #define VIEWER_LINE_DOWN BUTTON_DOWN
212 /* Sansa Clip keys */
213 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
214 #define VIEWER_QUIT BUTTON_POWER
215 #define VIEWER_PAGE_UP BUTTON_VOL_UP
216 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
217 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
218 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
219 #define VIEWER_MENU BUTTON_SELECT
220 #define VIEWER_AUTOSCROLL BUTTON_HOME
221 #define VIEWER_LINE_UP BUTTON_UP
222 #define VIEWER_LINE_DOWN BUTTON_DOWN
224 /* Sansa M200 keys */
225 #elif CONFIG_KEYPAD == SANSA_M200_PAD
226 #define VIEWER_QUIT BUTTON_POWER
227 #define VIEWER_PAGE_UP BUTTON_VOL_UP
228 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
229 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
230 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
231 #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP)
232 #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
233 #define VIEWER_LINE_UP BUTTON_UP
234 #define VIEWER_LINE_DOWN BUTTON_DOWN
236 /* iriver H10 keys */
237 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
238 #define VIEWER_QUIT BUTTON_POWER
239 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
240 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
241 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
242 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
243 #define VIEWER_MENU BUTTON_REW
244 #define VIEWER_AUTOSCROLL BUTTON_PLAY
246 /*M-Robe 500 keys */
247 #elif CONFIG_KEYPAD == MROBE500_PAD
248 #define VIEWER_QUIT BUTTON_POWER
249 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
250 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
251 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
252 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
253 #define VIEWER_MENU BUTTON_RC_HEART
254 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
256 /*Gigabeat S keys */
257 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
258 #define VIEWER_QUIT BUTTON_BACK
259 #define VIEWER_PAGE_UP BUTTON_PREV
260 #define VIEWER_PAGE_DOWN BUTTON_NEXT
261 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
262 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
263 #define VIEWER_MENU BUTTON_MENU
264 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
265 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
266 #define VIEWER_LINE_UP BUTTON_UP
267 #define VIEWER_LINE_DOWN BUTTON_DOWN
268 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
269 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
271 /*M-Robe 100 keys */
272 #elif CONFIG_KEYPAD == MROBE100_PAD
273 #define VIEWER_QUIT BUTTON_POWER
274 #define VIEWER_PAGE_UP BUTTON_UP
275 #define VIEWER_PAGE_DOWN BUTTON_DOWN
276 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
277 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
278 #define VIEWER_MENU BUTTON_MENU
279 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
281 /* iAUdio M3 keys */
282 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
283 #define VIEWER_QUIT BUTTON_RC_REC
284 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
285 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
286 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
287 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
288 #define VIEWER_MENU BUTTON_RC_MENU
289 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
290 #define VIEWER_RC_QUIT BUTTON_REC
292 /* Cowon D2 keys */
293 #elif CONFIG_KEYPAD == COWOND2_PAD
294 #define VIEWER_QUIT BUTTON_POWER
295 #define VIEWER_MENU BUTTON_MENU
297 #elif CONFIG_KEYPAD == IAUDIO67_PAD
298 #define VIEWER_QUIT BUTTON_POWER
299 #define VIEWER_PAGE_UP BUTTON_VOLUP
300 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
301 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
302 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
303 #define VIEWER_MENU BUTTON_MENU
304 #define VIEWER_AUTOSCROLL BUTTON_PLAY
305 #define VIEWER_RC_QUIT BUTTON_STOP
307 /* Creative Zen Vision:M keys */
308 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
309 #define VIEWER_QUIT BUTTON_BACK
310 #define VIEWER_PAGE_UP BUTTON_UP
311 #define VIEWER_PAGE_DOWN BUTTON_DOWN
312 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
313 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
314 #define VIEWER_MENU BUTTON_MENU
315 #define VIEWER_AUTOSCROLL BUTTON_SELECT
317 /* Philips HDD1630 keys */
318 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
319 #define VIEWER_QUIT BUTTON_POWER
320 #define VIEWER_PAGE_UP BUTTON_UP
321 #define VIEWER_PAGE_DOWN BUTTON_DOWN
322 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
323 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
324 #define VIEWER_MENU BUTTON_MENU
325 #define VIEWER_AUTOSCROLL BUTTON_VIEW
327 /* Philips SA9200 keys */
328 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
329 #define VIEWER_QUIT BUTTON_POWER
330 #define VIEWER_PAGE_UP BUTTON_UP
331 #define VIEWER_PAGE_DOWN BUTTON_DOWN
332 #define VIEWER_SCREEN_LEFT BUTTON_PREV
333 #define VIEWER_SCREEN_RIGHT BUTTON_NEXT
334 #define VIEWER_MENU BUTTON_MENU
335 #define VIEWER_AUTOSCROLL BUTTON_PLAY
337 /* Onda VX747 keys */
338 #elif CONFIG_KEYPAD == ONDAVX747_PAD
339 #define VIEWER_QUIT BUTTON_POWER
340 #define VIEWER_MENU BUTTON_MENU
342 /* Onda VX777 keys */
343 #elif CONFIG_KEYPAD == ONDAVX777_PAD
344 #define VIEWER_QUIT BUTTON_POWER
346 /* SAMSUNG YH-820 / YH-920 / YH-925 keys */
347 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
348 #define VIEWER_QUIT BUTTON_REC
349 #define VIEWER_PAGE_UP BUTTON_UP
350 #define VIEWER_PAGE_DOWN BUTTON_DOWN
351 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
352 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
353 #define VIEWER_MENU BUTTON_PLAY
354 #define VIEWER_AUTOSCROLL BUTTON_REW
356 #else
357 #error No keymap defined!
358 #endif
360 #ifdef HAVE_TOUCHSCREEN
361 #ifndef VIEWER_QUIT
362 #define VIEWER_QUIT BUTTON_TOPLEFT
363 #endif
364 #ifndef VIEWER_PAGE_UP
365 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
366 #endif
367 #ifndef VIEWER_PAGE_DOWN
368 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
369 #endif
370 #ifndef VIEWER_SCREEN_LEFT
371 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
372 #endif
373 #ifndef VIEWER_SCREEN_RIGHT
374 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
375 #endif
376 #ifndef VIEWER_MENU
377 #define VIEWER_MENU BUTTON_TOPRIGHT
378 #endif
379 #ifndef VIEWER_AUTOSCROLL
380 #define VIEWER_AUTOSCROLL BUTTON_CENTER
381 #endif
382 #endif
384 /* stuff for the bookmarking */
385 struct bookmarked_file_info {
386 long file_position;
387 int top_ptr_pos;
388 char filename[MAX_PATH];
391 struct bookmark_file_data {
392 signed int bookmarked_files_count;
393 struct bookmarked_file_info bookmarks[];
396 struct preferences {
397 enum {
398 WRAP=0,
399 CHOP,
400 } word_mode;
402 enum {
403 NORMAL=0,
404 JOIN,
405 EXPAND,
406 REFLOW, /* won't be set on charcell LCD, must be last */
407 } line_mode;
409 enum {
410 NARROW=0,
411 WIDE,
412 } view_mode;
414 enum codepages encoding;
416 #ifdef HAVE_LCD_BITMAP
417 enum {
418 SB_OFF=0,
419 SB_ON,
420 } scrollbar_mode;
421 bool need_scrollbar;
423 enum {
424 NO_OVERLAP=0,
425 OVERLAP,
426 } page_mode;
427 #endif /* HAVE_LCD_BITMAP */
429 enum {
430 PAGE=0,
431 LINE,
432 } scroll_mode;
434 int autoscroll_speed;
438 struct preferences prefs;
439 struct preferences old_prefs;
441 static unsigned char *buffer;
442 static long buffer_size;
443 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
444 static int display_columns; /* number of (pixel) columns on the display */
445 static int display_lines; /* number of lines on the display */
446 static int draw_columns; /* number of (pixel) columns available for text */
447 static int par_indent_spaces; /* number of spaces to indent first paragraph */
448 static int fd;
449 static const char *file_name;
450 static long file_size;
451 static long start_position; /* position in the file after the viewer is started */
452 static bool mac_text;
453 static long file_pos; /* Position of the top of the buffer in the file */
454 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
455 static int max_line_len;
456 static unsigned char *screen_top_ptr;
457 static unsigned char *next_screen_ptr;
458 static unsigned char *next_screen_to_draw_ptr;
459 static unsigned char *next_line_ptr;
460 #ifdef HAVE_LCD_BITMAP
461 static struct font *pf;
462 #endif
465 int glyph_width(int ch)
467 if (ch == 0)
468 ch = ' ';
470 #ifdef HAVE_LCD_BITMAP
471 return rb->font_get_width(pf, ch);
472 #else
473 return 1;
474 #endif
477 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
479 unsigned char utf8_tmp[6];
480 int count;
482 if (prefs.encoding == UTF_8)
483 return (unsigned char*)rb->utf8decode(str, ch);
485 count = BUFFER_OOB(str+2)? 1:2;
486 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
487 rb->utf8decode(utf8_tmp, ch);
489 #ifdef HAVE_LCD_BITMAP
490 if (prefs.encoding >= SJIS && *str >= 0x80
491 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
492 return (unsigned char*)str+2;
493 else
494 #endif
495 return (unsigned char*)str+1;
498 bool done = false;
499 int col = 0;
501 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
502 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
503 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
504 static unsigned char* crop_at_width(const unsigned char* p)
506 int k,width;
507 unsigned short ch;
508 const unsigned char *oldp = p;
510 k=width=0;
512 while (LINE_IS_NOT_FULL) {
513 oldp = p;
514 p = get_ucs(p, &ch);
515 ADVANCE_COUNTERS(ch);
518 return (unsigned char*)oldp;
521 static unsigned char* find_first_feed(const unsigned char* p, int size)
523 int i;
525 for (i=0; i < size; i++)
526 if (p[i] == 0)
527 return (unsigned char*) p+i;
529 return NULL;
532 static unsigned char* find_last_feed(const unsigned char* p, int size)
534 int i;
536 for (i=size-1; i>=0; i--)
537 if (p[i] == 0)
538 return (unsigned char*) p+i;
540 return NULL;
543 static unsigned char* find_last_space(const unsigned char* p, int size)
545 int i, j, k;
547 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
549 if (!BUFFER_OOB(&p[size]))
550 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
551 if (p[size] == line_break[j])
552 return (unsigned char*) p+size;
554 for (i=size-1; i>=0; i--)
555 for (j=k; j < (int) sizeof(line_break); j++)
557 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
558 if (p[i] == line_break[j])
559 return (unsigned char*) p+i;
562 return NULL;
565 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
567 const unsigned char *next_line = NULL;
568 int size, i, j, k, width, search_len, spaces, newlines;
569 bool first_chars;
570 unsigned char c;
572 if (is_short != NULL)
573 *is_short = true;
575 if BUFFER_OOB(cur_line)
576 return NULL;
578 if (prefs.view_mode == WIDE) {
579 search_len = MAX_WIDTH;
581 else { /* prefs.view_mode == NARROW */
582 search_len = crop_at_width(cur_line) - cur_line;
585 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
587 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
588 /* Need to scan ahead and possibly increase search_len and size,
589 or possibly set next_line at second hard return in a row. */
590 next_line = NULL;
591 first_chars=true;
592 for (j=k=width=spaces=newlines=0; ; j++) {
593 if (BUFFER_OOB(cur_line+j))
594 return NULL;
595 if (LINE_IS_FULL) {
596 size = search_len = j;
597 break;
600 c = cur_line[j];
601 switch (c) {
602 case ' ':
603 if (prefs.line_mode == REFLOW) {
604 if (newlines > 0) {
605 size = j;
606 next_line = cur_line + size;
607 return (unsigned char*) next_line;
609 if (j==0) /* i=1 is intentional */
610 for (i=0; i<par_indent_spaces; i++)
611 ADVANCE_COUNTERS(' ');
613 if (!first_chars) spaces++;
614 break;
616 case 0:
617 if (newlines > 0) {
618 size = j;
619 next_line = cur_line + size - spaces;
620 if (next_line != cur_line)
621 return (unsigned char*) next_line;
622 break;
625 newlines++;
626 size += spaces -1;
627 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
628 return NULL;
629 search_len = size;
630 spaces = first_chars? 0:1;
631 break;
633 default:
634 if (prefs.line_mode==JOIN || newlines>0) {
635 while (spaces) {
636 spaces--;
637 ADVANCE_COUNTERS(' ');
638 if (LINE_IS_FULL) {
639 size = search_len = j;
640 break;
643 newlines=0;
644 } else if (spaces) {
645 /* REFLOW, multiple spaces between words: count only
646 * one. If more are needed, they will be added
647 * while drawing. */
648 search_len = size;
649 spaces=0;
650 ADVANCE_COUNTERS(' ');
651 if (LINE_IS_FULL) {
652 size = search_len = j;
653 break;
656 first_chars = false;
657 ADVANCE_COUNTERS(c);
658 break;
662 else {
663 /* find first hard return */
664 next_line = find_first_feed(cur_line, size);
667 if (next_line == NULL)
668 if (size == search_len) {
669 if (prefs.word_mode == WRAP) /* Find last space */
670 next_line = find_last_space(cur_line, size);
672 if (next_line == NULL)
673 next_line = crop_at_width(cur_line);
674 else
675 if (prefs.word_mode == WRAP)
676 for (i=0;
677 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
678 i++)
679 next_line++;
682 if (prefs.line_mode == EXPAND)
683 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
684 if (next_line[0] == 0)
685 if (next_line != cur_line)
686 return (unsigned char*) next_line;
688 /* If next_line is pointing to a zero, increment it; i.e.,
689 leave the terminator at the end of cur_line. If pointing
690 to a hyphen, increment only if there is room to display
691 the hyphen on current line (won't apply in WIDE mode,
692 since it's guarenteed there won't be room). */
693 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
694 if (next_line[0] == 0)/* ||
695 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
696 next_line++;
698 if (BUFFER_OOB(next_line))
699 return NULL;
701 if (is_short)
702 *is_short = false;
704 return (unsigned char*) next_line;
707 static unsigned char* find_prev_line(const unsigned char* cur_line)
709 const unsigned char *prev_line = NULL;
710 const unsigned char *p;
712 if BUFFER_OOB(cur_line)
713 return NULL;
715 /* To wrap consistently at the same places, we must
716 start with a known hard return, then work downwards.
717 We can either search backwards for a hard return,
718 or simply start wrapping downwards from top of buffer.
719 If current line is not near top of buffer, this is
720 a file with long lines (paragraphs). We would need to
721 read earlier sectors before we could decide how to
722 properly wrap the lines above the current line, but
723 it probably is not worth the disk access. Instead,
724 start with top of buffer and wrap down from there.
725 This may result in some lines wrapping at different
726 points from where they wrap when scrolling down.
727 If buffer is at top of file, start at top of buffer. */
729 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
730 prev_line = p = NULL;
731 else
732 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
733 /* Null means no line feeds in buffer above current line. */
735 if (prev_line == NULL)
736 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
737 prev_line = p = buffer;
738 /* (else return NULL and read previous block) */
740 /* Wrap downwards until too far, then use the one before. */
741 while (p < cur_line && p != NULL) {
742 prev_line = p;
743 p = find_next_line(prev_line, NULL);
746 if (BUFFER_OOB(prev_line))
747 return NULL;
749 return (unsigned char*) prev_line;
752 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
754 /* Read from file and preprocess the data */
755 /* To minimize disk access, always read on sector boundaries */
756 unsigned numread, i;
757 bool found_CR = false;
759 rb->lseek(fd, pos, SEEK_SET);
760 numread = rb->read(fd, buf, size);
761 rb->button_clear_queue(); /* clear button queue */
763 for(i = 0; i < numread; i++) {
764 switch(buf[i]) {
765 case '\r':
766 if (mac_text) {
767 buf[i] = 0;
769 else {
770 buf[i] = ' ';
771 found_CR = true;
773 break;
775 case '\n':
776 buf[i] = 0;
777 found_CR = false;
778 break;
780 case 0: /* No break between case 0 and default, intentionally */
781 buf[i] = ' ';
782 default:
783 if (found_CR) {
784 buf[i - 1] = 0;
785 found_CR = false;
786 mac_text = true;
788 break;
793 static int read_and_synch(int direction)
795 /* Read next (or prev) block, and reposition global pointers. */
796 /* direction: 1 for down (i.e., further into file), -1 for up */
797 int move_size, move_vector, offset;
798 unsigned char *fill_buf;
800 if (direction == -1) /* up */ {
801 move_size = SMALL_BLOCK_SIZE;
802 offset = 0;
803 fill_buf = TOP_SECTOR;
804 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
805 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
807 else /* down */ {
808 if (prefs.view_mode == WIDE) {
809 /* WIDE mode needs more buffer so we have to read smaller blocks */
810 move_size = SMALL_BLOCK_SIZE;
811 offset = LARGE_BLOCK_SIZE;
812 fill_buf = BOTTOM_SECTOR;
813 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
814 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
816 else {
817 move_size = LARGE_BLOCK_SIZE;
818 offset = SMALL_BLOCK_SIZE;
819 fill_buf = MID_SECTOR;
820 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
823 move_vector = direction * move_size;
824 screen_top_ptr -= move_vector;
825 file_pos += move_vector;
826 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
827 fill_buffer(file_pos + offset, fill_buf, move_size);
828 return move_vector;
831 static void viewer_scroll_up(void)
833 unsigned char *p;
835 p = find_prev_line(screen_top_ptr);
836 if (p == NULL && !BUFFER_BOF()) {
837 read_and_synch(-1);
838 p = find_prev_line(screen_top_ptr);
840 if (p != NULL)
841 screen_top_ptr = p;
844 static void viewer_scroll_down(void)
846 if (next_screen_ptr != NULL)
847 screen_top_ptr = next_line_ptr;
850 #ifdef HAVE_LCD_BITMAP
851 static void viewer_scrollbar(void) {
852 int items, min_shown, max_shown;
854 items = (int) file_size; /* (SH1 int is same as long) */
855 min_shown = (int) file_pos + (screen_top_ptr - buffer);
857 if (next_screen_ptr == NULL)
858 max_shown = items;
859 else
860 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
862 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
863 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
865 #endif
867 static void viewer_draw(int col)
869 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
870 int width, extra_spaces, indent_spaces, spaces_per_word;
871 bool multiple_spacing, line_is_short;
872 unsigned short ch;
873 unsigned char *str, *oldstr;
874 unsigned char *line_begin;
875 unsigned char *line_end;
876 unsigned char c;
877 unsigned char scratch_buffer[MAX_COLUMNS + 1];
878 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
879 unsigned char *endptr;
881 /* If col==-1 do all calculations but don't display */
882 if (col != -1) {
883 #ifdef HAVE_LCD_BITMAP
884 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
885 #else
886 left_col = 0;
887 #endif
888 rb->lcd_clear_display();
890 max_line_len = 0;
891 line_begin = line_end = screen_top_ptr;
893 for (i = 0; i < display_lines; i++) {
894 if (BUFFER_OOB(line_end))
895 break; /* Happens after display last line at BUFFER_EOF() */
897 line_begin = line_end;
898 line_end = find_next_line(line_begin, &line_is_short);
900 if (line_end == NULL) {
901 if (BUFFER_EOF()) {
902 if (i < display_lines - 1 && !BUFFER_BOF()) {
903 if (col != -1)
904 rb->lcd_clear_display();
906 for (; i < display_lines - 1; i++)
907 viewer_scroll_up();
909 line_begin = line_end = screen_top_ptr;
910 i = -1;
911 continue;
913 else {
914 line_end = buffer_end;
917 else {
918 resynch_move = read_and_synch(1); /* Read block & move ptrs */
919 line_begin -= resynch_move;
920 if (i > 0)
921 next_line_ptr -= resynch_move;
923 line_end = find_next_line(line_begin, NULL);
924 if (line_end == NULL) /* Should not really happen */
925 break;
928 line_len = line_end - line_begin;
930 /* calculate line_len */
931 str = oldstr = line_begin;
932 j = -1;
933 while (str < line_end) {
934 oldstr = str;
935 str = crop_at_width(str);
936 j++;
938 line_width = j*draw_columns;
939 while (oldstr < line_end) {
940 oldstr = get_ucs(oldstr, &ch);
941 line_width += glyph_width(ch);
944 if (prefs.line_mode == JOIN) {
945 if (line_begin[0] == 0) {
946 line_begin++;
947 if (prefs.word_mode == CHOP)
948 line_end++;
949 else
950 line_len--;
952 for (j=k=spaces=0; j < line_len; j++) {
953 if (k == MAX_COLUMNS)
954 break;
956 c = line_begin[j];
957 switch (c) {
958 case ' ':
959 spaces++;
960 break;
961 case 0:
962 spaces = 0;
963 scratch_buffer[k++] = ' ';
964 break;
965 default:
966 while (spaces) {
967 spaces--;
968 scratch_buffer[k++] = ' ';
969 if (k == MAX_COLUMNS - 1)
970 break;
972 scratch_buffer[k++] = c;
973 break;
976 if (col != -1) {
977 scratch_buffer[k] = 0;
978 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
979 prefs.encoding, draw_columns/glyph_width('i'));
980 *endptr = 0;
983 else if (prefs.line_mode == REFLOW) {
984 if (line_begin[0] == 0) {
985 line_begin++;
986 if (prefs.word_mode == CHOP)
987 line_end++;
988 else
989 line_len--;
992 indent_spaces = 0;
993 if (!line_is_short) {
994 multiple_spacing = false;
995 width=spaces=0;
996 for (str = line_begin; str < line_end; ) {
997 str = get_ucs(str, &ch);
998 switch (ch) {
999 case ' ':
1000 case 0:
1001 if ((str == line_begin) && (prefs.word_mode==WRAP))
1002 /* special case: indent the paragraph,
1003 * don't count spaces */
1004 indent_spaces = par_indent_spaces;
1005 else if (!multiple_spacing)
1006 spaces++;
1007 multiple_spacing = true;
1008 break;
1009 default:
1010 multiple_spacing = false;
1011 width += glyph_width(ch);
1012 break;
1015 if (multiple_spacing) spaces--;
1017 if (spaces) {
1018 /* total number of spaces to insert between words */
1019 extra_spaces = (draw_columns-width)/glyph_width(' ')
1020 - indent_spaces;
1021 /* number of spaces between each word*/
1022 spaces_per_word = extra_spaces / spaces;
1023 /* number of words with n+1 spaces (to fill up) */
1024 extra_spaces = extra_spaces % spaces;
1025 if (spaces_per_word > 2) { /* too much spacing is awful */
1026 spaces_per_word = 3;
1027 extra_spaces = 0;
1029 } else { /* this doesn't matter much... no spaces anyway */
1030 spaces_per_word = extra_spaces = 0;
1032 } else { /* end of a paragraph: don't fill line */
1033 spaces_per_word = 1;
1034 extra_spaces = 0;
1037 multiple_spacing = false;
1038 for (j=k=spaces=0; j < line_len; j++) {
1039 if (k == MAX_COLUMNS)
1040 break;
1042 c = line_begin[j];
1043 switch (c) {
1044 case ' ':
1045 case 0:
1046 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1047 for (j=0; j<par_indent_spaces; j++)
1048 scratch_buffer[k++] = ' ';
1049 j=0;
1051 else if (!multiple_spacing) {
1052 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1053 scratch_buffer[k++] = ' ';
1054 spaces++;
1056 multiple_spacing = true;
1057 break;
1058 default:
1059 scratch_buffer[k++] = c;
1060 multiple_spacing = false;
1061 break;
1065 if (col != -1) {
1066 scratch_buffer[k] = 0;
1067 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1068 prefs.encoding, k-col);
1069 *endptr = 0;
1072 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1073 if (col != -1)
1074 if (line_width > col) {
1075 str = oldstr = line_begin;
1076 k = col;
1077 width = 0;
1078 while( (width<draw_columns) && (oldstr<line_end) )
1080 oldstr = get_ucs(oldstr, &ch);
1081 if (k > 0) {
1082 k -= glyph_width(ch);
1083 line_begin = oldstr;
1084 } else {
1085 width += glyph_width(ch);
1089 if(prefs.view_mode==WIDE)
1090 endptr = rb->iso_decode(line_begin, utf8_buffer,
1091 prefs.encoding, oldstr-line_begin);
1092 else
1093 endptr = rb->iso_decode(line_begin, utf8_buffer,
1094 prefs.encoding, line_end-line_begin);
1095 *endptr = 0;
1098 if (col != -1 && line_width > col)
1099 #ifdef HAVE_LCD_BITMAP
1100 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1101 #else
1102 rb->lcd_puts(left_col, i, utf8_buffer);
1103 #endif
1104 if (line_width > max_line_len)
1105 max_line_len = line_width;
1107 if (i == 0)
1108 next_line_ptr = line_end;
1110 next_screen_ptr = line_end;
1111 if (BUFFER_OOB(next_screen_ptr))
1112 next_screen_ptr = NULL;
1114 #ifdef HAVE_LCD_BITMAP
1115 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1117 if (prefs.need_scrollbar)
1118 viewer_scrollbar();
1119 #else
1120 next_screen_to_draw_ptr = next_screen_ptr;
1121 #endif
1123 if (col != -1)
1124 rb->lcd_update();
1127 static void viewer_top(void)
1129 /* Read top of file into buffer
1130 and point screen pointer to top */
1131 if (file_pos != 0)
1133 file_pos = 0;
1134 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1135 fill_buffer(0, buffer, buffer_size);
1138 screen_top_ptr = buffer;
1141 static void viewer_bottom(void)
1143 /* Read bottom of file into buffer
1144 and point screen pointer to bottom */
1145 long last_sectors;
1147 if (file_size > buffer_size) {
1148 /* Find last buffer in file, round up to next sector boundary */
1149 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1150 last_sectors /= SMALL_BLOCK_SIZE;
1151 last_sectors *= SMALL_BLOCK_SIZE;
1153 else {
1154 last_sectors = 0;
1157 if (file_pos != last_sectors)
1159 file_pos = last_sectors;
1160 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1161 fill_buffer(last_sectors, buffer, buffer_size);
1164 screen_top_ptr = buffer_end-1;
1167 #ifdef HAVE_LCD_BITMAP
1168 static void init_need_scrollbar(void) {
1169 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1170 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1171 viewer_draw(-1);
1172 prefs.need_scrollbar = NEED_SCROLLBAR();
1173 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1174 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1176 #else
1177 #define init_need_scrollbar()
1178 #endif
1180 static bool viewer_init(void)
1182 #ifdef HAVE_LCD_BITMAP
1184 pf = rb->font_get(FONT_UI);
1186 display_lines = LCD_HEIGHT / pf->height;
1187 draw_columns = display_columns = LCD_WIDTH;
1188 #else
1189 /* REAL fixed pitch :) all chars use up 1 cell */
1190 display_lines = 2;
1191 draw_columns = display_columns = 11;
1192 par_indent_spaces = 2;
1193 #endif
1195 fd = rb->open(file_name, O_RDONLY);
1196 if (fd < 0)
1197 return false;
1199 file_size = rb->filesize(fd);
1200 if (file_size==-1)
1201 return false;
1203 /* Init mac_text value used in processing buffer */
1204 mac_text = false;
1206 return true;
1209 static void viewer_default_settings(void)
1211 prefs.word_mode = WRAP;
1212 prefs.line_mode = NORMAL;
1213 prefs.view_mode = NARROW;
1214 prefs.scroll_mode = PAGE;
1215 #ifdef HAVE_LCD_BITMAP
1216 prefs.page_mode = NO_OVERLAP;
1217 prefs.scrollbar_mode = SB_OFF;
1218 #endif
1219 prefs.autoscroll_speed = 1;
1220 /* Set codepage to system default */
1221 prefs.encoding = rb->global_settings->default_codepage;
1224 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1226 int settings_fd, i;
1227 struct bookmark_file_data *data;
1228 struct bookmarked_file_info this_bookmark;
1230 /* read settings file */
1231 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1232 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1234 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1235 rb->close(settings_fd);
1237 else
1239 /* load default settings if there is no settings file */
1240 viewer_default_settings();
1243 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1245 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1246 data->bookmarked_files_count = 0;
1248 /* read bookmarks if file exists */
1249 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1250 if (settings_fd >= 0)
1252 /* figure out how many items to read */
1253 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1254 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1255 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1256 rb->read(settings_fd, data->bookmarks,
1257 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1258 rb->close(settings_fd);
1261 file_pos = 0;
1262 screen_top_ptr = buffer;
1264 /* check if current file is in list */
1265 for (i=0; i < data->bookmarked_files_count; i++)
1267 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1269 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1270 int screen_top = screen_pos % buffer_size;
1271 file_pos = screen_pos - screen_top;
1272 screen_top_ptr = buffer + screen_top;
1273 break;
1277 this_bookmark.file_position = file_pos;
1278 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1280 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1281 rb->strcpy(this_bookmark.filename,file_name);
1283 /* prevent potential slot overflow */
1284 if (i >= data->bookmarked_files_count)
1286 if (i < MAX_BOOKMARKED_FILES)
1287 data->bookmarked_files_count++;
1288 else
1289 i = MAX_BOOKMARKED_FILES-1;
1292 /* write bookmark file with spare slot in first position
1293 to be filled in by viewer_save_settings */
1294 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1295 if (settings_fd >=0 )
1297 /* write count */
1298 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1300 /* write the current bookmark */
1301 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1303 /* write everything that was before this bookmark */
1304 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1306 rb->close(settings_fd);
1309 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1311 if (BUFFER_OOB(screen_top_ptr))
1313 screen_top_ptr = buffer;
1316 fill_buffer(file_pos, buffer, buffer_size);
1318 /* remember the current position */
1319 start_position = file_pos + screen_top_ptr - buffer;
1321 init_need_scrollbar();
1324 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1326 int settings_fd;
1328 /* save the viewer settings if they have been changed */
1329 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1331 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1333 if (settings_fd >= 0 )
1335 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1336 rb->close(settings_fd);
1340 /* save the bookmark if the position has changed */
1341 if (file_pos + screen_top_ptr - buffer != start_position)
1343 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1345 if (settings_fd >= 0 )
1347 struct bookmarked_file_info b;
1348 b.file_position = file_pos + screen_top_ptr - buffer;
1349 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1350 rb->memset(&b.filename[0],0,MAX_PATH);
1351 rb->strcpy(b.filename,file_name);
1352 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1353 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1354 rb->close(settings_fd);
1359 static void viewer_exit(void *parameter)
1361 (void)parameter;
1363 viewer_save_settings();
1364 rb->close(fd);
1367 static int col_limit(int col)
1369 if (col < 0)
1370 col = 0;
1371 else
1372 if (col > max_line_len - 2*glyph_width('o'))
1373 col = max_line_len - 2*glyph_width('o');
1375 return col;
1378 /* settings helper functions */
1380 static bool encoding_setting(void)
1382 static struct opt_items names[NUM_CODEPAGES];
1383 int idx;
1385 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1387 names[idx].string = rb->get_codepage_name(idx);
1388 names[idx].voice_id = -1;
1391 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1392 sizeof(names) / sizeof(names[0]), NULL);
1395 static bool word_wrap_setting(void)
1397 static const struct opt_items names[] = {
1398 {"On", -1},
1399 {"Off (Chop Words)", -1},
1402 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1403 names, 2, NULL);
1406 static bool line_mode_setting(void)
1408 static const struct opt_items names[] = {
1409 {"Normal", -1},
1410 {"Join Lines", -1},
1411 {"Expand Lines", -1},
1412 #ifdef HAVE_LCD_BITMAP
1413 {"Reflow Lines", -1},
1414 #endif
1417 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1418 sizeof(names) / sizeof(names[0]), NULL);
1421 static bool view_mode_setting(void)
1423 static const struct opt_items names[] = {
1424 {"No (Narrow)", -1},
1425 {"Yes", -1},
1427 bool ret;
1428 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1429 names , 2, NULL);
1430 if (prefs.view_mode == NARROW)
1431 col = 0;
1432 return ret;
1435 static bool scroll_mode_setting(void)
1437 static const struct opt_items names[] = {
1438 {"Scroll by Page", -1},
1439 {"Scroll by Line", -1},
1442 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1443 names, 2, NULL);
1446 #ifdef HAVE_LCD_BITMAP
1447 static bool page_mode_setting(void)
1449 static const struct opt_items names[] = {
1450 {"No", -1},
1451 {"Yes", -1},
1454 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1455 names, 2, NULL);
1458 static bool scrollbar_setting(void)
1460 static const struct opt_items names[] = {
1461 {"Off", -1},
1462 {"On", -1}
1465 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1466 names, 2, NULL);
1468 #endif
1470 static bool autoscroll_speed_setting(void)
1472 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1473 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1476 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
1477 NULL, NULL, Icon_NOICON);
1478 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
1479 NULL, NULL, Icon_NOICON);
1480 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
1481 NULL, NULL, Icon_NOICON);
1482 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
1483 NULL, NULL, Icon_NOICON);
1484 #ifdef HAVE_LCD_BITMAP
1485 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
1486 NULL, NULL, Icon_NOICON);
1487 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
1488 NULL, NULL, Icon_NOICON);
1489 #endif
1490 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
1491 NULL, NULL, Icon_NOICON);
1492 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
1493 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
1494 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
1495 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
1496 #ifdef HAVE_LCD_BITMAP
1497 &scrollbar_item, &page_mode_item,
1498 #endif
1499 &scroll_mode_item, &autoscroll_speed_item);
1501 static bool viewer_options_menu(void)
1503 bool result;
1504 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
1506 #ifdef HAVE_LCD_BITMAP
1507 /* Show-scrollbar mode for current view-width mode */
1508 init_need_scrollbar();
1509 #endif
1510 return result;
1513 static void viewer_menu(void)
1515 int result;
1516 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
1517 "Return", "Viewer Options",
1518 "Show Playback Menu", "Quit");
1520 result = rb->do_menu(&menu, NULL, NULL, false);
1521 switch (result)
1523 case 0: /* return */
1524 break;
1525 case 1: /* change settings */
1526 done = viewer_options_menu();
1527 break;
1528 case 2: /* playback control */
1529 playback_control(NULL);
1530 break;
1531 case 3: /* quit */
1532 viewer_exit(NULL);
1533 done = true;
1534 break;
1536 viewer_draw(col);
1539 enum plugin_status plugin_start(const void* file)
1541 int button, i, ok;
1542 int lastbutton = BUTTON_NONE;
1543 bool autoscroll = false;
1544 long old_tick;
1546 old_tick = *rb->current_tick;
1548 /* get the plugin buffer */
1549 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1551 if (!file)
1552 return PLUGIN_ERROR;
1554 file_name = file;
1555 ok = viewer_init();
1556 if (!ok) {
1557 rb->splash(HZ, "Error opening file.");
1558 return PLUGIN_ERROR;
1561 viewer_load_settings(); /* load the preferences and bookmark */
1563 #if LCD_DEPTH > 1
1564 rb->lcd_set_backdrop(NULL);
1565 #endif
1567 viewer_draw(col);
1569 while (!done) {
1571 if(autoscroll)
1573 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1575 viewer_scroll_down();
1576 viewer_draw(col);
1577 old_tick = *rb->current_tick;
1581 button = rb->button_get_w_tmo(HZ/10);
1582 switch (button) {
1583 case VIEWER_MENU:
1584 viewer_menu();
1585 break;
1587 case VIEWER_AUTOSCROLL:
1588 #ifdef VIEWER_AUTOSCROLL_PRE
1589 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1590 break;
1591 #endif
1592 autoscroll = !autoscroll;
1593 break;
1595 case VIEWER_PAGE_UP:
1596 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1597 if (prefs.scroll_mode == PAGE)
1599 /* Page up */
1600 #ifdef HAVE_LCD_BITMAP
1601 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1602 #else
1603 for (i = 0; i < display_lines; i++)
1604 #endif
1605 viewer_scroll_up();
1607 else
1608 viewer_scroll_up();
1609 old_tick = *rb->current_tick;
1610 viewer_draw(col);
1611 break;
1613 case VIEWER_PAGE_DOWN:
1614 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1615 if (prefs.scroll_mode == PAGE)
1617 /* Page down */
1618 if (next_screen_ptr != NULL)
1619 screen_top_ptr = next_screen_to_draw_ptr;
1621 else
1622 viewer_scroll_down();
1623 old_tick = *rb->current_tick;
1624 viewer_draw(col);
1625 break;
1627 case VIEWER_SCREEN_LEFT:
1628 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1629 if (prefs.view_mode == WIDE) {
1630 /* Screen left */
1631 col -= draw_columns;
1632 col = col_limit(col);
1634 else { /* prefs.view_mode == NARROW */
1635 /* Top of file */
1636 viewer_top();
1639 viewer_draw(col);
1640 break;
1642 case VIEWER_SCREEN_RIGHT:
1643 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1644 if (prefs.view_mode == WIDE) {
1645 /* Screen right */
1646 col += draw_columns;
1647 col = col_limit(col);
1649 else { /* prefs.view_mode == NARROW */
1650 /* Bottom of file */
1651 viewer_bottom();
1654 viewer_draw(col);
1655 break;
1657 #ifdef VIEWER_LINE_UP
1658 case VIEWER_LINE_UP:
1659 case VIEWER_LINE_UP | BUTTON_REPEAT:
1660 /* Scroll up one line */
1661 viewer_scroll_up();
1662 old_tick = *rb->current_tick;
1663 viewer_draw(col);
1664 break;
1666 case VIEWER_LINE_DOWN:
1667 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1668 /* Scroll down one line */
1669 if (next_screen_ptr != NULL)
1670 screen_top_ptr = next_line_ptr;
1671 old_tick = *rb->current_tick;
1672 viewer_draw(col);
1673 break;
1674 #endif
1675 #ifdef VIEWER_COLUMN_LEFT
1676 case VIEWER_COLUMN_LEFT:
1677 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1678 if (prefs.view_mode == WIDE) {
1679 /* Scroll left one column */
1680 col -= glyph_width('o');
1681 col = col_limit(col);
1682 viewer_draw(col);
1684 break;
1686 case VIEWER_COLUMN_RIGHT:
1687 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1688 if (prefs.view_mode == WIDE) {
1689 /* Scroll right one column */
1690 col += glyph_width('o');
1691 col = col_limit(col);
1692 viewer_draw(col);
1694 break;
1695 #endif
1697 #ifdef VIEWER_RC_QUIT
1698 case VIEWER_RC_QUIT:
1699 #endif
1700 case VIEWER_QUIT:
1701 viewer_exit(NULL);
1702 done = true;
1703 break;
1705 default:
1706 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1707 == SYS_USB_CONNECTED)
1708 return PLUGIN_USB_CONNECTED;
1709 break;
1711 if (button != BUTTON_NONE)
1713 lastbutton = button;
1714 rb->yield();
1717 return PLUGIN_OK;