Packard Bell Vibe 500: Start committing plugin keymaps.
[kugel-rb.git] / apps / plugins / viewer.c
blob299c392177a1a510dad463bb89b5018ee1cf0aae
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 /* Packard Bell Vibe 500 keys */
356 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
357 #define VIEWER_QUIT BUTTON_REC
358 #define VIEWER_PAGE_UP BUTTON_OK
359 #define VIEWER_PAGE_DOWN BUTTON_CANCEL
360 #define VIEWER_LINE_UP BUTTON_UP
361 #define VIEWER_LINE_DOWN BUTTON_DOWN
362 #define VIEWER_SCREEN_LEFT BUTTON_PREV
363 #define VIEWER_SCREEN_RIGHT BUTTON_NEXT
364 #define VIEWER_MENU BUTTON_MENU
365 #define VIEWER_AUTOSCROLL BUTTON_PLAY
367 #else
368 #error No keymap defined!
369 #endif
371 #ifdef HAVE_TOUCHSCREEN
372 #ifndef VIEWER_QUIT
373 #define VIEWER_QUIT BUTTON_TOPLEFT
374 #endif
375 #ifndef VIEWER_PAGE_UP
376 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
377 #endif
378 #ifndef VIEWER_PAGE_DOWN
379 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
380 #endif
381 #ifndef VIEWER_SCREEN_LEFT
382 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
383 #endif
384 #ifndef VIEWER_SCREEN_RIGHT
385 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
386 #endif
387 #ifndef VIEWER_MENU
388 #define VIEWER_MENU BUTTON_TOPRIGHT
389 #endif
390 #ifndef VIEWER_AUTOSCROLL
391 #define VIEWER_AUTOSCROLL BUTTON_CENTER
392 #endif
393 #endif
395 /* stuff for the bookmarking */
396 struct bookmarked_file_info {
397 long file_position;
398 int top_ptr_pos;
399 char filename[MAX_PATH];
402 struct bookmark_file_data {
403 signed int bookmarked_files_count;
404 struct bookmarked_file_info bookmarks[];
407 struct preferences {
408 enum {
409 WRAP=0,
410 CHOP,
411 } word_mode;
413 enum {
414 NORMAL=0,
415 JOIN,
416 EXPAND,
417 REFLOW, /* won't be set on charcell LCD, must be last */
418 } line_mode;
420 enum {
421 NARROW=0,
422 WIDE,
423 } view_mode;
425 enum codepages encoding;
427 #ifdef HAVE_LCD_BITMAP
428 enum {
429 SB_OFF=0,
430 SB_ON,
431 } scrollbar_mode;
432 bool need_scrollbar;
434 enum {
435 NO_OVERLAP=0,
436 OVERLAP,
437 } page_mode;
438 #endif /* HAVE_LCD_BITMAP */
440 enum {
441 PAGE=0,
442 LINE,
443 } scroll_mode;
445 int autoscroll_speed;
448 struct preferences prefs;
449 struct preferences old_prefs;
451 static unsigned char *buffer;
452 static long buffer_size;
453 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
454 static int display_columns; /* number of (pixel) columns on the display */
455 static int display_lines; /* number of lines on the display */
456 static int draw_columns; /* number of (pixel) columns available for text */
457 static int par_indent_spaces; /* number of spaces to indent first paragraph */
458 static int fd;
459 static const char *file_name;
460 static long file_size;
461 static long start_position; /* position in the file after the viewer is started */
462 static bool mac_text;
463 static long file_pos; /* Position of the top of the buffer in the file */
464 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
465 static int max_line_len;
466 static unsigned char *screen_top_ptr;
467 static unsigned char *next_screen_ptr;
468 static unsigned char *next_screen_to_draw_ptr;
469 static unsigned char *next_line_ptr;
470 #ifdef HAVE_LCD_BITMAP
471 static struct font *pf;
472 #endif
475 int glyph_width(int ch)
477 if (ch == 0)
478 ch = ' ';
480 #ifdef HAVE_LCD_BITMAP
481 return rb->font_get_width(pf, ch);
482 #else
483 return 1;
484 #endif
487 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
489 unsigned char utf8_tmp[6];
490 int count;
492 if (prefs.encoding == UTF_8)
493 return (unsigned char*)rb->utf8decode(str, ch);
495 count = BUFFER_OOB(str+2)? 1:2;
496 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
497 rb->utf8decode(utf8_tmp, ch);
499 #ifdef HAVE_LCD_BITMAP
500 if (prefs.encoding >= SJIS && *str >= 0x80
501 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
502 return (unsigned char*)str+2;
503 else
504 #endif
505 return (unsigned char*)str+1;
508 bool done = false;
509 int col = 0;
511 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
512 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
513 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
514 static unsigned char* crop_at_width(const unsigned char* p)
516 int k,width;
517 unsigned short ch;
518 const unsigned char *oldp = p;
520 k=width=0;
522 while (LINE_IS_NOT_FULL) {
523 oldp = p;
524 p = get_ucs(p, &ch);
525 ADVANCE_COUNTERS(ch);
528 return (unsigned char*)oldp;
531 static unsigned char* find_first_feed(const unsigned char* p, int size)
533 int i;
535 for (i=0; i < size; i++)
536 if (p[i] == 0)
537 return (unsigned char*) p+i;
539 return NULL;
542 static unsigned char* find_last_feed(const unsigned char* p, int size)
544 int i;
546 for (i=size-1; i>=0; i--)
547 if (p[i] == 0)
548 return (unsigned char*) p+i;
550 return NULL;
553 static unsigned char* find_last_space(const unsigned char* p, int size)
555 int i, j, k;
557 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
559 if (!BUFFER_OOB(&p[size]))
560 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
561 if (p[size] == line_break[j])
562 return (unsigned char*) p+size;
564 for (i=size-1; i>=0; i--)
565 for (j=k; j < (int) sizeof(line_break); j++)
567 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
568 if (p[i] == line_break[j])
569 return (unsigned char*) p+i;
572 return NULL;
575 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
577 const unsigned char *next_line = NULL;
578 int size, i, j, k, width, search_len, spaces, newlines;
579 bool first_chars;
580 unsigned char c;
582 if (is_short != NULL)
583 *is_short = true;
585 if BUFFER_OOB(cur_line)
586 return NULL;
588 if (prefs.view_mode == WIDE) {
589 search_len = MAX_WIDTH;
591 else { /* prefs.view_mode == NARROW */
592 search_len = crop_at_width(cur_line) - cur_line;
595 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
597 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
598 /* Need to scan ahead and possibly increase search_len and size,
599 or possibly set next_line at second hard return in a row. */
600 next_line = NULL;
601 first_chars=true;
602 for (j=k=width=spaces=newlines=0; ; j++) {
603 if (BUFFER_OOB(cur_line+j))
604 return NULL;
605 if (LINE_IS_FULL) {
606 size = search_len = j;
607 break;
610 c = cur_line[j];
611 switch (c) {
612 case ' ':
613 if (prefs.line_mode == REFLOW) {
614 if (newlines > 0) {
615 size = j;
616 next_line = cur_line + size;
617 return (unsigned char*) next_line;
619 if (j==0) /* i=1 is intentional */
620 for (i=0; i<par_indent_spaces; i++)
621 ADVANCE_COUNTERS(' ');
623 if (!first_chars) spaces++;
624 break;
626 case 0:
627 if (newlines > 0) {
628 size = j;
629 next_line = cur_line + size - spaces;
630 if (next_line != cur_line)
631 return (unsigned char*) next_line;
632 break;
635 newlines++;
636 size += spaces -1;
637 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
638 return NULL;
639 search_len = size;
640 spaces = first_chars? 0:1;
641 break;
643 default:
644 if (prefs.line_mode==JOIN || newlines>0) {
645 while (spaces) {
646 spaces--;
647 ADVANCE_COUNTERS(' ');
648 if (LINE_IS_FULL) {
649 size = search_len = j;
650 break;
653 newlines=0;
654 } else if (spaces) {
655 /* REFLOW, multiple spaces between words: count only
656 * one. If more are needed, they will be added
657 * while drawing. */
658 search_len = size;
659 spaces=0;
660 ADVANCE_COUNTERS(' ');
661 if (LINE_IS_FULL) {
662 size = search_len = j;
663 break;
666 first_chars = false;
667 ADVANCE_COUNTERS(c);
668 break;
672 else {
673 /* find first hard return */
674 next_line = find_first_feed(cur_line, size);
677 if (next_line == NULL)
678 if (size == search_len) {
679 if (prefs.word_mode == WRAP) /* Find last space */
680 next_line = find_last_space(cur_line, size);
682 if (next_line == NULL)
683 next_line = crop_at_width(cur_line);
684 else
685 if (prefs.word_mode == WRAP)
686 for (i=0;
687 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
688 i++)
689 next_line++;
692 if (prefs.line_mode == EXPAND)
693 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
694 if (next_line[0] == 0)
695 if (next_line != cur_line)
696 return (unsigned char*) next_line;
698 /* If next_line is pointing to a zero, increment it; i.e.,
699 leave the terminator at the end of cur_line. If pointing
700 to a hyphen, increment only if there is room to display
701 the hyphen on current line (won't apply in WIDE mode,
702 since it's guarenteed there won't be room). */
703 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
704 if (next_line[0] == 0)/* ||
705 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
706 next_line++;
708 if (BUFFER_OOB(next_line))
709 return NULL;
711 if (is_short)
712 *is_short = false;
714 return (unsigned char*) next_line;
717 static unsigned char* find_prev_line(const unsigned char* cur_line)
719 const unsigned char *prev_line = NULL;
720 const unsigned char *p;
722 if BUFFER_OOB(cur_line)
723 return NULL;
725 /* To wrap consistently at the same places, we must
726 start with a known hard return, then work downwards.
727 We can either search backwards for a hard return,
728 or simply start wrapping downwards from top of buffer.
729 If current line is not near top of buffer, this is
730 a file with long lines (paragraphs). We would need to
731 read earlier sectors before we could decide how to
732 properly wrap the lines above the current line, but
733 it probably is not worth the disk access. Instead,
734 start with top of buffer and wrap down from there.
735 This may result in some lines wrapping at different
736 points from where they wrap when scrolling down.
737 If buffer is at top of file, start at top of buffer. */
739 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
740 prev_line = p = NULL;
741 else
742 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
743 /* Null means no line feeds in buffer above current line. */
745 if (prev_line == NULL)
746 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
747 prev_line = p = buffer;
748 /* (else return NULL and read previous block) */
750 /* Wrap downwards until too far, then use the one before. */
751 while (p < cur_line && p != NULL) {
752 prev_line = p;
753 p = find_next_line(prev_line, NULL);
756 if (BUFFER_OOB(prev_line))
757 return NULL;
759 return (unsigned char*) prev_line;
762 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
764 /* Read from file and preprocess the data */
765 /* To minimize disk access, always read on sector boundaries */
766 unsigned numread, i;
767 bool found_CR = false;
769 rb->lseek(fd, pos, SEEK_SET);
770 numread = rb->read(fd, buf, size);
771 rb->button_clear_queue(); /* clear button queue */
773 for(i = 0; i < numread; i++) {
774 switch(buf[i]) {
775 case '\r':
776 if (mac_text) {
777 buf[i] = 0;
779 else {
780 buf[i] = ' ';
781 found_CR = true;
783 break;
785 case '\n':
786 buf[i] = 0;
787 found_CR = false;
788 break;
790 case 0: /* No break between case 0 and default, intentionally */
791 buf[i] = ' ';
792 default:
793 if (found_CR) {
794 buf[i - 1] = 0;
795 found_CR = false;
796 mac_text = true;
798 break;
803 static int read_and_synch(int direction)
805 /* Read next (or prev) block, and reposition global pointers. */
806 /* direction: 1 for down (i.e., further into file), -1 for up */
807 int move_size, move_vector, offset;
808 unsigned char *fill_buf;
810 if (direction == -1) /* up */ {
811 move_size = SMALL_BLOCK_SIZE;
812 offset = 0;
813 fill_buf = TOP_SECTOR;
814 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
815 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
817 else /* down */ {
818 if (prefs.view_mode == WIDE) {
819 /* WIDE mode needs more buffer so we have to read smaller blocks */
820 move_size = SMALL_BLOCK_SIZE;
821 offset = LARGE_BLOCK_SIZE;
822 fill_buf = BOTTOM_SECTOR;
823 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
824 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
826 else {
827 move_size = LARGE_BLOCK_SIZE;
828 offset = SMALL_BLOCK_SIZE;
829 fill_buf = MID_SECTOR;
830 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
833 move_vector = direction * move_size;
834 screen_top_ptr -= move_vector;
835 file_pos += move_vector;
836 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
837 fill_buffer(file_pos + offset, fill_buf, move_size);
838 return move_vector;
841 static void viewer_scroll_up(void)
843 unsigned char *p;
845 p = find_prev_line(screen_top_ptr);
846 if (p == NULL && !BUFFER_BOF()) {
847 read_and_synch(-1);
848 p = find_prev_line(screen_top_ptr);
850 if (p != NULL)
851 screen_top_ptr = p;
854 static void viewer_scroll_down(void)
856 if (next_screen_ptr != NULL)
857 screen_top_ptr = next_line_ptr;
860 #ifdef HAVE_LCD_BITMAP
861 static void viewer_scrollbar(void) {
862 int items, min_shown, max_shown;
864 items = (int) file_size; /* (SH1 int is same as long) */
865 min_shown = (int) file_pos + (screen_top_ptr - buffer);
867 if (next_screen_ptr == NULL)
868 max_shown = items;
869 else
870 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
872 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
873 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
875 #endif
877 static void viewer_draw(int col)
879 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
880 int width, extra_spaces, indent_spaces, spaces_per_word;
881 bool multiple_spacing, line_is_short;
882 unsigned short ch;
883 unsigned char *str, *oldstr;
884 unsigned char *line_begin;
885 unsigned char *line_end;
886 unsigned char c;
887 unsigned char scratch_buffer[MAX_COLUMNS + 1];
888 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
889 unsigned char *endptr;
891 /* If col==-1 do all calculations but don't display */
892 if (col != -1) {
893 #ifdef HAVE_LCD_BITMAP
894 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
895 #else
896 left_col = 0;
897 #endif
898 rb->lcd_clear_display();
900 max_line_len = 0;
901 line_begin = line_end = screen_top_ptr;
903 for (i = 0; i < display_lines; i++) {
904 if (BUFFER_OOB(line_end))
905 break; /* Happens after display last line at BUFFER_EOF() */
907 line_begin = line_end;
908 line_end = find_next_line(line_begin, &line_is_short);
910 if (line_end == NULL) {
911 if (BUFFER_EOF()) {
912 if (i < display_lines - 1 && !BUFFER_BOF()) {
913 if (col != -1)
914 rb->lcd_clear_display();
916 for (; i < display_lines - 1; i++)
917 viewer_scroll_up();
919 line_begin = line_end = screen_top_ptr;
920 i = -1;
921 continue;
923 else {
924 line_end = buffer_end;
927 else {
928 resynch_move = read_and_synch(1); /* Read block & move ptrs */
929 line_begin -= resynch_move;
930 if (i > 0)
931 next_line_ptr -= resynch_move;
933 line_end = find_next_line(line_begin, NULL);
934 if (line_end == NULL) /* Should not really happen */
935 break;
938 line_len = line_end - line_begin;
940 /* calculate line_len */
941 str = oldstr = line_begin;
942 j = -1;
943 while (str < line_end) {
944 oldstr = str;
945 str = crop_at_width(str);
946 j++;
948 line_width = j*draw_columns;
949 while (oldstr < line_end) {
950 oldstr = get_ucs(oldstr, &ch);
951 line_width += glyph_width(ch);
954 if (prefs.line_mode == JOIN) {
955 if (line_begin[0] == 0) {
956 line_begin++;
957 if (prefs.word_mode == CHOP)
958 line_end++;
959 else
960 line_len--;
962 for (j=k=spaces=0; j < line_len; j++) {
963 if (k == MAX_COLUMNS)
964 break;
966 c = line_begin[j];
967 switch (c) {
968 case ' ':
969 spaces++;
970 break;
971 case 0:
972 spaces = 0;
973 scratch_buffer[k++] = ' ';
974 break;
975 default:
976 while (spaces) {
977 spaces--;
978 scratch_buffer[k++] = ' ';
979 if (k == MAX_COLUMNS - 1)
980 break;
982 scratch_buffer[k++] = c;
983 break;
986 if (col != -1) {
987 scratch_buffer[k] = 0;
988 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
989 prefs.encoding, draw_columns/glyph_width('i'));
990 *endptr = 0;
993 else if (prefs.line_mode == REFLOW) {
994 if (line_begin[0] == 0) {
995 line_begin++;
996 if (prefs.word_mode == CHOP)
997 line_end++;
998 else
999 line_len--;
1002 indent_spaces = 0;
1003 if (!line_is_short) {
1004 multiple_spacing = false;
1005 width=spaces=0;
1006 for (str = line_begin; str < line_end; ) {
1007 str = get_ucs(str, &ch);
1008 switch (ch) {
1009 case ' ':
1010 case 0:
1011 if ((str == line_begin) && (prefs.word_mode==WRAP))
1012 /* special case: indent the paragraph,
1013 * don't count spaces */
1014 indent_spaces = par_indent_spaces;
1015 else if (!multiple_spacing)
1016 spaces++;
1017 multiple_spacing = true;
1018 break;
1019 default:
1020 multiple_spacing = false;
1021 width += glyph_width(ch);
1022 break;
1025 if (multiple_spacing) spaces--;
1027 if (spaces) {
1028 /* total number of spaces to insert between words */
1029 extra_spaces = (draw_columns-width)/glyph_width(' ')
1030 - indent_spaces;
1031 /* number of spaces between each word*/
1032 spaces_per_word = extra_spaces / spaces;
1033 /* number of words with n+1 spaces (to fill up) */
1034 extra_spaces = extra_spaces % spaces;
1035 if (spaces_per_word > 2) { /* too much spacing is awful */
1036 spaces_per_word = 3;
1037 extra_spaces = 0;
1039 } else { /* this doesn't matter much... no spaces anyway */
1040 spaces_per_word = extra_spaces = 0;
1042 } else { /* end of a paragraph: don't fill line */
1043 spaces_per_word = 1;
1044 extra_spaces = 0;
1047 multiple_spacing = false;
1048 for (j=k=spaces=0; j < line_len; j++) {
1049 if (k == MAX_COLUMNS)
1050 break;
1052 c = line_begin[j];
1053 switch (c) {
1054 case ' ':
1055 case 0:
1056 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1057 for (j=0; j<par_indent_spaces; j++)
1058 scratch_buffer[k++] = ' ';
1059 j=0;
1061 else if (!multiple_spacing) {
1062 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1063 scratch_buffer[k++] = ' ';
1064 spaces++;
1066 multiple_spacing = true;
1067 break;
1068 default:
1069 scratch_buffer[k++] = c;
1070 multiple_spacing = false;
1071 break;
1075 if (col != -1) {
1076 scratch_buffer[k] = 0;
1077 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1078 prefs.encoding, k-col);
1079 *endptr = 0;
1082 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1083 if (col != -1)
1084 if (line_width > col) {
1085 str = oldstr = line_begin;
1086 k = col;
1087 width = 0;
1088 while( (width<draw_columns) && (oldstr<line_end) )
1090 oldstr = get_ucs(oldstr, &ch);
1091 if (k > 0) {
1092 k -= glyph_width(ch);
1093 line_begin = oldstr;
1094 } else {
1095 width += glyph_width(ch);
1099 if(prefs.view_mode==WIDE)
1100 endptr = rb->iso_decode(line_begin, utf8_buffer,
1101 prefs.encoding, oldstr-line_begin);
1102 else
1103 endptr = rb->iso_decode(line_begin, utf8_buffer,
1104 prefs.encoding, line_end-line_begin);
1105 *endptr = 0;
1108 if (col != -1 && line_width > col)
1109 #ifdef HAVE_LCD_BITMAP
1110 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1111 #else
1112 rb->lcd_puts(left_col, i, utf8_buffer);
1113 #endif
1114 if (line_width > max_line_len)
1115 max_line_len = line_width;
1117 if (i == 0)
1118 next_line_ptr = line_end;
1120 next_screen_ptr = line_end;
1121 if (BUFFER_OOB(next_screen_ptr))
1122 next_screen_ptr = NULL;
1124 #ifdef HAVE_LCD_BITMAP
1125 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1127 if (prefs.need_scrollbar)
1128 viewer_scrollbar();
1129 #else
1130 next_screen_to_draw_ptr = next_screen_ptr;
1131 #endif
1133 if (col != -1)
1134 rb->lcd_update();
1137 static void viewer_top(void)
1139 /* Read top of file into buffer
1140 and point screen pointer to top */
1141 if (file_pos != 0)
1143 file_pos = 0;
1144 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1145 fill_buffer(0, buffer, buffer_size);
1148 screen_top_ptr = buffer;
1151 static void viewer_bottom(void)
1153 /* Read bottom of file into buffer
1154 and point screen pointer to bottom */
1155 long last_sectors;
1157 if (file_size > buffer_size) {
1158 /* Find last buffer in file, round up to next sector boundary */
1159 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1160 last_sectors /= SMALL_BLOCK_SIZE;
1161 last_sectors *= SMALL_BLOCK_SIZE;
1163 else {
1164 last_sectors = 0;
1167 if (file_pos != last_sectors)
1169 file_pos = last_sectors;
1170 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1171 fill_buffer(last_sectors, buffer, buffer_size);
1174 screen_top_ptr = buffer_end-1;
1177 #ifdef HAVE_LCD_BITMAP
1178 static void init_need_scrollbar(void) {
1179 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1180 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1181 viewer_draw(-1);
1182 prefs.need_scrollbar = NEED_SCROLLBAR();
1183 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1184 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1186 #else
1187 #define init_need_scrollbar()
1188 #endif
1190 static bool viewer_init(void)
1192 #ifdef HAVE_LCD_BITMAP
1194 pf = rb->font_get(FONT_UI);
1196 display_lines = LCD_HEIGHT / pf->height;
1197 draw_columns = display_columns = LCD_WIDTH;
1198 #else
1199 /* REAL fixed pitch :) all chars use up 1 cell */
1200 display_lines = 2;
1201 draw_columns = display_columns = 11;
1202 par_indent_spaces = 2;
1203 #endif
1205 fd = rb->open(file_name, O_RDONLY);
1206 if (fd < 0)
1207 return false;
1209 file_size = rb->filesize(fd);
1210 if (file_size==-1)
1211 return false;
1213 /* Init mac_text value used in processing buffer */
1214 mac_text = false;
1216 return true;
1219 static void viewer_default_settings(void)
1221 prefs.word_mode = WRAP;
1222 prefs.line_mode = NORMAL;
1223 prefs.view_mode = NARROW;
1224 prefs.scroll_mode = PAGE;
1225 #ifdef HAVE_LCD_BITMAP
1226 prefs.page_mode = NO_OVERLAP;
1227 prefs.scrollbar_mode = SB_OFF;
1228 #endif
1229 prefs.autoscroll_speed = 1;
1230 /* Set codepage to system default */
1231 prefs.encoding = rb->global_settings->default_codepage;
1234 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1236 int settings_fd, i;
1237 struct bookmark_file_data *data;
1238 struct bookmarked_file_info this_bookmark;
1240 /* read settings file */
1241 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1242 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1244 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1245 rb->close(settings_fd);
1247 else
1249 /* load default settings if there is no settings file */
1250 viewer_default_settings();
1253 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1255 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1256 data->bookmarked_files_count = 0;
1258 /* read bookmarks if file exists */
1259 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1260 if (settings_fd >= 0)
1262 /* figure out how many items to read */
1263 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1264 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1265 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1266 rb->read(settings_fd, data->bookmarks,
1267 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1268 rb->close(settings_fd);
1271 file_pos = 0;
1272 screen_top_ptr = buffer;
1274 /* check if current file is in list */
1275 for (i=0; i < data->bookmarked_files_count; i++)
1277 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1279 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1280 int screen_top = screen_pos % buffer_size;
1281 file_pos = screen_pos - screen_top;
1282 screen_top_ptr = buffer + screen_top;
1283 break;
1287 this_bookmark.file_position = file_pos;
1288 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1290 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1291 rb->strcpy(this_bookmark.filename,file_name);
1293 /* prevent potential slot overflow */
1294 if (i >= data->bookmarked_files_count)
1296 if (i < MAX_BOOKMARKED_FILES)
1297 data->bookmarked_files_count++;
1298 else
1299 i = MAX_BOOKMARKED_FILES-1;
1302 /* write bookmark file with spare slot in first position
1303 to be filled in by viewer_save_settings */
1304 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1305 if (settings_fd >=0 )
1307 /* write count */
1308 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1310 /* write the current bookmark */
1311 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1313 /* write everything that was before this bookmark */
1314 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1316 rb->close(settings_fd);
1319 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1321 if (BUFFER_OOB(screen_top_ptr))
1323 screen_top_ptr = buffer;
1326 fill_buffer(file_pos, buffer, buffer_size);
1328 /* remember the current position */
1329 start_position = file_pos + screen_top_ptr - buffer;
1331 init_need_scrollbar();
1334 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1336 int settings_fd;
1338 /* save the viewer settings if they have been changed */
1339 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1341 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1343 if (settings_fd >= 0 )
1345 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1346 rb->close(settings_fd);
1350 /* save the bookmark if the position has changed */
1351 if (file_pos + screen_top_ptr - buffer != start_position)
1353 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1355 if (settings_fd >= 0 )
1357 struct bookmarked_file_info b;
1358 b.file_position = file_pos + screen_top_ptr - buffer;
1359 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1360 rb->memset(&b.filename[0],0,MAX_PATH);
1361 rb->strcpy(b.filename,file_name);
1362 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1363 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1364 rb->close(settings_fd);
1369 static void viewer_exit(void *parameter)
1371 (void)parameter;
1373 viewer_save_settings();
1374 rb->close(fd);
1377 static int col_limit(int col)
1379 if (col < 0)
1380 col = 0;
1381 else
1382 if (col > max_line_len - 2*glyph_width('o'))
1383 col = max_line_len - 2*glyph_width('o');
1385 return col;
1388 /* settings helper functions */
1390 static bool encoding_setting(void)
1392 static struct opt_items names[NUM_CODEPAGES];
1393 int idx;
1395 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1397 names[idx].string = rb->get_codepage_name(idx);
1398 names[idx].voice_id = -1;
1401 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1402 sizeof(names) / sizeof(names[0]), NULL);
1405 static bool word_wrap_setting(void)
1407 static const struct opt_items names[] = {
1408 {"On", -1},
1409 {"Off (Chop Words)", -1},
1412 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1413 names, 2, NULL);
1416 static bool line_mode_setting(void)
1418 static const struct opt_items names[] = {
1419 {"Normal", -1},
1420 {"Join Lines", -1},
1421 {"Expand Lines", -1},
1422 #ifdef HAVE_LCD_BITMAP
1423 {"Reflow Lines", -1},
1424 #endif
1427 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1428 sizeof(names) / sizeof(names[0]), NULL);
1431 static bool view_mode_setting(void)
1433 static const struct opt_items names[] = {
1434 {"No (Narrow)", -1},
1435 {"Yes", -1},
1437 bool ret;
1438 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1439 names , 2, NULL);
1440 if (prefs.view_mode == NARROW)
1441 col = 0;
1442 return ret;
1445 static bool scroll_mode_setting(void)
1447 static const struct opt_items names[] = {
1448 {"Scroll by Page", -1},
1449 {"Scroll by Line", -1},
1452 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1453 names, 2, NULL);
1456 #ifdef HAVE_LCD_BITMAP
1457 static bool page_mode_setting(void)
1459 static const struct opt_items names[] = {
1460 {"No", -1},
1461 {"Yes", -1},
1464 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1465 names, 2, NULL);
1468 static bool scrollbar_setting(void)
1470 static const struct opt_items names[] = {
1471 {"Off", -1},
1472 {"On", -1}
1475 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1476 names, 2, NULL);
1478 #endif
1480 static bool autoscroll_speed_setting(void)
1482 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1483 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1486 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
1487 NULL, NULL, Icon_NOICON);
1488 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
1489 NULL, NULL, Icon_NOICON);
1490 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
1491 NULL, NULL, Icon_NOICON);
1492 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
1493 NULL, NULL, Icon_NOICON);
1494 #ifdef HAVE_LCD_BITMAP
1495 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
1496 NULL, NULL, Icon_NOICON);
1497 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
1498 NULL, NULL, Icon_NOICON);
1499 #endif
1500 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
1501 NULL, NULL, Icon_NOICON);
1502 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
1503 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
1504 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
1505 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
1506 #ifdef HAVE_LCD_BITMAP
1507 &scrollbar_item, &page_mode_item,
1508 #endif
1509 &scroll_mode_item, &autoscroll_speed_item);
1511 static bool viewer_options_menu(void)
1513 bool result;
1514 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
1516 #ifdef HAVE_LCD_BITMAP
1517 /* Show-scrollbar mode for current view-width mode */
1518 init_need_scrollbar();
1519 #endif
1520 return result;
1523 static void viewer_menu(void)
1525 int result;
1526 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
1527 "Return", "Viewer Options",
1528 "Show Playback Menu", "Quit");
1530 result = rb->do_menu(&menu, NULL, NULL, false);
1531 switch (result)
1533 case 0: /* return */
1534 break;
1535 case 1: /* change settings */
1536 done = viewer_options_menu();
1537 break;
1538 case 2: /* playback control */
1539 playback_control(NULL);
1540 break;
1541 case 3: /* quit */
1542 viewer_exit(NULL);
1543 done = true;
1544 break;
1546 viewer_draw(col);
1549 enum plugin_status plugin_start(const void* file)
1551 int button, i, ok;
1552 int lastbutton = BUTTON_NONE;
1553 bool autoscroll = false;
1554 long old_tick;
1556 old_tick = *rb->current_tick;
1558 /* get the plugin buffer */
1559 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1561 if (!file)
1562 return PLUGIN_ERROR;
1564 file_name = file;
1565 ok = viewer_init();
1566 if (!ok) {
1567 rb->splash(HZ, "Error opening file.");
1568 return PLUGIN_ERROR;
1571 viewer_load_settings(); /* load the preferences and bookmark */
1573 #if LCD_DEPTH > 1
1574 rb->lcd_set_backdrop(NULL);
1575 #endif
1577 viewer_draw(col);
1579 while (!done) {
1581 if(autoscroll)
1583 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1585 viewer_scroll_down();
1586 viewer_draw(col);
1587 old_tick = *rb->current_tick;
1591 button = rb->button_get_w_tmo(HZ/10);
1592 switch (button) {
1593 case VIEWER_MENU:
1594 viewer_menu();
1595 break;
1597 case VIEWER_AUTOSCROLL:
1598 #ifdef VIEWER_AUTOSCROLL_PRE
1599 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1600 break;
1601 #endif
1602 autoscroll = !autoscroll;
1603 break;
1605 case VIEWER_PAGE_UP:
1606 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1607 if (prefs.scroll_mode == PAGE)
1609 /* Page up */
1610 #ifdef HAVE_LCD_BITMAP
1611 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1612 #else
1613 for (i = 0; i < display_lines; i++)
1614 #endif
1615 viewer_scroll_up();
1617 else
1618 viewer_scroll_up();
1619 old_tick = *rb->current_tick;
1620 viewer_draw(col);
1621 break;
1623 case VIEWER_PAGE_DOWN:
1624 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1625 if (prefs.scroll_mode == PAGE)
1627 /* Page down */
1628 if (next_screen_ptr != NULL)
1629 screen_top_ptr = next_screen_to_draw_ptr;
1631 else
1632 viewer_scroll_down();
1633 old_tick = *rb->current_tick;
1634 viewer_draw(col);
1635 break;
1637 case VIEWER_SCREEN_LEFT:
1638 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1639 if (prefs.view_mode == WIDE) {
1640 /* Screen left */
1641 col -= draw_columns;
1642 col = col_limit(col);
1644 else { /* prefs.view_mode == NARROW */
1645 /* Top of file */
1646 viewer_top();
1649 viewer_draw(col);
1650 break;
1652 case VIEWER_SCREEN_RIGHT:
1653 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1654 if (prefs.view_mode == WIDE) {
1655 /* Screen right */
1656 col += draw_columns;
1657 col = col_limit(col);
1659 else { /* prefs.view_mode == NARROW */
1660 /* Bottom of file */
1661 viewer_bottom();
1664 viewer_draw(col);
1665 break;
1667 #ifdef VIEWER_LINE_UP
1668 case VIEWER_LINE_UP:
1669 case VIEWER_LINE_UP | BUTTON_REPEAT:
1670 /* Scroll up one line */
1671 viewer_scroll_up();
1672 old_tick = *rb->current_tick;
1673 viewer_draw(col);
1674 break;
1676 case VIEWER_LINE_DOWN:
1677 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1678 /* Scroll down one line */
1679 if (next_screen_ptr != NULL)
1680 screen_top_ptr = next_line_ptr;
1681 old_tick = *rb->current_tick;
1682 viewer_draw(col);
1683 break;
1684 #endif
1685 #ifdef VIEWER_COLUMN_LEFT
1686 case VIEWER_COLUMN_LEFT:
1687 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1688 if (prefs.view_mode == WIDE) {
1689 /* Scroll left one column */
1690 col -= glyph_width('o');
1691 col = col_limit(col);
1692 viewer_draw(col);
1694 break;
1696 case VIEWER_COLUMN_RIGHT:
1697 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1698 if (prefs.view_mode == WIDE) {
1699 /* Scroll right one column */
1700 col += glyph_width('o');
1701 col = col_limit(col);
1702 viewer_draw(col);
1704 break;
1705 #endif
1707 #ifdef VIEWER_RC_QUIT
1708 case VIEWER_RC_QUIT:
1709 #endif
1710 case VIEWER_QUIT:
1711 viewer_exit(NULL);
1712 done = true;
1713 break;
1715 default:
1716 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1717 == SYS_USB_CONNECTED)
1718 return PLUGIN_USB_CONNECTED;
1719 break;
1721 if (button != BUTTON_NONE)
1723 lastbutton = button;
1724 rb->yield();
1727 return PLUGIN_OK;