fix red.
[kugel-rb/myfork.git] / apps / plugins / viewer.c
blobf2a3f9f029fb95730a8f4198124cfa27d0da8549
1 /***************************************************************************
3 * __________ __ ___.
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
8 * \/ \/ \/ \/ \/
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "plugin.h"
23 #include <ctype.h>
24 #include "lib/playback_control.h"
26 PLUGIN_HEADER
28 #define SETTINGS_FILE VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
29 #define BOOKMARKS_FILE VIEWERS_DIR "/viewer_bookmarks.dat"
31 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
32 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
33 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
34 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
35 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
36 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
37 #define TOP_SECTOR buffer
38 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
39 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
40 #undef SCROLLBAR_WIDTH
41 #define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width
43 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
45 /* Out-Of-Bounds test for any pointer to data in the buffer */
46 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
48 /* Does the buffer contain the beginning of the file? */
49 #define BUFFER_BOF() (file_pos==0)
51 /* Does the buffer contain the end of the file? */
52 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
54 /* Formula for the endpoint address outside of buffer data */
55 #define BUFFER_END() \
56 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
58 /* Is the entire file being shown in one screen? */
59 #define ONE_SCREEN_FITS_ALL() \
60 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
62 /* Is a scrollbar called for on the current screen? */
63 #define NEED_SCROLLBAR() \
64 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
66 /* variable button definitions */
68 /* Recorder keys */
69 #if CONFIG_KEYPAD == RECORDER_PAD
70 #define VIEWER_QUIT BUTTON_OFF
71 #define VIEWER_PAGE_UP BUTTON_UP
72 #define VIEWER_PAGE_DOWN BUTTON_DOWN
73 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
74 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
75 #define VIEWER_MENU BUTTON_F1
76 #define VIEWER_AUTOSCROLL BUTTON_PLAY
77 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
78 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
79 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
80 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
82 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
83 #define VIEWER_QUIT BUTTON_OFF
84 #define VIEWER_PAGE_UP BUTTON_UP
85 #define VIEWER_PAGE_DOWN BUTTON_DOWN
86 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
87 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
88 #define VIEWER_MENU BUTTON_F1
89 #define VIEWER_AUTOSCROLL BUTTON_SELECT
90 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
91 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
92 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
93 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
95 /* Ondio keys */
96 #elif CONFIG_KEYPAD == ONDIO_PAD
97 #define VIEWER_QUIT BUTTON_OFF
98 #define VIEWER_PAGE_UP BUTTON_UP
99 #define VIEWER_PAGE_DOWN BUTTON_DOWN
100 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
101 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
102 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
103 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
104 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
106 /* Player keys */
107 #elif CONFIG_KEYPAD == PLAYER_PAD
108 #define VIEWER_QUIT BUTTON_STOP
109 #define VIEWER_PAGE_UP BUTTON_LEFT
110 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
111 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
112 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
113 #define VIEWER_MENU BUTTON_MENU
114 #define VIEWER_AUTOSCROLL BUTTON_PLAY
116 /* iRiver H1x0 && H3x0 keys */
117 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
118 (CONFIG_KEYPAD == IRIVER_H300_PAD)
119 #define VIEWER_QUIT BUTTON_OFF
120 #define VIEWER_PAGE_UP BUTTON_UP
121 #define VIEWER_PAGE_DOWN BUTTON_DOWN
122 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
123 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
124 #define VIEWER_MENU BUTTON_MODE
125 #define VIEWER_AUTOSCROLL BUTTON_SELECT
126 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
127 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
128 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
129 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
131 #define VIEWER_RC_QUIT BUTTON_RC_STOP
133 /* iPods */
134 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
135 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
136 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
137 #define VIEWER_QUIT_PRE BUTTON_SELECT
138 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
139 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
140 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
141 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
142 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
143 #define VIEWER_MENU BUTTON_MENU
144 #define VIEWER_AUTOSCROLL BUTTON_PLAY
146 /* iFP7xx keys */
147 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
148 #define VIEWER_QUIT BUTTON_PLAY
149 #define VIEWER_PAGE_UP BUTTON_UP
150 #define VIEWER_PAGE_DOWN BUTTON_DOWN
151 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
152 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
153 #define VIEWER_MENU BUTTON_MODE
154 #define VIEWER_AUTOSCROLL BUTTON_SELECT
156 /* iAudio X5 keys */
157 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
158 #define VIEWER_QUIT BUTTON_POWER
159 #define VIEWER_PAGE_UP BUTTON_UP
160 #define VIEWER_PAGE_DOWN BUTTON_DOWN
161 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
162 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
163 #define VIEWER_MENU BUTTON_SELECT
164 #define VIEWER_AUTOSCROLL BUTTON_PLAY
166 /* GIGABEAT keys */
167 #elif CONFIG_KEYPAD == GIGABEAT_PAD
168 #define VIEWER_QUIT BUTTON_POWER
169 #define VIEWER_PAGE_UP BUTTON_UP
170 #define VIEWER_PAGE_DOWN BUTTON_DOWN
171 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
172 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
173 #define VIEWER_MENU BUTTON_MENU
174 #define VIEWER_AUTOSCROLL BUTTON_A
176 /* Sansa E200 keys */
177 #elif CONFIG_KEYPAD == SANSA_E200_PAD
178 #define VIEWER_QUIT BUTTON_POWER
179 #define VIEWER_PAGE_UP BUTTON_UP
180 #define VIEWER_PAGE_DOWN BUTTON_DOWN
181 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
182 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
183 #define VIEWER_MENU BUTTON_SELECT
184 #define VIEWER_AUTOSCROLL BUTTON_REC
185 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
186 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
188 /* Sansa Fuze keys */
189 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
190 #define VIEWER_QUIT (BUTTON_HOME|BUTTON_REPEAT)
191 #define VIEWER_PAGE_UP BUTTON_UP
192 #define VIEWER_PAGE_DOWN BUTTON_DOWN
193 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
194 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
195 #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT
196 #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
197 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
198 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
200 /* Sansa C200 keys */
201 #elif CONFIG_KEYPAD == SANSA_C200_PAD
202 #define VIEWER_QUIT BUTTON_POWER
203 #define VIEWER_PAGE_UP BUTTON_VOL_UP
204 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
205 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
206 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
207 #define VIEWER_MENU BUTTON_SELECT
208 #define VIEWER_AUTOSCROLL BUTTON_REC
209 #define VIEWER_LINE_UP BUTTON_UP
210 #define VIEWER_LINE_DOWN BUTTON_DOWN
212 /* Sansa Clip keys */
213 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
214 #define VIEWER_QUIT BUTTON_POWER
215 #define VIEWER_PAGE_UP BUTTON_VOL_UP
216 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
217 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
218 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
219 #define VIEWER_MENU BUTTON_SELECT
220 #define VIEWER_AUTOSCROLL BUTTON_HOME
221 #define VIEWER_LINE_UP BUTTON_UP
222 #define VIEWER_LINE_DOWN BUTTON_DOWN
224 /* Sansa M200 keys */
225 #elif CONFIG_KEYPAD == SANSA_M200_PAD
226 #define VIEWER_QUIT BUTTON_POWER
227 #define VIEWER_PAGE_UP BUTTON_VOL_UP
228 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
229 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
230 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
231 #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP)
232 #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
233 #define VIEWER_LINE_UP BUTTON_UP
234 #define VIEWER_LINE_DOWN BUTTON_DOWN
236 /* iriver H10 keys */
237 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
238 #define VIEWER_QUIT BUTTON_POWER
239 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
240 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
241 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
242 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
243 #define VIEWER_MENU BUTTON_REW
244 #define VIEWER_AUTOSCROLL BUTTON_PLAY
246 /*M-Robe 500 keys */
247 #elif CONFIG_KEYPAD == MROBE500_PAD
248 #define VIEWER_QUIT BUTTON_POWER
249 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
250 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
251 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
252 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
253 #define VIEWER_MENU BUTTON_RC_HEART
254 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
256 /*Gigabeat S keys */
257 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
258 #define VIEWER_QUIT BUTTON_BACK
259 #define VIEWER_PAGE_UP BUTTON_PREV
260 #define VIEWER_PAGE_DOWN BUTTON_NEXT
261 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
262 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
263 #define VIEWER_MENU BUTTON_MENU
264 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
265 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
266 #define VIEWER_LINE_UP BUTTON_UP
267 #define VIEWER_LINE_DOWN BUTTON_DOWN
268 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
269 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
271 /*M-Robe 100 keys */
272 #elif CONFIG_KEYPAD == MROBE100_PAD
273 #define VIEWER_QUIT BUTTON_POWER
274 #define VIEWER_PAGE_UP BUTTON_UP
275 #define VIEWER_PAGE_DOWN BUTTON_DOWN
276 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
277 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
278 #define VIEWER_MENU BUTTON_MENU
279 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
281 /* iAUdio M3 keys */
282 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
283 #define VIEWER_QUIT BUTTON_RC_REC
284 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
285 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
286 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
287 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
288 #define VIEWER_MENU BUTTON_RC_MENU
289 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
290 #define VIEWER_RC_QUIT BUTTON_REC
292 /* Cowon D2 keys */
293 #elif CONFIG_KEYPAD == COWOND2_PAD
294 #define VIEWER_QUIT BUTTON_POWER
295 #define VIEWER_MENU BUTTON_MENU
297 #elif CONFIG_KEYPAD == IAUDIO67_PAD
298 #define VIEWER_QUIT BUTTON_POWER
299 #define VIEWER_PAGE_UP BUTTON_VOLUP
300 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
301 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
302 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
303 #define VIEWER_MENU BUTTON_MENU
304 #define VIEWER_AUTOSCROLL BUTTON_PLAY
305 #define VIEWER_RC_QUIT BUTTON_STOP
307 /* Creative Zen Vision:M keys */
308 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
309 #define VIEWER_QUIT BUTTON_BACK
310 #define VIEWER_PAGE_UP BUTTON_UP
311 #define VIEWER_PAGE_DOWN BUTTON_DOWN
312 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
313 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
314 #define VIEWER_MENU BUTTON_MENU
315 #define VIEWER_AUTOSCROLL BUTTON_SELECT
317 /* Philips HDD1630 keys */
318 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
319 #define VIEWER_QUIT BUTTON_POWER
320 #define VIEWER_PAGE_UP BUTTON_UP
321 #define VIEWER_PAGE_DOWN BUTTON_DOWN
322 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
323 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
324 #define VIEWER_MENU BUTTON_MENU
325 #define VIEWER_AUTOSCROLL BUTTON_VIEW
327 /* Onda VX747 keys */
328 #elif CONFIG_KEYPAD == ONDAVX747_PAD
329 #define VIEWER_QUIT BUTTON_POWER
330 #define VIEWER_MENU BUTTON_MENU
332 /* SAMSUNG YH-820 / YH-920 / YH-925 keys */
333 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
334 #define VIEWER_QUIT BUTTON_REC
335 #define VIEWER_PAGE_UP BUTTON_UP
336 #define VIEWER_PAGE_DOWN BUTTON_DOWN
337 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
338 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
339 #define VIEWER_MENU BUTTON_PLAY
340 #define VIEWER_AUTOSCROLL BUTTON_REW
342 #else
343 #error No keymap defined!
344 #endif
346 #ifdef HAVE_TOUCHSCREEN
347 #ifndef VIEWER_QUIT
348 #define VIEWER_QUIT BUTTON_TOPLEFT
349 #endif
350 #ifndef VIEWER_PAGE_UP
351 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
352 #endif
353 #ifndef VIEWER_PAGE_DOWN
354 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
355 #endif
356 #ifndef VIEWER_SCREEN_LEFT
357 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
358 #endif
359 #ifndef VIEWER_SCREEN_RIGHT
360 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
361 #endif
362 #ifndef VIEWER_MENU
363 #define VIEWER_MENU BUTTON_TOPRIGHT
364 #endif
365 #ifndef VIEWER_AUTOSCROLL
366 #define VIEWER_AUTOSCROLL BUTTON_CENTER
367 #endif
368 #endif
370 /* stuff for the bookmarking */
371 struct bookmarked_file_info {
372 long file_position;
373 int top_ptr_pos;
374 char filename[MAX_PATH];
377 struct bookmark_file_data {
378 signed int bookmarked_files_count;
379 struct bookmarked_file_info bookmarks[];
382 struct preferences {
383 enum {
384 WRAP=0,
385 CHOP,
386 } word_mode;
388 enum {
389 NORMAL=0,
390 JOIN,
391 EXPAND,
392 REFLOW, /* won't be set on charcell LCD, must be last */
393 } line_mode;
395 enum {
396 NARROW=0,
397 WIDE,
398 } view_mode;
400 enum codepages encoding;
402 #ifdef HAVE_LCD_BITMAP
403 enum {
404 SB_OFF=0,
405 SB_ON,
406 } scrollbar_mode;
407 bool need_scrollbar;
409 enum {
410 NO_OVERLAP=0,
411 OVERLAP,
412 } page_mode;
413 #endif /* HAVE_LCD_BITMAP */
415 enum {
416 PAGE=0,
417 LINE,
418 } scroll_mode;
420 int autoscroll_speed;
424 struct preferences prefs;
425 struct preferences old_prefs;
427 static unsigned char *buffer;
428 static long buffer_size;
429 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
430 static int display_columns; /* number of (pixel) columns on the display */
431 static int display_lines; /* number of lines on the display */
432 static int draw_columns; /* number of (pixel) columns available for text */
433 static int par_indent_spaces; /* number of spaces to indent first paragraph */
434 static int fd;
435 static const char *file_name;
436 static long file_size;
437 static long start_position; /* position in the file after the viewer is started */
438 static bool mac_text;
439 static long file_pos; /* Position of the top of the buffer in the file */
440 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
441 static int max_line_len;
442 static unsigned char *screen_top_ptr;
443 static unsigned char *next_screen_ptr;
444 static unsigned char *next_screen_to_draw_ptr;
445 static unsigned char *next_line_ptr;
446 #ifdef HAVE_LCD_BITMAP
447 static struct font *pf;
448 #endif
451 int glyph_width(int ch)
453 if (ch == 0)
454 ch = ' ';
456 #ifdef HAVE_LCD_BITMAP
457 return rb->font_get_width(pf, ch);
458 #else
459 return 1;
460 #endif
463 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
465 unsigned char utf8_tmp[6];
466 int count;
468 if (prefs.encoding == UTF_8)
469 return (unsigned char*)rb->utf8decode(str, ch);
471 count = BUFFER_OOB(str+2)? 1:2;
472 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
473 rb->utf8decode(utf8_tmp, ch);
475 #ifdef HAVE_LCD_BITMAP
476 if (prefs.encoding >= SJIS && *str >= 0x80
477 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
478 return (unsigned char*)str+2;
479 else
480 #endif
481 return (unsigned char*)str+1;
484 bool done = false;
485 int col = 0;
487 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
488 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
489 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
490 static unsigned char* crop_at_width(const unsigned char* p)
492 int k,width;
493 unsigned short ch;
494 const unsigned char *oldp = p;
496 k=width=0;
498 while (LINE_IS_NOT_FULL) {
499 oldp = p;
500 p = get_ucs(p, &ch);
501 ADVANCE_COUNTERS(ch);
504 return (unsigned char*)oldp;
507 static unsigned char* find_first_feed(const unsigned char* p, int size)
509 int i;
511 for (i=0; i < size; i++)
512 if (p[i] == 0)
513 return (unsigned char*) p+i;
515 return NULL;
518 static unsigned char* find_last_feed(const unsigned char* p, int size)
520 int i;
522 for (i=size-1; i>=0; i--)
523 if (p[i] == 0)
524 return (unsigned char*) p+i;
526 return NULL;
529 static unsigned char* find_last_space(const unsigned char* p, int size)
531 int i, j, k;
533 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
535 if (!BUFFER_OOB(&p[size]))
536 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
537 if (p[size] == line_break[j])
538 return (unsigned char*) p+size;
540 for (i=size-1; i>=0; i--)
541 for (j=k; j < (int) sizeof(line_break); j++)
543 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
544 if (p[i] == line_break[j])
545 return (unsigned char*) p+i;
548 return NULL;
551 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
553 const unsigned char *next_line = NULL;
554 int size, i, j, k, width, search_len, spaces, newlines;
555 bool first_chars;
556 unsigned char c;
558 if (is_short != NULL)
559 *is_short = true;
561 if BUFFER_OOB(cur_line)
562 return NULL;
564 if (prefs.view_mode == WIDE) {
565 search_len = MAX_WIDTH;
567 else { /* prefs.view_mode == NARROW */
568 search_len = crop_at_width(cur_line) - cur_line;
571 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
573 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
574 /* Need to scan ahead and possibly increase search_len and size,
575 or possibly set next_line at second hard return in a row. */
576 next_line = NULL;
577 first_chars=true;
578 for (j=k=width=spaces=newlines=0; ; j++) {
579 if (BUFFER_OOB(cur_line+j))
580 return NULL;
581 if (LINE_IS_FULL) {
582 size = search_len = j;
583 break;
586 c = cur_line[j];
587 switch (c) {
588 case ' ':
589 if (prefs.line_mode == REFLOW) {
590 if (newlines > 0) {
591 size = j;
592 next_line = cur_line + size;
593 return (unsigned char*) next_line;
595 if (j==0) /* i=1 is intentional */
596 for (i=0; i<par_indent_spaces; i++)
597 ADVANCE_COUNTERS(' ');
599 if (!first_chars) spaces++;
600 break;
602 case 0:
603 if (newlines > 0) {
604 size = j;
605 next_line = cur_line + size - spaces;
606 if (next_line != cur_line)
607 return (unsigned char*) next_line;
608 break;
611 newlines++;
612 size += spaces -1;
613 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
614 return NULL;
615 search_len = size;
616 spaces = first_chars? 0:1;
617 break;
619 default:
620 if (prefs.line_mode==JOIN || newlines>0) {
621 while (spaces) {
622 spaces--;
623 ADVANCE_COUNTERS(' ');
624 if (LINE_IS_FULL) {
625 size = search_len = j;
626 break;
629 newlines=0;
630 } else if (spaces) {
631 /* REFLOW, multiple spaces between words: count only
632 * one. If more are needed, they will be added
633 * while drawing. */
634 search_len = size;
635 spaces=0;
636 ADVANCE_COUNTERS(' ');
637 if (LINE_IS_FULL) {
638 size = search_len = j;
639 break;
642 first_chars = false;
643 ADVANCE_COUNTERS(c);
644 break;
648 else {
649 /* find first hard return */
650 next_line = find_first_feed(cur_line, size);
653 if (next_line == NULL)
654 if (size == search_len) {
655 if (prefs.word_mode == WRAP) /* Find last space */
656 next_line = find_last_space(cur_line, size);
658 if (next_line == NULL)
659 next_line = crop_at_width(cur_line);
660 else
661 if (prefs.word_mode == WRAP)
662 for (i=0;
663 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
664 i++)
665 next_line++;
668 if (prefs.line_mode == EXPAND)
669 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
670 if (next_line[0] == 0)
671 if (next_line != cur_line)
672 return (unsigned char*) next_line;
674 /* If next_line is pointing to a zero, increment it; i.e.,
675 leave the terminator at the end of cur_line. If pointing
676 to a hyphen, increment only if there is room to display
677 the hyphen on current line (won't apply in WIDE mode,
678 since it's guarenteed there won't be room). */
679 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
680 if (next_line[0] == 0)/* ||
681 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
682 next_line++;
684 if (BUFFER_OOB(next_line))
685 return NULL;
687 if (is_short)
688 *is_short = false;
690 return (unsigned char*) next_line;
693 static unsigned char* find_prev_line(const unsigned char* cur_line)
695 const unsigned char *prev_line = NULL;
696 const unsigned char *p;
698 if BUFFER_OOB(cur_line)
699 return NULL;
701 /* To wrap consistently at the same places, we must
702 start with a known hard return, then work downwards.
703 We can either search backwards for a hard return,
704 or simply start wrapping downwards from top of buffer.
705 If current line is not near top of buffer, this is
706 a file with long lines (paragraphs). We would need to
707 read earlier sectors before we could decide how to
708 properly wrap the lines above the current line, but
709 it probably is not worth the disk access. Instead,
710 start with top of buffer and wrap down from there.
711 This may result in some lines wrapping at different
712 points from where they wrap when scrolling down.
713 If buffer is at top of file, start at top of buffer. */
715 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
716 prev_line = p = NULL;
717 else
718 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
719 /* Null means no line feeds in buffer above current line. */
721 if (prev_line == NULL)
722 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
723 prev_line = p = buffer;
724 /* (else return NULL and read previous block) */
726 /* Wrap downwards until too far, then use the one before. */
727 while (p < cur_line && p != NULL) {
728 prev_line = p;
729 p = find_next_line(prev_line, NULL);
732 if (BUFFER_OOB(prev_line))
733 return NULL;
735 return (unsigned char*) prev_line;
738 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
740 /* Read from file and preprocess the data */
741 /* To minimize disk access, always read on sector boundaries */
742 unsigned numread, i;
743 bool found_CR = false;
745 rb->lseek(fd, pos, SEEK_SET);
746 numread = rb->read(fd, buf, size);
747 rb->button_clear_queue(); /* clear button queue */
749 for(i = 0; i < numread; i++) {
750 switch(buf[i]) {
751 case '\r':
752 if (mac_text) {
753 buf[i] = 0;
755 else {
756 buf[i] = ' ';
757 found_CR = true;
759 break;
761 case '\n':
762 buf[i] = 0;
763 found_CR = false;
764 break;
766 case 0: /* No break between case 0 and default, intentionally */
767 buf[i] = ' ';
768 default:
769 if (found_CR) {
770 buf[i - 1] = 0;
771 found_CR = false;
772 mac_text = true;
774 break;
779 static int read_and_synch(int direction)
781 /* Read next (or prev) block, and reposition global pointers. */
782 /* direction: 1 for down (i.e., further into file), -1 for up */
783 int move_size, move_vector, offset;
784 unsigned char *fill_buf;
786 if (direction == -1) /* up */ {
787 move_size = SMALL_BLOCK_SIZE;
788 offset = 0;
789 fill_buf = TOP_SECTOR;
790 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
791 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
793 else /* down */ {
794 if (prefs.view_mode == WIDE) {
795 /* WIDE mode needs more buffer so we have to read smaller blocks */
796 move_size = SMALL_BLOCK_SIZE;
797 offset = LARGE_BLOCK_SIZE;
798 fill_buf = BOTTOM_SECTOR;
799 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
800 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
802 else {
803 move_size = LARGE_BLOCK_SIZE;
804 offset = SMALL_BLOCK_SIZE;
805 fill_buf = MID_SECTOR;
806 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
809 move_vector = direction * move_size;
810 screen_top_ptr -= move_vector;
811 file_pos += move_vector;
812 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
813 fill_buffer(file_pos + offset, fill_buf, move_size);
814 return move_vector;
817 static void viewer_scroll_up(void)
819 unsigned char *p;
821 p = find_prev_line(screen_top_ptr);
822 if (p == NULL && !BUFFER_BOF()) {
823 read_and_synch(-1);
824 p = find_prev_line(screen_top_ptr);
826 if (p != NULL)
827 screen_top_ptr = p;
830 static void viewer_scroll_down(void)
832 if (next_screen_ptr != NULL)
833 screen_top_ptr = next_line_ptr;
836 #ifdef HAVE_LCD_BITMAP
837 static void viewer_scrollbar(void) {
838 int items, min_shown, max_shown;
840 items = (int) file_size; /* (SH1 int is same as long) */
841 min_shown = (int) file_pos + (screen_top_ptr - buffer);
843 if (next_screen_ptr == NULL)
844 max_shown = items;
845 else
846 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
848 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
849 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
851 #endif
853 static void viewer_draw(int col)
855 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
856 int width, extra_spaces, indent_spaces, spaces_per_word;
857 bool multiple_spacing, line_is_short;
858 unsigned short ch;
859 unsigned char *str, *oldstr;
860 unsigned char *line_begin;
861 unsigned char *line_end;
862 unsigned char c;
863 unsigned char scratch_buffer[MAX_COLUMNS + 1];
864 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
865 unsigned char *endptr;
867 /* If col==-1 do all calculations but don't display */
868 if (col != -1) {
869 #ifdef HAVE_LCD_BITMAP
870 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
871 #else
872 left_col = 0;
873 #endif
874 rb->lcd_clear_display();
876 max_line_len = 0;
877 line_begin = line_end = screen_top_ptr;
879 for (i = 0; i < display_lines; i++) {
880 if (BUFFER_OOB(line_end))
881 break; /* Happens after display last line at BUFFER_EOF() */
883 line_begin = line_end;
884 line_end = find_next_line(line_begin, &line_is_short);
886 if (line_end == NULL) {
887 if (BUFFER_EOF()) {
888 if (i < display_lines - 1 && !BUFFER_BOF()) {
889 if (col != -1)
890 rb->lcd_clear_display();
892 for (; i < display_lines - 1; i++)
893 viewer_scroll_up();
895 line_begin = line_end = screen_top_ptr;
896 i = -1;
897 continue;
899 else {
900 line_end = buffer_end;
903 else {
904 resynch_move = read_and_synch(1); /* Read block & move ptrs */
905 line_begin -= resynch_move;
906 if (i > 0)
907 next_line_ptr -= resynch_move;
909 line_end = find_next_line(line_begin, NULL);
910 if (line_end == NULL) /* Should not really happen */
911 break;
914 line_len = line_end - line_begin;
916 /* calculate line_len */
917 str = oldstr = line_begin;
918 j = -1;
919 while (str < line_end) {
920 oldstr = str;
921 str = crop_at_width(str);
922 j++;
924 line_width = j*draw_columns;
925 while (oldstr < line_end) {
926 oldstr = get_ucs(oldstr, &ch);
927 line_width += glyph_width(ch);
930 if (prefs.line_mode == JOIN) {
931 if (line_begin[0] == 0) {
932 line_begin++;
933 if (prefs.word_mode == CHOP)
934 line_end++;
935 else
936 line_len--;
938 for (j=k=spaces=0; j < line_len; j++) {
939 if (k == MAX_COLUMNS)
940 break;
942 c = line_begin[j];
943 switch (c) {
944 case ' ':
945 spaces++;
946 break;
947 case 0:
948 spaces = 0;
949 scratch_buffer[k++] = ' ';
950 break;
951 default:
952 while (spaces) {
953 spaces--;
954 scratch_buffer[k++] = ' ';
955 if (k == MAX_COLUMNS - 1)
956 break;
958 scratch_buffer[k++] = c;
959 break;
962 if (col != -1) {
963 scratch_buffer[k] = 0;
964 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
965 prefs.encoding, draw_columns/glyph_width('i'));
966 *endptr = 0;
969 else if (prefs.line_mode == REFLOW) {
970 if (line_begin[0] == 0) {
971 line_begin++;
972 if (prefs.word_mode == CHOP)
973 line_end++;
974 else
975 line_len--;
978 indent_spaces = 0;
979 if (!line_is_short) {
980 multiple_spacing = false;
981 width=spaces=0;
982 for (str = line_begin; str < line_end; ) {
983 str = get_ucs(str, &ch);
984 switch (ch) {
985 case ' ':
986 case 0:
987 if ((str == line_begin) && (prefs.word_mode==WRAP))
988 /* special case: indent the paragraph,
989 * don't count spaces */
990 indent_spaces = par_indent_spaces;
991 else if (!multiple_spacing)
992 spaces++;
993 multiple_spacing = true;
994 break;
995 default:
996 multiple_spacing = false;
997 width += glyph_width(ch);
998 break;
1001 if (multiple_spacing) spaces--;
1003 if (spaces) {
1004 /* total number of spaces to insert between words */
1005 extra_spaces = (draw_columns-width)/glyph_width(' ')
1006 - indent_spaces;
1007 /* number of spaces between each word*/
1008 spaces_per_word = extra_spaces / spaces;
1009 /* number of words with n+1 spaces (to fill up) */
1010 extra_spaces = extra_spaces % spaces;
1011 if (spaces_per_word > 2) { /* too much spacing is awful */
1012 spaces_per_word = 3;
1013 extra_spaces = 0;
1015 } else { /* this doesn't matter much... no spaces anyway */
1016 spaces_per_word = extra_spaces = 0;
1018 } else { /* end of a paragraph: don't fill line */
1019 spaces_per_word = 1;
1020 extra_spaces = 0;
1023 multiple_spacing = false;
1024 for (j=k=spaces=0; j < line_len; j++) {
1025 if (k == MAX_COLUMNS)
1026 break;
1028 c = line_begin[j];
1029 switch (c) {
1030 case ' ':
1031 case 0:
1032 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1033 for (j=0; j<par_indent_spaces; j++)
1034 scratch_buffer[k++] = ' ';
1035 j=0;
1037 else if (!multiple_spacing) {
1038 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1039 scratch_buffer[k++] = ' ';
1040 spaces++;
1042 multiple_spacing = true;
1043 break;
1044 default:
1045 scratch_buffer[k++] = c;
1046 multiple_spacing = false;
1047 break;
1051 if (col != -1) {
1052 scratch_buffer[k] = 0;
1053 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1054 prefs.encoding, k-col);
1055 *endptr = 0;
1058 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1059 if (col != -1)
1060 if (line_width > col) {
1061 str = oldstr = line_begin;
1062 k = col;
1063 width = 0;
1064 while( (width<draw_columns) && (oldstr<line_end) )
1066 oldstr = get_ucs(oldstr, &ch);
1067 if (k > 0) {
1068 k -= glyph_width(ch);
1069 line_begin = oldstr;
1070 } else {
1071 width += glyph_width(ch);
1075 if(prefs.view_mode==WIDE)
1076 endptr = rb->iso_decode(line_begin, utf8_buffer,
1077 prefs.encoding, oldstr-line_begin);
1078 else
1079 endptr = rb->iso_decode(line_begin, utf8_buffer,
1080 prefs.encoding, line_end-line_begin);
1081 *endptr = 0;
1084 if (col != -1 && line_width > col)
1085 #ifdef HAVE_LCD_BITMAP
1086 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1087 #else
1088 rb->lcd_puts(left_col, i, utf8_buffer);
1089 #endif
1090 if (line_width > max_line_len)
1091 max_line_len = line_width;
1093 if (i == 0)
1094 next_line_ptr = line_end;
1096 next_screen_ptr = line_end;
1097 if (BUFFER_OOB(next_screen_ptr))
1098 next_screen_ptr = NULL;
1100 #ifdef HAVE_LCD_BITMAP
1101 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1103 if (prefs.need_scrollbar)
1104 viewer_scrollbar();
1105 #else
1106 next_screen_to_draw_ptr = next_screen_ptr;
1107 #endif
1109 if (col != -1)
1110 rb->lcd_update();
1113 static void viewer_top(void)
1115 /* Read top of file into buffer
1116 and point screen pointer to top */
1117 if (file_pos != 0)
1119 file_pos = 0;
1120 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1121 fill_buffer(0, buffer, buffer_size);
1124 screen_top_ptr = buffer;
1127 static void viewer_bottom(void)
1129 /* Read bottom of file into buffer
1130 and point screen pointer to bottom */
1131 long last_sectors;
1133 if (file_size > buffer_size) {
1134 /* Find last buffer in file, round up to next sector boundary */
1135 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1136 last_sectors /= SMALL_BLOCK_SIZE;
1137 last_sectors *= SMALL_BLOCK_SIZE;
1139 else {
1140 last_sectors = 0;
1143 if (file_pos != last_sectors)
1145 file_pos = last_sectors;
1146 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1147 fill_buffer(last_sectors, buffer, buffer_size);
1150 screen_top_ptr = buffer_end-1;
1153 #ifdef HAVE_LCD_BITMAP
1154 static void init_need_scrollbar(void) {
1155 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1156 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1157 viewer_draw(-1);
1158 prefs.need_scrollbar = NEED_SCROLLBAR();
1159 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1160 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1162 #else
1163 #define init_need_scrollbar()
1164 #endif
1166 static bool viewer_init(void)
1168 #ifdef HAVE_LCD_BITMAP
1170 pf = rb->font_get(FONT_UI);
1172 display_lines = LCD_HEIGHT / pf->height;
1173 draw_columns = display_columns = LCD_WIDTH;
1174 #else
1175 /* REAL fixed pitch :) all chars use up 1 cell */
1176 display_lines = 2;
1177 draw_columns = display_columns = 11;
1178 par_indent_spaces = 2;
1179 #endif
1181 fd = rb->open(file_name, O_RDONLY);
1182 if (fd==-1)
1183 return false;
1185 file_size = rb->filesize(fd);
1186 if (file_size==-1)
1187 return false;
1189 /* Init mac_text value used in processing buffer */
1190 mac_text = false;
1192 return true;
1195 static void viewer_default_settings(void)
1197 prefs.word_mode = WRAP;
1198 prefs.line_mode = NORMAL;
1199 prefs.view_mode = NARROW;
1200 prefs.scroll_mode = PAGE;
1201 #ifdef HAVE_LCD_BITMAP
1202 prefs.page_mode = NO_OVERLAP;
1203 prefs.scrollbar_mode = SB_OFF;
1204 #endif
1205 prefs.autoscroll_speed = 1;
1206 /* Set codepage to system default */
1207 prefs.encoding = rb->global_settings->default_codepage;
1210 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1212 int settings_fd, i;
1213 struct bookmark_file_data *data;
1214 struct bookmarked_file_info this_bookmark;
1216 /* read settings file */
1217 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1218 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1220 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1221 rb->close(settings_fd);
1223 else
1225 /* load default settings if there is no settings file */
1226 viewer_default_settings();
1229 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1231 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1232 data->bookmarked_files_count = 0;
1234 /* read bookmarks if file exists */
1235 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1236 if (settings_fd >= 0)
1238 /* figure out how many items to read */
1239 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1240 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1241 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1242 rb->read(settings_fd, data->bookmarks,
1243 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1244 rb->close(settings_fd);
1247 file_pos = 0;
1248 screen_top_ptr = buffer;
1250 /* check if current file is in list */
1251 for (i=0; i < data->bookmarked_files_count; i++)
1253 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1255 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1256 int screen_top = screen_pos % buffer_size;
1257 file_pos = screen_pos - screen_top;
1258 screen_top_ptr = buffer + screen_top;
1259 break;
1263 this_bookmark.file_position = file_pos;
1264 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1266 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1267 rb->strcpy(this_bookmark.filename,file_name);
1269 /* prevent potential slot overflow */
1270 if (i >= data->bookmarked_files_count)
1272 if (i < MAX_BOOKMARKED_FILES)
1273 data->bookmarked_files_count++;
1274 else
1275 i = MAX_BOOKMARKED_FILES-1;
1278 /* write bookmark file with spare slot in first position
1279 to be filled in by viewer_save_settings */
1280 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1281 if (settings_fd >=0 )
1283 /* write count */
1284 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1286 /* write the current bookmark */
1287 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1289 /* write everything that was before this bookmark */
1290 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1292 rb->close(settings_fd);
1295 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1297 if (BUFFER_OOB(screen_top_ptr))
1299 screen_top_ptr = buffer;
1302 fill_buffer(file_pos, buffer, buffer_size);
1304 /* remember the current position */
1305 start_position = file_pos + screen_top_ptr - buffer;
1307 init_need_scrollbar();
1310 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1312 int settings_fd;
1314 /* save the viewer settings if they have been changed */
1315 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1317 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1319 if (settings_fd >= 0 )
1321 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1322 rb->close(settings_fd);
1326 /* save the bookmark if the position has changed */
1327 if (file_pos + screen_top_ptr - buffer != start_position)
1329 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1331 if (settings_fd >= 0 )
1333 struct bookmarked_file_info b;
1334 b.file_position = file_pos + screen_top_ptr - buffer;
1335 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1336 rb->memset(&b.filename[0],0,MAX_PATH);
1337 rb->strcpy(b.filename,file_name);
1338 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1339 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1340 rb->close(settings_fd);
1345 static void viewer_exit(void *parameter)
1347 (void)parameter;
1349 viewer_save_settings();
1350 rb->close(fd);
1353 static int col_limit(int col)
1355 if (col < 0)
1356 col = 0;
1357 else
1358 if (col > max_line_len - 2*glyph_width('o'))
1359 col = max_line_len - 2*glyph_width('o');
1361 return col;
1364 /* settings helper functions */
1366 static bool encoding_setting(void)
1368 static struct opt_items names[NUM_CODEPAGES];
1369 int idx;
1371 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1373 names[idx].string = rb->get_codepage_name(idx);
1374 names[idx].voice_id = -1;
1377 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1378 sizeof(names) / sizeof(names[0]), NULL);
1381 static bool word_wrap_setting(void)
1383 static const struct opt_items names[] = {
1384 {"On", -1},
1385 {"Off (Chop Words)", -1},
1388 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1389 names, 2, NULL);
1392 static bool line_mode_setting(void)
1394 static const struct opt_items names[] = {
1395 {"Normal", -1},
1396 {"Join Lines", -1},
1397 {"Expand Lines", -1},
1398 #ifdef HAVE_LCD_BITMAP
1399 {"Reflow Lines", -1},
1400 #endif
1403 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1404 sizeof(names) / sizeof(names[0]), NULL);
1407 static bool view_mode_setting(void)
1409 static const struct opt_items names[] = {
1410 {"No (Narrow)", -1},
1411 {"Yes", -1},
1413 bool ret;
1414 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1415 names , 2, NULL);
1416 if (prefs.view_mode == NARROW)
1417 col = 0;
1418 return ret;
1421 static bool scroll_mode_setting(void)
1423 static const struct opt_items names[] = {
1424 {"Scroll by Page", -1},
1425 {"Scroll by Line", -1},
1428 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1429 names, 2, NULL);
1432 #ifdef HAVE_LCD_BITMAP
1433 static bool page_mode_setting(void)
1435 static const struct opt_items names[] = {
1436 {"No", -1},
1437 {"Yes", -1},
1440 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1441 names, 2, NULL);
1444 static bool scrollbar_setting(void)
1446 static const struct opt_items names[] = {
1447 {"Off", -1},
1448 {"On", -1}
1451 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1452 names, 2, NULL);
1454 #endif
1456 static bool autoscroll_speed_setting(void)
1458 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1459 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1462 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
1463 NULL, NULL, Icon_NOICON);
1464 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
1465 NULL, NULL, Icon_NOICON);
1466 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
1467 NULL, NULL, Icon_NOICON);
1468 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
1469 NULL, NULL, Icon_NOICON);
1470 #ifdef HAVE_LCD_BITMAP
1471 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
1472 NULL, NULL, Icon_NOICON);
1473 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
1474 NULL, NULL, Icon_NOICON);
1475 #endif
1476 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
1477 NULL, NULL, Icon_NOICON);
1478 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
1479 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
1480 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
1481 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
1482 #ifdef HAVE_LCD_BITMAP
1483 &scrollbar_item, &page_mode_item,
1484 #endif
1485 &scroll_mode_item, &autoscroll_speed_item);
1487 static bool viewer_options_menu(void)
1489 bool result;
1490 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
1492 #ifdef HAVE_LCD_BITMAP
1493 /* Show-scrollbar mode for current view-width mode */
1494 init_need_scrollbar();
1495 #endif
1496 return result;
1499 static void viewer_menu(void)
1501 int result;
1502 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
1503 "Return", "Viewer Options",
1504 "Show Playback Menu", "Quit");
1506 result = rb->do_menu(&menu, NULL, NULL, false);
1507 switch (result)
1509 case 0: /* return */
1510 break;
1511 case 1: /* change settings */
1512 done = viewer_options_menu();
1513 break;
1514 case 2: /* playback control */
1515 playback_control(NULL);
1516 break;
1517 case 3: /* quit */
1518 viewer_exit(NULL);
1519 done = true;
1520 break;
1522 viewer_draw(col);
1525 enum plugin_status plugin_start(const void* file)
1527 int button, i, ok;
1528 int lastbutton = BUTTON_NONE;
1529 bool autoscroll = false;
1530 long old_tick;
1532 old_tick = *rb->current_tick;
1534 /* get the plugin buffer */
1535 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1537 if (!file)
1538 return PLUGIN_ERROR;
1540 file_name = file;
1541 ok = viewer_init();
1542 if (!ok) {
1543 rb->splash(HZ, "Error opening file.");
1544 return PLUGIN_ERROR;
1547 viewer_load_settings(); /* load the preferences and bookmark */
1549 #if LCD_DEPTH > 1
1550 rb->lcd_set_backdrop(NULL);
1551 #endif
1553 viewer_draw(col);
1555 while (!done) {
1557 if(autoscroll)
1559 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1561 viewer_scroll_down();
1562 viewer_draw(col);
1563 old_tick = *rb->current_tick;
1567 button = rb->button_get_w_tmo(HZ/10);
1568 switch (button) {
1569 case VIEWER_MENU:
1570 viewer_menu();
1571 break;
1573 case VIEWER_AUTOSCROLL:
1574 #ifdef VIEWER_AUTOSCROLL_PRE
1575 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1576 break;
1577 #endif
1578 autoscroll = !autoscroll;
1579 break;
1581 case VIEWER_PAGE_UP:
1582 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1583 if (prefs.scroll_mode == PAGE)
1585 /* Page up */
1586 #ifdef HAVE_LCD_BITMAP
1587 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1588 #else
1589 for (i = 0; i < display_lines; i++)
1590 #endif
1591 viewer_scroll_up();
1593 else
1594 viewer_scroll_up();
1595 old_tick = *rb->current_tick;
1596 viewer_draw(col);
1597 break;
1599 case VIEWER_PAGE_DOWN:
1600 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1601 if (prefs.scroll_mode == PAGE)
1603 /* Page down */
1604 if (next_screen_ptr != NULL)
1605 screen_top_ptr = next_screen_to_draw_ptr;
1607 else
1608 viewer_scroll_down();
1609 old_tick = *rb->current_tick;
1610 viewer_draw(col);
1611 break;
1613 case VIEWER_SCREEN_LEFT:
1614 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1615 if (prefs.view_mode == WIDE) {
1616 /* Screen left */
1617 col -= draw_columns;
1618 col = col_limit(col);
1620 else { /* prefs.view_mode == NARROW */
1621 /* Top of file */
1622 viewer_top();
1625 viewer_draw(col);
1626 break;
1628 case VIEWER_SCREEN_RIGHT:
1629 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1630 if (prefs.view_mode == WIDE) {
1631 /* Screen right */
1632 col += draw_columns;
1633 col = col_limit(col);
1635 else { /* prefs.view_mode == NARROW */
1636 /* Bottom of file */
1637 viewer_bottom();
1640 viewer_draw(col);
1641 break;
1643 #ifdef VIEWER_LINE_UP
1644 case VIEWER_LINE_UP:
1645 case VIEWER_LINE_UP | BUTTON_REPEAT:
1646 /* Scroll up one line */
1647 viewer_scroll_up();
1648 old_tick = *rb->current_tick;
1649 viewer_draw(col);
1650 break;
1652 case VIEWER_LINE_DOWN:
1653 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1654 /* Scroll down one line */
1655 if (next_screen_ptr != NULL)
1656 screen_top_ptr = next_line_ptr;
1657 old_tick = *rb->current_tick;
1658 viewer_draw(col);
1659 break;
1660 #endif
1661 #ifdef VIEWER_COLUMN_LEFT
1662 case VIEWER_COLUMN_LEFT:
1663 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1664 if (prefs.view_mode == WIDE) {
1665 /* Scroll left one column */
1666 col -= glyph_width('o');
1667 col = col_limit(col);
1668 viewer_draw(col);
1670 break;
1672 case VIEWER_COLUMN_RIGHT:
1673 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1674 if (prefs.view_mode == WIDE) {
1675 /* Scroll right one column */
1676 col += glyph_width('o');
1677 col = col_limit(col);
1678 viewer_draw(col);
1680 break;
1681 #endif
1683 #ifdef VIEWER_RC_QUIT
1684 case VIEWER_RC_QUIT:
1685 #endif
1686 case VIEWER_QUIT:
1687 viewer_exit(NULL);
1688 done = true;
1689 break;
1691 default:
1692 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1693 == SYS_USB_CONNECTED)
1694 return PLUGIN_USB_CONNECTED;
1695 break;
1697 if (button != BUTTON_NONE)
1699 lastbutton = button;
1700 rb->yield();
1703 return PLUGIN_OK;