Reverting parts of r19760 that was mistakenly committed.
[kugel-rb.git] / apps / plugins / viewer.c
blobfd460e50e0a5d60d64fc081e282b7919a12c2e71
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "plugin.h"
23 #include <ctype.h>
24 #include "lib/playback_control.h"
25 #include "lib/oldmenuapi.h"
27 PLUGIN_HEADER
29 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
30 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
32 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
33 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
34 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
35 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
36 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
37 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
38 #define TOP_SECTOR buffer
39 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
40 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
41 #define SCROLLBAR_WIDTH 6
43 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
45 /* Out-Of-Bounds test for any pointer to data in the buffer */
46 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
48 /* Does the buffer contain the beginning of the file? */
49 #define BUFFER_BOF() (file_pos==0)
51 /* Does the buffer contain the end of the file? */
52 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
54 /* Formula for the endpoint address outside of buffer data */
55 #define BUFFER_END() \
56 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
58 /* Is the entire file being shown in one screen? */
59 #define ONE_SCREEN_FITS_ALL() \
60 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
62 /* Is a scrollbar called for on the current screen? */
63 #define NEED_SCROLLBAR() \
64 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
66 /* variable button definitions */
68 /* Recorder keys */
69 #if CONFIG_KEYPAD == RECORDER_PAD
70 #define VIEWER_QUIT BUTTON_OFF
71 #define VIEWER_PAGE_UP BUTTON_UP
72 #define VIEWER_PAGE_DOWN BUTTON_DOWN
73 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
74 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
75 #define VIEWER_MENU BUTTON_F1
76 #define VIEWER_AUTOSCROLL BUTTON_PLAY
77 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
78 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
79 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
80 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
82 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
83 #define VIEWER_QUIT BUTTON_OFF
84 #define VIEWER_PAGE_UP BUTTON_UP
85 #define VIEWER_PAGE_DOWN BUTTON_DOWN
86 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
87 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
88 #define VIEWER_MENU BUTTON_F1
89 #define VIEWER_AUTOSCROLL BUTTON_SELECT
90 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
91 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
92 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
93 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
95 /* Ondio keys */
96 #elif CONFIG_KEYPAD == ONDIO_PAD
97 #define VIEWER_QUIT BUTTON_OFF
98 #define VIEWER_PAGE_UP BUTTON_UP
99 #define VIEWER_PAGE_DOWN BUTTON_DOWN
100 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
101 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
102 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
103 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
104 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
106 /* Player keys */
107 #elif CONFIG_KEYPAD == PLAYER_PAD
108 #define VIEWER_QUIT BUTTON_STOP
109 #define VIEWER_PAGE_UP BUTTON_LEFT
110 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
111 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
112 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
113 #define VIEWER_MENU BUTTON_MENU
114 #define VIEWER_AUTOSCROLL BUTTON_PLAY
116 /* iRiver H1x0 && H3x0 keys */
117 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
118 (CONFIG_KEYPAD == IRIVER_H300_PAD)
119 #define VIEWER_QUIT BUTTON_OFF
120 #define VIEWER_PAGE_UP BUTTON_UP
121 #define VIEWER_PAGE_DOWN BUTTON_DOWN
122 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
123 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
124 #define VIEWER_MENU BUTTON_MODE
125 #define VIEWER_AUTOSCROLL BUTTON_SELECT
126 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
127 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
128 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
129 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
131 #define VIEWER_RC_QUIT BUTTON_RC_STOP
133 /* iPods */
134 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
135 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
136 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
137 #define VIEWER_QUIT_PRE BUTTON_SELECT
138 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
139 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
140 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
141 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
142 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
143 #define VIEWER_MENU BUTTON_MENU
144 #define VIEWER_AUTOSCROLL BUTTON_PLAY
146 /* iFP7xx keys */
147 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
148 #define VIEWER_QUIT BUTTON_PLAY
149 #define VIEWER_PAGE_UP BUTTON_UP
150 #define VIEWER_PAGE_DOWN BUTTON_DOWN
151 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
152 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
153 #define VIEWER_MENU BUTTON_MODE
154 #define VIEWER_AUTOSCROLL BUTTON_SELECT
156 /* iAudio X5 keys */
157 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
158 #define VIEWER_QUIT BUTTON_POWER
159 #define VIEWER_PAGE_UP BUTTON_UP
160 #define VIEWER_PAGE_DOWN BUTTON_DOWN
161 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
162 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
163 #define VIEWER_MENU BUTTON_SELECT
164 #define VIEWER_AUTOSCROLL BUTTON_PLAY
166 /* GIGABEAT keys */
167 #elif CONFIG_KEYPAD == GIGABEAT_PAD
168 #define VIEWER_QUIT BUTTON_POWER
169 #define VIEWER_PAGE_UP BUTTON_UP
170 #define VIEWER_PAGE_DOWN BUTTON_DOWN
171 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
172 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
173 #define VIEWER_MENU BUTTON_MENU
174 #define VIEWER_AUTOSCROLL BUTTON_A
176 /* Sansa E200 keys */
177 #elif CONFIG_KEYPAD == SANSA_E200_PAD
178 #define VIEWER_QUIT BUTTON_POWER
179 #define VIEWER_PAGE_UP BUTTON_UP
180 #define VIEWER_PAGE_DOWN BUTTON_DOWN
181 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
182 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
183 #define VIEWER_MENU BUTTON_SELECT
184 #define VIEWER_AUTOSCROLL BUTTON_REC
185 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
186 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
188 /* Sansa Fuze keys */
189 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
190 #define VIEWER_QUIT BUTTON_POWER
191 #define VIEWER_PAGE_UP BUTTON_UP
192 #define VIEWER_PAGE_DOWN BUTTON_DOWN
193 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
194 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
195 #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT
196 #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
197 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
198 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
200 /* Sansa C200 keys */
201 #elif CONFIG_KEYPAD == SANSA_C200_PAD
202 #define VIEWER_QUIT BUTTON_POWER
203 #define VIEWER_PAGE_UP BUTTON_VOL_UP
204 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
205 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
206 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
207 #define VIEWER_MENU BUTTON_SELECT
208 #define VIEWER_AUTOSCROLL BUTTON_REC
209 #define VIEWER_LINE_UP BUTTON_UP
210 #define VIEWER_LINE_DOWN BUTTON_DOWN
212 /* Sansa Clip keys */
213 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
214 #define VIEWER_QUIT BUTTON_POWER
215 #define VIEWER_PAGE_UP BUTTON_VOL_UP
216 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
217 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
218 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
219 #define VIEWER_MENU BUTTON_SELECT
220 #define VIEWER_AUTOSCROLL BUTTON_HOME
221 #define VIEWER_LINE_UP BUTTON_UP
222 #define VIEWER_LINE_DOWN BUTTON_DOWN
224 /* Sansa M200 keys */
225 #elif CONFIG_KEYPAD == SANSA_M200_PAD
226 #define VIEWER_QUIT BUTTON_POWER
227 #define VIEWER_PAGE_UP BUTTON_VOL_UP
228 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
229 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
230 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
231 #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP)
232 #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
233 #define VIEWER_LINE_UP BUTTON_UP
234 #define VIEWER_LINE_DOWN BUTTON_DOWN
236 /* iriver H10 keys */
237 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
238 #define VIEWER_QUIT BUTTON_POWER
239 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
240 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
241 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
242 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
243 #define VIEWER_MENU BUTTON_REW
244 #define VIEWER_AUTOSCROLL BUTTON_PLAY
246 /*M-Robe 500 keys */
247 #elif CONFIG_KEYPAD == MROBE500_PAD
248 #define VIEWER_QUIT BUTTON_POWER
249 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
250 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
251 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
252 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
253 #define VIEWER_MENU BUTTON_RC_HEART
254 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
256 /*Gigabeat S keys */
257 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
258 #define VIEWER_QUIT BUTTON_BACK
259 #define VIEWER_PAGE_UP BUTTON_PREV
260 #define VIEWER_PAGE_DOWN BUTTON_NEXT
261 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
262 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
263 #define VIEWER_MENU BUTTON_MENU
264 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
265 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
266 #define VIEWER_LINE_UP BUTTON_UP
267 #define VIEWER_LINE_DOWN BUTTON_DOWN
268 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
269 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
271 /*M-Robe 100 keys */
272 #elif CONFIG_KEYPAD == MROBE100_PAD
273 #define VIEWER_QUIT BUTTON_POWER
274 #define VIEWER_PAGE_UP BUTTON_UP
275 #define VIEWER_PAGE_DOWN BUTTON_DOWN
276 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
277 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
278 #define VIEWER_MENU BUTTON_MENU
279 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
281 /* iAUdio M3 keys */
282 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
283 #define VIEWER_QUIT BUTTON_RC_REC
284 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
285 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
286 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
287 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
288 #define VIEWER_MENU BUTTON_RC_MENU
289 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
290 #define VIEWER_RC_QUIT BUTTON_REC
292 /* Cowon D2 keys */
293 #elif CONFIG_KEYPAD == COWOND2_PAD
294 #define VIEWER_QUIT BUTTON_POWER
295 #define VIEWER_MENU BUTTON_MENU
297 #elif CONFIG_KEYPAD == IAUDIO67_PAD
298 #define VIEWER_QUIT BUTTON_POWER
299 #define VIEWER_PAGE_UP BUTTON_VOLUP
300 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
301 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
302 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
303 #define VIEWER_MENU BUTTON_MENU
304 #define VIEWER_AUTOSCROLL BUTTON_PLAY
305 #define VIEWER_RC_QUIT BUTTON_STOP
307 /* Creative Zen Vision:M keys */
308 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
309 #define VIEWER_QUIT BUTTON_BACK
310 #define VIEWER_PAGE_UP BUTTON_UP
311 #define VIEWER_PAGE_DOWN BUTTON_DOWN
312 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
313 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
314 #define VIEWER_MENU BUTTON_MENU
315 #define VIEWER_AUTOSCROLL BUTTON_SELECT
317 #else
318 #error No keymap defined!
319 #endif
321 #ifdef HAVE_TOUCHSCREEN
322 #ifndef VIEWER_QUIT
323 #define VIEWER_QUIT BUTTON_TOPLEFT
324 #endif
325 #ifndef VIEWER_PAGE_UP
326 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
327 #endif
328 #ifndef VIEWER_PAGE_DOWN
329 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
330 #endif
331 #ifndef VIEWER_SCREEN_LEFT
332 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
333 #endif
334 #ifndef VIEWER_SCREEN_RIGHT
335 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
336 #endif
337 #ifndef VIEWER_MENU
338 #define VIEWER_MENU BUTTON_TOPRIGHT
339 #endif
340 #ifndef VIEWER_AUTOSCROLL
341 #define VIEWER_AUTOSCROLL BUTTON_CENTER
342 #endif
343 #endif
345 /* stuff for the bookmarking */
346 struct bookmarked_file_info {
347 long file_position;
348 int top_ptr_pos;
349 char filename[MAX_PATH];
352 struct bookmark_file_data {
353 signed int bookmarked_files_count;
354 struct bookmarked_file_info bookmarks[];
357 struct preferences {
358 enum {
359 WRAP=0,
360 CHOP,
361 } word_mode;
363 enum {
364 NORMAL=0,
365 JOIN,
366 EXPAND,
367 REFLOW, /* won't be set on charcell LCD, must be last */
368 } line_mode;
370 enum {
371 NARROW=0,
372 WIDE,
373 } view_mode;
375 enum codepages encoding;
377 #ifdef HAVE_LCD_BITMAP
378 enum {
379 SB_OFF=0,
380 SB_ON,
381 } scrollbar_mode;
382 bool need_scrollbar;
384 enum {
385 NO_OVERLAP=0,
386 OVERLAP,
387 } page_mode;
388 #endif /* HAVE_LCD_BITMAP */
390 enum {
391 PAGE=0,
392 LINE,
393 } scroll_mode;
395 int autoscroll_speed;
399 struct preferences prefs;
400 struct preferences old_prefs;
402 static unsigned char *buffer;
403 static long buffer_size;
404 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
405 static int display_columns; /* number of (pixel) columns on the display */
406 static int display_lines; /* number of lines on the display */
407 static int draw_columns; /* number of (pixel) columns available for text */
408 static int par_indent_spaces; /* number of spaces to indent first paragraph */
409 static int fd;
410 static const char *file_name;
411 static long file_size;
412 static long start_position; /* position in the file after the viewer is started */
413 static bool mac_text;
414 static long file_pos; /* Position of the top of the buffer in the file */
415 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
416 static int max_line_len;
417 static unsigned char *screen_top_ptr;
418 static unsigned char *next_screen_ptr;
419 static unsigned char *next_screen_to_draw_ptr;
420 static unsigned char *next_line_ptr;
421 static const struct plugin_api* rb;
422 #ifdef HAVE_LCD_BITMAP
423 static struct font *pf;
424 #endif
427 int glyph_width(int ch)
429 if (ch == 0)
430 ch = ' ';
432 #ifdef HAVE_LCD_BITMAP
433 return rb->font_get_width(pf, ch);
434 #else
435 return 1;
436 #endif
439 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
441 unsigned char utf8_tmp[6];
442 int count;
444 if (prefs.encoding == UTF_8)
445 return (unsigned char*)rb->utf8decode(str, ch);
447 count = BUFFER_OOB(str+2)? 1:2;
448 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
449 rb->utf8decode(utf8_tmp, ch);
451 #ifdef HAVE_LCD_BITMAP
452 if ((prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0) || prefs.encoding < SJIS)
453 return (unsigned char*)str+1;
454 else
455 #endif
456 return (unsigned char*)str+2;
459 bool done = false;
460 int col = 0;
462 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
463 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
464 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
465 static unsigned char* crop_at_width(const unsigned char* p)
467 int k,width;
468 unsigned short ch;
469 const unsigned char *oldp = p;
471 k=width=0;
473 while (LINE_IS_NOT_FULL) {
474 oldp = p;
475 p = get_ucs(p, &ch);
476 ADVANCE_COUNTERS(ch);
479 return (unsigned char*)oldp;
482 static unsigned char* find_first_feed(const unsigned char* p, int size)
484 int i;
486 for (i=0; i < size; i++)
487 if (p[i] == 0)
488 return (unsigned char*) p+i;
490 return NULL;
493 static unsigned char* find_last_feed(const unsigned char* p, int size)
495 int i;
497 for (i=size-1; i>=0; i--)
498 if (p[i] == 0)
499 return (unsigned char*) p+i;
501 return NULL;
504 static unsigned char* find_last_space(const unsigned char* p, int size)
506 int i, j, k;
508 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
510 if (!BUFFER_OOB(&p[size]))
511 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
512 if (p[size] == line_break[j])
513 return (unsigned char*) p+size;
515 for (i=size-1; i>=0; i--)
516 for (j=k; j < (int) sizeof(line_break); j++)
518 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
519 if (p[i] == line_break[j])
520 return (unsigned char*) p+i;
523 return NULL;
526 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
528 const unsigned char *next_line = NULL;
529 int size, i, j, k, width, search_len, spaces, newlines;
530 bool first_chars;
531 unsigned char c;
533 if (is_short != NULL)
534 *is_short = true;
536 if BUFFER_OOB(cur_line)
537 return NULL;
539 if (prefs.view_mode == WIDE) {
540 search_len = MAX_WIDTH;
542 else { /* prefs.view_mode == NARROW */
543 search_len = crop_at_width(cur_line) - cur_line;
546 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
548 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
549 /* Need to scan ahead and possibly increase search_len and size,
550 or possibly set next_line at second hard return in a row. */
551 next_line = NULL;
552 first_chars=true;
553 for (j=k=width=spaces=newlines=0; ; j++) {
554 if (BUFFER_OOB(cur_line+j))
555 return NULL;
556 if (LINE_IS_FULL) {
557 size = search_len = j;
558 break;
561 c = cur_line[j];
562 switch (c) {
563 case ' ':
564 if (prefs.line_mode == REFLOW) {
565 if (newlines > 0) {
566 size = j;
567 next_line = cur_line + size;
568 return (unsigned char*) next_line;
570 if (j==0) /* i=1 is intentional */
571 for (i=0; i<par_indent_spaces; i++)
572 ADVANCE_COUNTERS(' ');
574 if (!first_chars) spaces++;
575 break;
577 case 0:
578 if (newlines > 0) {
579 size = j;
580 next_line = cur_line + size - spaces;
581 if (next_line != cur_line)
582 return (unsigned char*) next_line;
583 break;
586 newlines++;
587 size += spaces -1;
588 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
589 return NULL;
590 search_len = size;
591 spaces = first_chars? 0:1;
592 break;
594 default:
595 if (prefs.line_mode==JOIN || newlines>0) {
596 while (spaces) {
597 spaces--;
598 ADVANCE_COUNTERS(' ');
599 if (LINE_IS_FULL) {
600 size = search_len = j;
601 break;
604 newlines=0;
605 } else if (spaces) {
606 /* REFLOW, multiple spaces between words: count only
607 * one. If more are needed, they will be added
608 * while drawing. */
609 search_len = size;
610 spaces=0;
611 ADVANCE_COUNTERS(' ');
612 if (LINE_IS_FULL) {
613 size = search_len = j;
614 break;
617 first_chars = false;
618 ADVANCE_COUNTERS(c);
619 break;
623 else {
624 /* find first hard return */
625 next_line = find_first_feed(cur_line, size);
628 if (next_line == NULL)
629 if (size == search_len) {
630 if (prefs.word_mode == WRAP) /* Find last space */
631 next_line = find_last_space(cur_line, size);
633 if (next_line == NULL)
634 next_line = crop_at_width(cur_line);
635 else
636 if (prefs.word_mode == WRAP)
637 for (i=0;
638 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
639 i++)
640 next_line++;
643 if (prefs.line_mode == EXPAND)
644 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
645 if (next_line[0] == 0)
646 if (next_line != cur_line)
647 return (unsigned char*) next_line;
649 /* If next_line is pointing to a zero, increment it; i.e.,
650 leave the terminator at the end of cur_line. If pointing
651 to a hyphen, increment only if there is room to display
652 the hyphen on current line (won't apply in WIDE mode,
653 since it's guarenteed there won't be room). */
654 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
655 if (next_line[0] == 0)/* ||
656 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
657 next_line++;
659 if (BUFFER_OOB(next_line))
660 return NULL;
662 if (is_short)
663 *is_short = false;
665 return (unsigned char*) next_line;
668 static unsigned char* find_prev_line(const unsigned char* cur_line)
670 const unsigned char *prev_line = NULL;
671 const unsigned char *p;
673 if BUFFER_OOB(cur_line)
674 return NULL;
676 /* To wrap consistently at the same places, we must
677 start with a known hard return, then work downwards.
678 We can either search backwards for a hard return,
679 or simply start wrapping downwards from top of buffer.
680 If current line is not near top of buffer, this is
681 a file with long lines (paragraphs). We would need to
682 read earlier sectors before we could decide how to
683 properly wrap the lines above the current line, but
684 it probably is not worth the disk access. Instead,
685 start with top of buffer and wrap down from there.
686 This may result in some lines wrapping at different
687 points from where they wrap when scrolling down.
688 If buffer is at top of file, start at top of buffer. */
690 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
691 prev_line = p = NULL;
692 else
693 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
694 /* Null means no line feeds in buffer above current line. */
696 if (prev_line == NULL)
697 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
698 prev_line = p = buffer;
699 /* (else return NULL and read previous block) */
701 /* Wrap downwards until too far, then use the one before. */
702 while (p < cur_line && p != NULL) {
703 prev_line = p;
704 p = find_next_line(prev_line, NULL);
707 if (BUFFER_OOB(prev_line))
708 return NULL;
710 return (unsigned char*) prev_line;
713 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
715 /* Read from file and preprocess the data */
716 /* To minimize disk access, always read on sector boundaries */
717 unsigned numread, i;
718 bool found_CR = false;
720 rb->lseek(fd, pos, SEEK_SET);
721 numread = rb->read(fd, buf, size);
722 rb->button_clear_queue(); /* clear button queue */
724 for(i = 0; i < numread; i++) {
725 switch(buf[i]) {
726 case '\r':
727 if (mac_text) {
728 buf[i] = 0;
730 else {
731 buf[i] = ' ';
732 found_CR = true;
734 break;
736 case '\n':
737 buf[i] = 0;
738 found_CR = false;
739 break;
741 case 0: /* No break between case 0 and default, intentionally */
742 buf[i] = ' ';
743 default:
744 if (found_CR) {
745 buf[i - 1] = 0;
746 found_CR = false;
747 mac_text = true;
749 break;
754 static int read_and_synch(int direction)
756 /* Read next (or prev) block, and reposition global pointers. */
757 /* direction: 1 for down (i.e., further into file), -1 for up */
758 int move_size, move_vector, offset;
759 unsigned char *fill_buf;
761 if (direction == -1) /* up */ {
762 move_size = SMALL_BLOCK_SIZE;
763 offset = 0;
764 fill_buf = TOP_SECTOR;
765 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
766 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
768 else /* down */ {
769 if (prefs.view_mode == WIDE) {
770 /* WIDE mode needs more buffer so we have to read smaller blocks */
771 move_size = SMALL_BLOCK_SIZE;
772 offset = LARGE_BLOCK_SIZE;
773 fill_buf = BOTTOM_SECTOR;
774 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
775 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
777 else {
778 move_size = LARGE_BLOCK_SIZE;
779 offset = SMALL_BLOCK_SIZE;
780 fill_buf = MID_SECTOR;
781 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
784 move_vector = direction * move_size;
785 screen_top_ptr -= move_vector;
786 file_pos += move_vector;
787 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
788 fill_buffer(file_pos + offset, fill_buf, move_size);
789 return move_vector;
792 static void viewer_scroll_up(void)
794 unsigned char *p;
796 p = find_prev_line(screen_top_ptr);
797 if (p == NULL && !BUFFER_BOF()) {
798 read_and_synch(-1);
799 p = find_prev_line(screen_top_ptr);
801 if (p != NULL)
802 screen_top_ptr = p;
805 static void viewer_scroll_down(void)
807 if (next_screen_ptr != NULL)
808 screen_top_ptr = next_line_ptr;
811 #ifdef HAVE_LCD_BITMAP
812 static void viewer_scrollbar(void) {
813 int items, min_shown, max_shown;
815 items = (int) file_size; /* (SH1 int is same as long) */
816 min_shown = (int) file_pos + (screen_top_ptr - buffer);
818 if (next_screen_ptr == NULL)
819 max_shown = items;
820 else
821 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
823 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
824 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
826 #endif
828 static void viewer_draw(int col)
830 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
831 int width, extra_spaces, indent_spaces, spaces_per_word;
832 bool multiple_spacing, line_is_short;
833 unsigned short ch;
834 unsigned char *str, *oldstr;
835 unsigned char *line_begin;
836 unsigned char *line_end;
837 unsigned char c;
838 unsigned char scratch_buffer[MAX_COLUMNS + 1];
839 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
840 unsigned char *endptr;
842 /* If col==-1 do all calculations but don't display */
843 if (col != -1) {
844 #ifdef HAVE_LCD_BITMAP
845 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
846 #else
847 left_col = 0;
848 #endif
849 rb->lcd_clear_display();
851 max_line_len = 0;
852 line_begin = line_end = screen_top_ptr;
854 for (i = 0; i < display_lines; i++) {
855 if (BUFFER_OOB(line_end))
856 break; /* Happens after display last line at BUFFER_EOF() */
858 line_begin = line_end;
859 line_end = find_next_line(line_begin, &line_is_short);
861 if (line_end == NULL) {
862 if (BUFFER_EOF()) {
863 if (i < display_lines - 1 && !BUFFER_BOF()) {
864 if (col != -1)
865 rb->lcd_clear_display();
867 for (; i < display_lines - 1; i++)
868 viewer_scroll_up();
870 line_begin = line_end = screen_top_ptr;
871 i = -1;
872 continue;
874 else {
875 line_end = buffer_end;
878 else {
879 resynch_move = read_and_synch(1); /* Read block & move ptrs */
880 line_begin -= resynch_move;
881 if (i > 0)
882 next_line_ptr -= resynch_move;
884 line_end = find_next_line(line_begin, NULL);
885 if (line_end == NULL) /* Should not really happen */
886 break;
889 line_len = line_end - line_begin;
891 /* calculate line_len */
892 str = oldstr = line_begin;
893 j = -1;
894 while (str < line_end) {
895 oldstr = str;
896 str = crop_at_width(str);
897 j++;
899 line_width = j*draw_columns;
900 while (oldstr < line_end) {
901 oldstr = get_ucs(oldstr, &ch);
902 line_width += glyph_width(ch);
905 if (prefs.line_mode == JOIN) {
906 if (line_begin[0] == 0) {
907 line_begin++;
908 if (prefs.word_mode == CHOP)
909 line_end++;
910 else
911 line_len--;
913 for (j=k=spaces=0; j < line_len; j++) {
914 if (k == MAX_COLUMNS)
915 break;
917 c = line_begin[j];
918 switch (c) {
919 case ' ':
920 spaces++;
921 break;
922 case 0:
923 spaces = 0;
924 scratch_buffer[k++] = ' ';
925 break;
926 default:
927 while (spaces) {
928 spaces--;
929 scratch_buffer[k++] = ' ';
930 if (k == MAX_COLUMNS - 1)
931 break;
933 scratch_buffer[k++] = c;
934 break;
937 if (col != -1) {
938 scratch_buffer[k] = 0;
939 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
940 prefs.encoding, draw_columns/glyph_width('i'));
941 *endptr = 0;
944 else if (prefs.line_mode == REFLOW) {
945 if (line_begin[0] == 0) {
946 line_begin++;
947 if (prefs.word_mode == CHOP)
948 line_end++;
949 else
950 line_len--;
953 indent_spaces = 0;
954 if (!line_is_short) {
955 multiple_spacing = false;
956 width=spaces=0;
957 for (str = line_begin; str < line_end; ) {
958 str = get_ucs(str, &ch);
959 switch (ch) {
960 case ' ':
961 case 0:
962 if ((str == line_begin) && (prefs.word_mode==WRAP))
963 /* special case: indent the paragraph,
964 * don't count spaces */
965 indent_spaces = par_indent_spaces;
966 else if (!multiple_spacing)
967 spaces++;
968 multiple_spacing = true;
969 break;
970 default:
971 multiple_spacing = false;
972 width += glyph_width(ch);
973 break;
976 if (multiple_spacing) spaces--;
978 if (spaces) {
979 /* total number of spaces to insert between words */
980 extra_spaces = (draw_columns-width)/glyph_width(' ')
981 - indent_spaces;
982 /* number of spaces between each word*/
983 spaces_per_word = extra_spaces / spaces;
984 /* number of words with n+1 spaces (to fill up) */
985 extra_spaces = extra_spaces % spaces;
986 if (spaces_per_word > 2) { /* too much spacing is awful */
987 spaces_per_word = 3;
988 extra_spaces = 0;
990 } else { /* this doesn't matter much... no spaces anyway */
991 spaces_per_word = extra_spaces = 0;
993 } else { /* end of a paragraph: don't fill line */
994 spaces_per_word = 1;
995 extra_spaces = 0;
998 multiple_spacing = false;
999 for (j=k=spaces=0; j < line_len; j++) {
1000 if (k == MAX_COLUMNS)
1001 break;
1003 c = line_begin[j];
1004 switch (c) {
1005 case ' ':
1006 case 0:
1007 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1008 for (j=0; j<par_indent_spaces; j++)
1009 scratch_buffer[k++] = ' ';
1010 j=0;
1012 else if (!multiple_spacing) {
1013 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1014 scratch_buffer[k++] = ' ';
1015 spaces++;
1017 multiple_spacing = true;
1018 break;
1019 default:
1020 scratch_buffer[k++] = c;
1021 multiple_spacing = false;
1022 break;
1026 if (col != -1) {
1027 scratch_buffer[k] = 0;
1028 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1029 prefs.encoding, k-col);
1030 *endptr = 0;
1033 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1034 if (col != -1)
1035 if (line_width > col) {
1036 str = oldstr = line_begin;
1037 k = col;
1038 width = 0;
1039 while( (width<draw_columns) && (oldstr<line_end) )
1041 oldstr = get_ucs(oldstr, &ch);
1042 if (k > 0) {
1043 k -= glyph_width(ch);
1044 line_begin = oldstr;
1045 } else {
1046 width += glyph_width(ch);
1050 if(prefs.view_mode==WIDE)
1051 endptr = rb->iso_decode(line_begin, utf8_buffer,
1052 prefs.encoding, oldstr-line_begin);
1053 else
1054 endptr = rb->iso_decode(line_begin, utf8_buffer,
1055 prefs.encoding, line_end-line_begin);
1056 *endptr = 0;
1059 if (col != -1 && line_width > col)
1060 #ifdef HAVE_LCD_BITMAP
1061 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1062 #else
1063 rb->lcd_puts(left_col, i, utf8_buffer);
1064 #endif
1065 if (line_width > max_line_len)
1066 max_line_len = line_width;
1068 if (i == 0)
1069 next_line_ptr = line_end;
1071 next_screen_ptr = line_end;
1072 if (BUFFER_OOB(next_screen_ptr))
1073 next_screen_ptr = NULL;
1075 #ifdef HAVE_LCD_BITMAP
1076 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1078 if (prefs.need_scrollbar)
1079 viewer_scrollbar();
1080 #else
1081 next_screen_to_draw_ptr = next_screen_ptr;
1082 #endif
1084 if (col != -1)
1085 rb->lcd_update();
1088 static void viewer_top(void)
1090 /* Read top of file into buffer
1091 and point screen pointer to top */
1092 if (file_pos != 0)
1094 file_pos = 0;
1095 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1096 fill_buffer(0, buffer, buffer_size);
1099 screen_top_ptr = buffer;
1102 static void viewer_bottom(void)
1104 /* Read bottom of file into buffer
1105 and point screen pointer to bottom */
1106 long last_sectors;
1108 if (file_size > buffer_size) {
1109 /* Find last buffer in file, round up to next sector boundary */
1110 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1111 last_sectors /= SMALL_BLOCK_SIZE;
1112 last_sectors *= SMALL_BLOCK_SIZE;
1114 else {
1115 last_sectors = 0;
1118 if (file_pos != last_sectors)
1120 file_pos = last_sectors;
1121 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1122 fill_buffer(last_sectors, buffer, buffer_size);
1125 screen_top_ptr = buffer_end-1;
1128 #ifdef HAVE_LCD_BITMAP
1129 static void init_need_scrollbar(void) {
1130 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1131 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1132 viewer_draw(-1);
1133 prefs.need_scrollbar = NEED_SCROLLBAR();
1134 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1135 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1137 #else
1138 #define init_need_scrollbar()
1139 #endif
1141 static bool viewer_init(void)
1143 #ifdef HAVE_LCD_BITMAP
1145 pf = rb->font_get(FONT_UI);
1147 display_lines = LCD_HEIGHT / pf->height;
1148 draw_columns = display_columns = LCD_WIDTH;
1149 #else
1150 /* REAL fixed pitch :) all chars use up 1 cell */
1151 display_lines = 2;
1152 draw_columns = display_columns = 11;
1153 par_indent_spaces = 2;
1154 #endif
1156 fd = rb->open(file_name, O_RDONLY);
1157 if (fd==-1)
1158 return false;
1160 file_size = rb->filesize(fd);
1161 if (file_size==-1)
1162 return false;
1164 /* Init mac_text value used in processing buffer */
1165 mac_text = false;
1167 return true;
1170 static void viewer_default_settings(void)
1172 prefs.word_mode = WRAP;
1173 prefs.line_mode = NORMAL;
1174 prefs.view_mode = NARROW;
1175 prefs.scroll_mode = PAGE;
1176 #ifdef HAVE_LCD_BITMAP
1177 prefs.page_mode = NO_OVERLAP;
1178 prefs.scrollbar_mode = SB_OFF;
1179 #endif
1180 prefs.autoscroll_speed = 1;
1181 /* Set codepage to system default */
1182 prefs.encoding = rb->global_settings->default_codepage;
1185 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1187 int settings_fd, i;
1188 struct bookmark_file_data *data;
1189 struct bookmarked_file_info this_bookmark;
1191 /* read settings file */
1192 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1193 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1195 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1196 rb->close(settings_fd);
1198 else
1200 /* load default settings if there is no settings file */
1201 viewer_default_settings();
1204 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1206 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1207 data->bookmarked_files_count = 0;
1209 /* read bookmarks if file exists */
1210 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1211 if (settings_fd >= 0)
1213 /* figure out how many items to read */
1214 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1215 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1216 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1217 rb->read(settings_fd, data->bookmarks,
1218 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1219 rb->close(settings_fd);
1222 file_pos = 0;
1223 screen_top_ptr = buffer;
1225 /* check if current file is in list */
1226 for (i=0; i < data->bookmarked_files_count; i++)
1228 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1230 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1231 int screen_top = screen_pos % buffer_size;
1232 file_pos = screen_pos - screen_top;
1233 screen_top_ptr = buffer + screen_top;
1234 break;
1238 this_bookmark.file_position = file_pos;
1239 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1241 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1242 rb->strcpy(this_bookmark.filename,file_name);
1244 /* prevent potential slot overflow */
1245 if (i >= data->bookmarked_files_count)
1247 if (i < MAX_BOOKMARKED_FILES)
1248 data->bookmarked_files_count++;
1249 else
1250 i = MAX_BOOKMARKED_FILES-1;
1253 /* write bookmark file with spare slot in first position
1254 to be filled in by viewer_save_settings */
1255 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1256 if (settings_fd >=0 )
1258 /* write count */
1259 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1261 /* write the current bookmark */
1262 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1264 /* write everything that was before this bookmark */
1265 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1267 rb->close(settings_fd);
1270 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1272 if (BUFFER_OOB(screen_top_ptr))
1274 screen_top_ptr = buffer;
1277 fill_buffer(file_pos, buffer, buffer_size);
1279 /* remember the current position */
1280 start_position = file_pos + screen_top_ptr - buffer;
1282 init_need_scrollbar();
1285 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1287 int settings_fd;
1289 /* save the viewer settings if they have been changed */
1290 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1292 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1294 if (settings_fd >= 0 )
1296 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1297 rb->close(settings_fd);
1301 /* save the bookmark if the position has changed */
1302 if (file_pos + screen_top_ptr - buffer != start_position)
1304 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1306 if (settings_fd >= 0 )
1308 struct bookmarked_file_info b;
1309 b.file_position = file_pos + screen_top_ptr - buffer;
1310 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1311 rb->memset(&b.filename[0],0,MAX_PATH);
1312 rb->strcpy(b.filename,file_name);
1313 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1314 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1315 rb->close(settings_fd);
1320 static void viewer_exit(void *parameter)
1322 (void)parameter;
1324 viewer_save_settings();
1325 rb->close(fd);
1328 static int col_limit(int col)
1330 if (col < 0)
1331 col = 0;
1332 else
1333 if (col > max_line_len - 2*glyph_width('o'))
1334 col = max_line_len - 2*glyph_width('o');
1336 return col;
1339 /* settings helper functions */
1341 static bool encoding_setting(void)
1343 static struct opt_items names[NUM_CODEPAGES];
1344 int idx;
1346 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1348 names[idx].string = rb->get_codepage_name(idx);
1349 names[idx].voice_id = -1;
1352 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1353 sizeof(names) / sizeof(names[0]), NULL);
1356 static bool word_wrap_setting(void)
1358 static const struct opt_items names[] = {
1359 {"On", -1},
1360 {"Off (Chop Words)", -1},
1363 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1364 names, 2, NULL);
1367 static bool line_mode_setting(void)
1369 static const struct opt_items names[] = {
1370 {"Normal", -1},
1371 {"Join Lines", -1},
1372 {"Expand Lines", -1},
1373 #ifdef HAVE_LCD_BITMAP
1374 {"Reflow Lines", -1},
1375 #endif
1378 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1379 sizeof(names) / sizeof(names[0]), NULL);
1382 static bool view_mode_setting(void)
1384 static const struct opt_items names[] = {
1385 {"No (Narrow)", -1},
1386 {"Yes", -1},
1388 bool ret;
1389 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1390 names , 2, NULL);
1391 if (prefs.view_mode == NARROW)
1392 col = 0;
1393 return ret;
1396 static bool scroll_mode_setting(void)
1398 static const struct opt_items names[] = {
1399 {"Scroll by Page", -1},
1400 {"Scroll by Line", -1},
1403 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1404 names, 2, NULL);
1407 #ifdef HAVE_LCD_BITMAP
1408 static bool page_mode_setting(void)
1410 static const struct opt_items names[] = {
1411 {"No", -1},
1412 {"Yes", -1},
1415 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1416 names, 2, NULL);
1419 static bool scrollbar_setting(void)
1421 static const struct opt_items names[] = {
1422 {"Off", -1},
1423 {"On", -1}
1426 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1427 names, 2, NULL);
1429 #endif
1431 static bool autoscroll_speed_setting(void)
1433 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1434 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1437 static bool viewer_options_menu(void)
1439 int m;
1440 bool result;
1442 static const struct menu_item items[] = {
1443 {"Encoding", encoding_setting },
1444 {"Word Wrap", word_wrap_setting },
1445 {"Line Mode", line_mode_setting },
1446 {"Wide View", view_mode_setting },
1447 #ifdef HAVE_LCD_BITMAP
1448 {"Show Scrollbar", scrollbar_setting },
1449 {"Overlap Pages", page_mode_setting },
1450 #endif
1451 {"Scroll Mode", scroll_mode_setting},
1452 {"Auto-Scroll Speed", autoscroll_speed_setting },
1454 m = menu_init(rb, items, sizeof(items) / sizeof(*items),
1455 NULL, NULL, NULL, NULL);
1457 result = menu_run(m);
1458 menu_exit(m);
1459 #ifdef HAVE_LCD_BITMAP
1461 /* Show-scrollbar mode for current view-width mode */
1462 init_need_scrollbar();
1463 #endif
1464 return result;
1467 static void viewer_menu(void)
1469 int m;
1470 int result;
1471 static const struct menu_item items[] = {
1472 {"Quit", NULL },
1473 {"Viewer Options", NULL },
1474 {"Show Playback Menu", NULL },
1475 {"Return", NULL },
1478 m = menu_init(rb, items, sizeof(items) / sizeof(*items), NULL, NULL, NULL, NULL);
1479 result=menu_show(m);
1480 switch (result)
1482 case 0: /* quit */
1483 menu_exit(m);
1484 viewer_exit(NULL);
1485 done = true;
1486 break;
1487 case 1: /* change settings */
1488 done = viewer_options_menu();
1489 break;
1490 case 2: /* playback control */
1491 playback_control(rb, NULL);
1492 break;
1493 case 3: /* return */
1494 break;
1496 menu_exit(m);
1497 viewer_draw(col);
1500 enum plugin_status plugin_start(const struct plugin_api* api, const void* file)
1502 int button, i, ok;
1503 int lastbutton = BUTTON_NONE;
1504 bool autoscroll = false;
1505 long old_tick;
1507 rb = api;
1508 old_tick = *rb->current_tick;
1510 /* get the plugin buffer */
1511 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1513 if (!file)
1514 return PLUGIN_ERROR;
1516 file_name = file;
1517 ok = viewer_init();
1518 if (!ok) {
1519 rb->splash(HZ, "Error opening file.");
1520 return PLUGIN_ERROR;
1523 viewer_load_settings(); /* load the preferences and bookmark */
1525 #if LCD_DEPTH > 1
1526 rb->lcd_set_backdrop(NULL);
1527 #endif
1529 viewer_draw(col);
1531 while (!done) {
1533 if(autoscroll)
1535 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1537 viewer_scroll_down();
1538 viewer_draw(col);
1539 old_tick = *rb->current_tick;
1543 button = rb->button_get_w_tmo(HZ/10);
1544 switch (button) {
1545 case VIEWER_MENU:
1546 viewer_menu();
1547 break;
1549 case VIEWER_AUTOSCROLL:
1550 #ifdef VIEWER_AUTOSCROLL_PRE
1551 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1552 break;
1553 #endif
1554 autoscroll = !autoscroll;
1555 break;
1557 case VIEWER_PAGE_UP:
1558 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1559 if (prefs.scroll_mode == PAGE)
1561 /* Page up */
1562 #ifdef HAVE_LCD_BITMAP
1563 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1564 #else
1565 for (i = 0; i < display_lines; i++)
1566 #endif
1567 viewer_scroll_up();
1569 else
1570 viewer_scroll_up();
1571 old_tick = *rb->current_tick;
1572 viewer_draw(col);
1573 break;
1575 case VIEWER_PAGE_DOWN:
1576 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1577 if (prefs.scroll_mode == PAGE)
1579 /* Page down */
1580 if (next_screen_ptr != NULL)
1581 screen_top_ptr = next_screen_to_draw_ptr;
1583 else
1584 viewer_scroll_down();
1585 old_tick = *rb->current_tick;
1586 viewer_draw(col);
1587 break;
1589 case VIEWER_SCREEN_LEFT:
1590 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1591 if (prefs.view_mode == WIDE) {
1592 /* Screen left */
1593 col -= draw_columns;
1594 col = col_limit(col);
1596 else { /* prefs.view_mode == NARROW */
1597 /* Top of file */
1598 viewer_top();
1601 viewer_draw(col);
1602 break;
1604 case VIEWER_SCREEN_RIGHT:
1605 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1606 if (prefs.view_mode == WIDE) {
1607 /* Screen right */
1608 col += draw_columns;
1609 col = col_limit(col);
1611 else { /* prefs.view_mode == NARROW */
1612 /* Bottom of file */
1613 viewer_bottom();
1616 viewer_draw(col);
1617 break;
1619 #ifdef VIEWER_LINE_UP
1620 case VIEWER_LINE_UP:
1621 case VIEWER_LINE_UP | BUTTON_REPEAT:
1622 /* Scroll up one line */
1623 viewer_scroll_up();
1624 old_tick = *rb->current_tick;
1625 viewer_draw(col);
1626 break;
1628 case VIEWER_LINE_DOWN:
1629 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1630 /* Scroll down one line */
1631 if (next_screen_ptr != NULL)
1632 screen_top_ptr = next_line_ptr;
1633 old_tick = *rb->current_tick;
1634 viewer_draw(col);
1635 break;
1636 #endif
1637 #ifdef VIEWER_COLUMN_LEFT
1638 case VIEWER_COLUMN_LEFT:
1639 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1640 if (prefs.view_mode == WIDE) {
1641 /* Scroll left one column */
1642 col -= glyph_width('o');
1643 col = col_limit(col);
1644 viewer_draw(col);
1646 break;
1648 case VIEWER_COLUMN_RIGHT:
1649 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1650 if (prefs.view_mode == WIDE) {
1651 /* Scroll right one column */
1652 col += glyph_width('o');
1653 col = col_limit(col);
1654 viewer_draw(col);
1656 break;
1657 #endif
1659 #ifdef VIEWER_RC_QUIT
1660 case VIEWER_RC_QUIT:
1661 #endif
1662 case VIEWER_QUIT:
1663 viewer_exit(NULL);
1664 done = true;
1665 break;
1667 default:
1668 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1669 == SYS_USB_CONNECTED)
1670 return PLUGIN_USB_CONNECTED;
1671 break;
1673 if (button != BUTTON_NONE)
1675 lastbutton = button;
1676 rb->yield();
1679 return PLUGIN_OK;