Add a browse (remote) custom statusbar item in the theme settings.
[kugel-rb.git] / apps / plugins / viewer.c
blob4d83dd0cc041538db9342ec489e58510f956afbf
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 /* Onda VX777 keys */
333 #elif CONFIG_KEYPAD == ONDAVX777_PAD
334 #define VIEWER_QUIT BUTTON_POWER
336 /* SAMSUNG YH-820 / YH-920 / YH-925 keys */
337 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
338 #define VIEWER_QUIT BUTTON_REC
339 #define VIEWER_PAGE_UP BUTTON_UP
340 #define VIEWER_PAGE_DOWN BUTTON_DOWN
341 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
342 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
343 #define VIEWER_MENU BUTTON_PLAY
344 #define VIEWER_AUTOSCROLL BUTTON_REW
346 #else
347 #error No keymap defined!
348 #endif
350 #ifdef HAVE_TOUCHSCREEN
351 #ifndef VIEWER_QUIT
352 #define VIEWER_QUIT BUTTON_TOPLEFT
353 #endif
354 #ifndef VIEWER_PAGE_UP
355 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
356 #endif
357 #ifndef VIEWER_PAGE_DOWN
358 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
359 #endif
360 #ifndef VIEWER_SCREEN_LEFT
361 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
362 #endif
363 #ifndef VIEWER_SCREEN_RIGHT
364 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
365 #endif
366 #ifndef VIEWER_MENU
367 #define VIEWER_MENU BUTTON_TOPRIGHT
368 #endif
369 #ifndef VIEWER_AUTOSCROLL
370 #define VIEWER_AUTOSCROLL BUTTON_CENTER
371 #endif
372 #endif
374 /* stuff for the bookmarking */
375 struct bookmarked_file_info {
376 long file_position;
377 int top_ptr_pos;
378 char filename[MAX_PATH];
381 struct bookmark_file_data {
382 signed int bookmarked_files_count;
383 struct bookmarked_file_info bookmarks[];
386 struct preferences {
387 enum {
388 WRAP=0,
389 CHOP,
390 } word_mode;
392 enum {
393 NORMAL=0,
394 JOIN,
395 EXPAND,
396 REFLOW, /* won't be set on charcell LCD, must be last */
397 } line_mode;
399 enum {
400 NARROW=0,
401 WIDE,
402 } view_mode;
404 enum codepages encoding;
406 #ifdef HAVE_LCD_BITMAP
407 enum {
408 SB_OFF=0,
409 SB_ON,
410 } scrollbar_mode;
411 bool need_scrollbar;
413 enum {
414 NO_OVERLAP=0,
415 OVERLAP,
416 } page_mode;
417 #endif /* HAVE_LCD_BITMAP */
419 enum {
420 PAGE=0,
421 LINE,
422 } scroll_mode;
424 int autoscroll_speed;
428 struct preferences prefs;
429 struct preferences old_prefs;
431 static unsigned char *buffer;
432 static long buffer_size;
433 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
434 static int display_columns; /* number of (pixel) columns on the display */
435 static int display_lines; /* number of lines on the display */
436 static int draw_columns; /* number of (pixel) columns available for text */
437 static int par_indent_spaces; /* number of spaces to indent first paragraph */
438 static int fd;
439 static const char *file_name;
440 static long file_size;
441 static long start_position; /* position in the file after the viewer is started */
442 static bool mac_text;
443 static long file_pos; /* Position of the top of the buffer in the file */
444 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
445 static int max_line_len;
446 static unsigned char *screen_top_ptr;
447 static unsigned char *next_screen_ptr;
448 static unsigned char *next_screen_to_draw_ptr;
449 static unsigned char *next_line_ptr;
450 #ifdef HAVE_LCD_BITMAP
451 static struct font *pf;
452 #endif
455 int glyph_width(int ch)
457 if (ch == 0)
458 ch = ' ';
460 #ifdef HAVE_LCD_BITMAP
461 return rb->font_get_width(pf, ch);
462 #else
463 return 1;
464 #endif
467 unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
469 unsigned char utf8_tmp[6];
470 int count;
472 if (prefs.encoding == UTF_8)
473 return (unsigned char*)rb->utf8decode(str, ch);
475 count = BUFFER_OOB(str+2)? 1:2;
476 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
477 rb->utf8decode(utf8_tmp, ch);
479 #ifdef HAVE_LCD_BITMAP
480 if (prefs.encoding >= SJIS && *str >= 0x80
481 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
482 return (unsigned char*)str+2;
483 else
484 #endif
485 return (unsigned char*)str+1;
488 bool done = false;
489 int col = 0;
491 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
492 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
493 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
494 static unsigned char* crop_at_width(const unsigned char* p)
496 int k,width;
497 unsigned short ch;
498 const unsigned char *oldp = p;
500 k=width=0;
502 while (LINE_IS_NOT_FULL) {
503 oldp = p;
504 p = get_ucs(p, &ch);
505 ADVANCE_COUNTERS(ch);
508 return (unsigned char*)oldp;
511 static unsigned char* find_first_feed(const unsigned char* p, int size)
513 int i;
515 for (i=0; i < size; i++)
516 if (p[i] == 0)
517 return (unsigned char*) p+i;
519 return NULL;
522 static unsigned char* find_last_feed(const unsigned char* p, int size)
524 int i;
526 for (i=size-1; i>=0; i--)
527 if (p[i] == 0)
528 return (unsigned char*) p+i;
530 return NULL;
533 static unsigned char* find_last_space(const unsigned char* p, int size)
535 int i, j, k;
537 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
539 if (!BUFFER_OOB(&p[size]))
540 for (j=k; j < ((int) sizeof(line_break)) - 1; j++)
541 if (p[size] == line_break[j])
542 return (unsigned char*) p+size;
544 for (i=size-1; i>=0; i--)
545 for (j=k; j < (int) sizeof(line_break); j++)
547 if (!((p[i] == '-') && (prefs.word_mode == WRAP)))
548 if (p[i] == line_break[j])
549 return (unsigned char*) p+i;
552 return NULL;
555 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
557 const unsigned char *next_line = NULL;
558 int size, i, j, k, width, search_len, spaces, newlines;
559 bool first_chars;
560 unsigned char c;
562 if (is_short != NULL)
563 *is_short = true;
565 if BUFFER_OOB(cur_line)
566 return NULL;
568 if (prefs.view_mode == WIDE) {
569 search_len = MAX_WIDTH;
571 else { /* prefs.view_mode == NARROW */
572 search_len = crop_at_width(cur_line) - cur_line;
575 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
577 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
578 /* Need to scan ahead and possibly increase search_len and size,
579 or possibly set next_line at second hard return in a row. */
580 next_line = NULL;
581 first_chars=true;
582 for (j=k=width=spaces=newlines=0; ; j++) {
583 if (BUFFER_OOB(cur_line+j))
584 return NULL;
585 if (LINE_IS_FULL) {
586 size = search_len = j;
587 break;
590 c = cur_line[j];
591 switch (c) {
592 case ' ':
593 if (prefs.line_mode == REFLOW) {
594 if (newlines > 0) {
595 size = j;
596 next_line = cur_line + size;
597 return (unsigned char*) next_line;
599 if (j==0) /* i=1 is intentional */
600 for (i=0; i<par_indent_spaces; i++)
601 ADVANCE_COUNTERS(' ');
603 if (!first_chars) spaces++;
604 break;
606 case 0:
607 if (newlines > 0) {
608 size = j;
609 next_line = cur_line + size - spaces;
610 if (next_line != cur_line)
611 return (unsigned char*) next_line;
612 break;
615 newlines++;
616 size += spaces -1;
617 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
618 return NULL;
619 search_len = size;
620 spaces = first_chars? 0:1;
621 break;
623 default:
624 if (prefs.line_mode==JOIN || newlines>0) {
625 while (spaces) {
626 spaces--;
627 ADVANCE_COUNTERS(' ');
628 if (LINE_IS_FULL) {
629 size = search_len = j;
630 break;
633 newlines=0;
634 } else if (spaces) {
635 /* REFLOW, multiple spaces between words: count only
636 * one. If more are needed, they will be added
637 * while drawing. */
638 search_len = size;
639 spaces=0;
640 ADVANCE_COUNTERS(' ');
641 if (LINE_IS_FULL) {
642 size = search_len = j;
643 break;
646 first_chars = false;
647 ADVANCE_COUNTERS(c);
648 break;
652 else {
653 /* find first hard return */
654 next_line = find_first_feed(cur_line, size);
657 if (next_line == NULL)
658 if (size == search_len) {
659 if (prefs.word_mode == WRAP) /* Find last space */
660 next_line = find_last_space(cur_line, size);
662 if (next_line == NULL)
663 next_line = crop_at_width(cur_line);
664 else
665 if (prefs.word_mode == WRAP)
666 for (i=0;
667 i<WRAP_TRIM && isspace(next_line[0]) && !BUFFER_OOB(next_line);
668 i++)
669 next_line++;
672 if (prefs.line_mode == EXPAND)
673 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
674 if (next_line[0] == 0)
675 if (next_line != cur_line)
676 return (unsigned char*) next_line;
678 /* If next_line is pointing to a zero, increment it; i.e.,
679 leave the terminator at the end of cur_line. If pointing
680 to a hyphen, increment only if there is room to display
681 the hyphen on current line (won't apply in WIDE mode,
682 since it's guarenteed there won't be room). */
683 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
684 if (next_line[0] == 0)/* ||
685 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
686 next_line++;
688 if (BUFFER_OOB(next_line))
689 return NULL;
691 if (is_short)
692 *is_short = false;
694 return (unsigned char*) next_line;
697 static unsigned char* find_prev_line(const unsigned char* cur_line)
699 const unsigned char *prev_line = NULL;
700 const unsigned char *p;
702 if BUFFER_OOB(cur_line)
703 return NULL;
705 /* To wrap consistently at the same places, we must
706 start with a known hard return, then work downwards.
707 We can either search backwards for a hard return,
708 or simply start wrapping downwards from top of buffer.
709 If current line is not near top of buffer, this is
710 a file with long lines (paragraphs). We would need to
711 read earlier sectors before we could decide how to
712 properly wrap the lines above the current line, but
713 it probably is not worth the disk access. Instead,
714 start with top of buffer and wrap down from there.
715 This may result in some lines wrapping at different
716 points from where they wrap when scrolling down.
717 If buffer is at top of file, start at top of buffer. */
719 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
720 prev_line = p = NULL;
721 else
722 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
723 /* Null means no line feeds in buffer above current line. */
725 if (prev_line == NULL)
726 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
727 prev_line = p = buffer;
728 /* (else return NULL and read previous block) */
730 /* Wrap downwards until too far, then use the one before. */
731 while (p < cur_line && p != NULL) {
732 prev_line = p;
733 p = find_next_line(prev_line, NULL);
736 if (BUFFER_OOB(prev_line))
737 return NULL;
739 return (unsigned char*) prev_line;
742 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
744 /* Read from file and preprocess the data */
745 /* To minimize disk access, always read on sector boundaries */
746 unsigned numread, i;
747 bool found_CR = false;
749 rb->lseek(fd, pos, SEEK_SET);
750 numread = rb->read(fd, buf, size);
751 rb->button_clear_queue(); /* clear button queue */
753 for(i = 0; i < numread; i++) {
754 switch(buf[i]) {
755 case '\r':
756 if (mac_text) {
757 buf[i] = 0;
759 else {
760 buf[i] = ' ';
761 found_CR = true;
763 break;
765 case '\n':
766 buf[i] = 0;
767 found_CR = false;
768 break;
770 case 0: /* No break between case 0 and default, intentionally */
771 buf[i] = ' ';
772 default:
773 if (found_CR) {
774 buf[i - 1] = 0;
775 found_CR = false;
776 mac_text = true;
778 break;
783 static int read_and_synch(int direction)
785 /* Read next (or prev) block, and reposition global pointers. */
786 /* direction: 1 for down (i.e., further into file), -1 for up */
787 int move_size, move_vector, offset;
788 unsigned char *fill_buf;
790 if (direction == -1) /* up */ {
791 move_size = SMALL_BLOCK_SIZE;
792 offset = 0;
793 fill_buf = TOP_SECTOR;
794 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
795 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
797 else /* down */ {
798 if (prefs.view_mode == WIDE) {
799 /* WIDE mode needs more buffer so we have to read smaller blocks */
800 move_size = SMALL_BLOCK_SIZE;
801 offset = LARGE_BLOCK_SIZE;
802 fill_buf = BOTTOM_SECTOR;
803 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
804 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
806 else {
807 move_size = LARGE_BLOCK_SIZE;
808 offset = SMALL_BLOCK_SIZE;
809 fill_buf = MID_SECTOR;
810 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
813 move_vector = direction * move_size;
814 screen_top_ptr -= move_vector;
815 file_pos += move_vector;
816 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
817 fill_buffer(file_pos + offset, fill_buf, move_size);
818 return move_vector;
821 static void viewer_scroll_up(void)
823 unsigned char *p;
825 p = find_prev_line(screen_top_ptr);
826 if (p == NULL && !BUFFER_BOF()) {
827 read_and_synch(-1);
828 p = find_prev_line(screen_top_ptr);
830 if (p != NULL)
831 screen_top_ptr = p;
834 static void viewer_scroll_down(void)
836 if (next_screen_ptr != NULL)
837 screen_top_ptr = next_line_ptr;
840 #ifdef HAVE_LCD_BITMAP
841 static void viewer_scrollbar(void) {
842 int items, min_shown, max_shown;
844 items = (int) file_size; /* (SH1 int is same as long) */
845 min_shown = (int) file_pos + (screen_top_ptr - buffer);
847 if (next_screen_ptr == NULL)
848 max_shown = items;
849 else
850 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
852 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, 0, SCROLLBAR_WIDTH-1,
853 LCD_HEIGHT, items, min_shown, max_shown, VERTICAL);
855 #endif
857 static void viewer_draw(int col)
859 int i, j, k, line_len, line_width, resynch_move, spaces, left_col=0;
860 int width, extra_spaces, indent_spaces, spaces_per_word;
861 bool multiple_spacing, line_is_short;
862 unsigned short ch;
863 unsigned char *str, *oldstr;
864 unsigned char *line_begin;
865 unsigned char *line_end;
866 unsigned char c;
867 unsigned char scratch_buffer[MAX_COLUMNS + 1];
868 unsigned char utf8_buffer[MAX_COLUMNS*4 + 1];
869 unsigned char *endptr;
871 /* If col==-1 do all calculations but don't display */
872 if (col != -1) {
873 #ifdef HAVE_LCD_BITMAP
874 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
875 #else
876 left_col = 0;
877 #endif
878 rb->lcd_clear_display();
880 max_line_len = 0;
881 line_begin = line_end = screen_top_ptr;
883 for (i = 0; i < display_lines; i++) {
884 if (BUFFER_OOB(line_end))
885 break; /* Happens after display last line at BUFFER_EOF() */
887 line_begin = line_end;
888 line_end = find_next_line(line_begin, &line_is_short);
890 if (line_end == NULL) {
891 if (BUFFER_EOF()) {
892 if (i < display_lines - 1 && !BUFFER_BOF()) {
893 if (col != -1)
894 rb->lcd_clear_display();
896 for (; i < display_lines - 1; i++)
897 viewer_scroll_up();
899 line_begin = line_end = screen_top_ptr;
900 i = -1;
901 continue;
903 else {
904 line_end = buffer_end;
907 else {
908 resynch_move = read_and_synch(1); /* Read block & move ptrs */
909 line_begin -= resynch_move;
910 if (i > 0)
911 next_line_ptr -= resynch_move;
913 line_end = find_next_line(line_begin, NULL);
914 if (line_end == NULL) /* Should not really happen */
915 break;
918 line_len = line_end - line_begin;
920 /* calculate line_len */
921 str = oldstr = line_begin;
922 j = -1;
923 while (str < line_end) {
924 oldstr = str;
925 str = crop_at_width(str);
926 j++;
928 line_width = j*draw_columns;
929 while (oldstr < line_end) {
930 oldstr = get_ucs(oldstr, &ch);
931 line_width += glyph_width(ch);
934 if (prefs.line_mode == JOIN) {
935 if (line_begin[0] == 0) {
936 line_begin++;
937 if (prefs.word_mode == CHOP)
938 line_end++;
939 else
940 line_len--;
942 for (j=k=spaces=0; j < line_len; j++) {
943 if (k == MAX_COLUMNS)
944 break;
946 c = line_begin[j];
947 switch (c) {
948 case ' ':
949 spaces++;
950 break;
951 case 0:
952 spaces = 0;
953 scratch_buffer[k++] = ' ';
954 break;
955 default:
956 while (spaces) {
957 spaces--;
958 scratch_buffer[k++] = ' ';
959 if (k == MAX_COLUMNS - 1)
960 break;
962 scratch_buffer[k++] = c;
963 break;
966 if (col != -1) {
967 scratch_buffer[k] = 0;
968 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
969 prefs.encoding, draw_columns/glyph_width('i'));
970 *endptr = 0;
973 else if (prefs.line_mode == REFLOW) {
974 if (line_begin[0] == 0) {
975 line_begin++;
976 if (prefs.word_mode == CHOP)
977 line_end++;
978 else
979 line_len--;
982 indent_spaces = 0;
983 if (!line_is_short) {
984 multiple_spacing = false;
985 width=spaces=0;
986 for (str = line_begin; str < line_end; ) {
987 str = get_ucs(str, &ch);
988 switch (ch) {
989 case ' ':
990 case 0:
991 if ((str == line_begin) && (prefs.word_mode==WRAP))
992 /* special case: indent the paragraph,
993 * don't count spaces */
994 indent_spaces = par_indent_spaces;
995 else if (!multiple_spacing)
996 spaces++;
997 multiple_spacing = true;
998 break;
999 default:
1000 multiple_spacing = false;
1001 width += glyph_width(ch);
1002 break;
1005 if (multiple_spacing) spaces--;
1007 if (spaces) {
1008 /* total number of spaces to insert between words */
1009 extra_spaces = (draw_columns-width)/glyph_width(' ')
1010 - indent_spaces;
1011 /* number of spaces between each word*/
1012 spaces_per_word = extra_spaces / spaces;
1013 /* number of words with n+1 spaces (to fill up) */
1014 extra_spaces = extra_spaces % spaces;
1015 if (spaces_per_word > 2) { /* too much spacing is awful */
1016 spaces_per_word = 3;
1017 extra_spaces = 0;
1019 } else { /* this doesn't matter much... no spaces anyway */
1020 spaces_per_word = extra_spaces = 0;
1022 } else { /* end of a paragraph: don't fill line */
1023 spaces_per_word = 1;
1024 extra_spaces = 0;
1027 multiple_spacing = false;
1028 for (j=k=spaces=0; j < line_len; j++) {
1029 if (k == MAX_COLUMNS)
1030 break;
1032 c = line_begin[j];
1033 switch (c) {
1034 case ' ':
1035 case 0:
1036 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1037 for (j=0; j<par_indent_spaces; j++)
1038 scratch_buffer[k++] = ' ';
1039 j=0;
1041 else if (!multiple_spacing) {
1042 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1043 scratch_buffer[k++] = ' ';
1044 spaces++;
1046 multiple_spacing = true;
1047 break;
1048 default:
1049 scratch_buffer[k++] = c;
1050 multiple_spacing = false;
1051 break;
1055 if (col != -1) {
1056 scratch_buffer[k] = 0;
1057 endptr = rb->iso_decode(scratch_buffer + col, utf8_buffer,
1058 prefs.encoding, k-col);
1059 *endptr = 0;
1062 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1063 if (col != -1)
1064 if (line_width > col) {
1065 str = oldstr = line_begin;
1066 k = col;
1067 width = 0;
1068 while( (width<draw_columns) && (oldstr<line_end) )
1070 oldstr = get_ucs(oldstr, &ch);
1071 if (k > 0) {
1072 k -= glyph_width(ch);
1073 line_begin = oldstr;
1074 } else {
1075 width += glyph_width(ch);
1079 if(prefs.view_mode==WIDE)
1080 endptr = rb->iso_decode(line_begin, utf8_buffer,
1081 prefs.encoding, oldstr-line_begin);
1082 else
1083 endptr = rb->iso_decode(line_begin, utf8_buffer,
1084 prefs.encoding, line_end-line_begin);
1085 *endptr = 0;
1088 if (col != -1 && line_width > col)
1089 #ifdef HAVE_LCD_BITMAP
1090 rb->lcd_putsxy(left_col, i*pf->height, utf8_buffer);
1091 #else
1092 rb->lcd_puts(left_col, i, utf8_buffer);
1093 #endif
1094 if (line_width > max_line_len)
1095 max_line_len = line_width;
1097 if (i == 0)
1098 next_line_ptr = line_end;
1100 next_screen_ptr = line_end;
1101 if (BUFFER_OOB(next_screen_ptr))
1102 next_screen_ptr = NULL;
1104 #ifdef HAVE_LCD_BITMAP
1105 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1107 if (prefs.need_scrollbar)
1108 viewer_scrollbar();
1109 #else
1110 next_screen_to_draw_ptr = next_screen_ptr;
1111 #endif
1113 if (col != -1)
1114 rb->lcd_update();
1117 static void viewer_top(void)
1119 /* Read top of file into buffer
1120 and point screen pointer to top */
1121 if (file_pos != 0)
1123 file_pos = 0;
1124 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1125 fill_buffer(0, buffer, buffer_size);
1128 screen_top_ptr = buffer;
1131 static void viewer_bottom(void)
1133 /* Read bottom of file into buffer
1134 and point screen pointer to bottom */
1135 long last_sectors;
1137 if (file_size > buffer_size) {
1138 /* Find last buffer in file, round up to next sector boundary */
1139 last_sectors = file_size - buffer_size + SMALL_BLOCK_SIZE;
1140 last_sectors /= SMALL_BLOCK_SIZE;
1141 last_sectors *= SMALL_BLOCK_SIZE;
1143 else {
1144 last_sectors = 0;
1147 if (file_pos != last_sectors)
1149 file_pos = last_sectors;
1150 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1151 fill_buffer(last_sectors, buffer, buffer_size);
1154 screen_top_ptr = buffer_end-1;
1157 #ifdef HAVE_LCD_BITMAP
1158 static void init_need_scrollbar(void) {
1159 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1160 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1161 viewer_draw(-1);
1162 prefs.need_scrollbar = NEED_SCROLLBAR();
1163 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1164 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1166 #else
1167 #define init_need_scrollbar()
1168 #endif
1170 static bool viewer_init(void)
1172 #ifdef HAVE_LCD_BITMAP
1174 pf = rb->font_get(FONT_UI);
1176 display_lines = LCD_HEIGHT / pf->height;
1177 draw_columns = display_columns = LCD_WIDTH;
1178 #else
1179 /* REAL fixed pitch :) all chars use up 1 cell */
1180 display_lines = 2;
1181 draw_columns = display_columns = 11;
1182 par_indent_spaces = 2;
1183 #endif
1185 fd = rb->open(file_name, O_RDONLY);
1186 if (fd==-1)
1187 return false;
1189 file_size = rb->filesize(fd);
1190 if (file_size==-1)
1191 return false;
1193 /* Init mac_text value used in processing buffer */
1194 mac_text = false;
1196 return true;
1199 static void viewer_default_settings(void)
1201 prefs.word_mode = WRAP;
1202 prefs.line_mode = NORMAL;
1203 prefs.view_mode = NARROW;
1204 prefs.scroll_mode = PAGE;
1205 #ifdef HAVE_LCD_BITMAP
1206 prefs.page_mode = NO_OVERLAP;
1207 prefs.scrollbar_mode = SB_OFF;
1208 #endif
1209 prefs.autoscroll_speed = 1;
1210 /* Set codepage to system default */
1211 prefs.encoding = rb->global_settings->default_codepage;
1214 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1216 int settings_fd, i;
1217 struct bookmark_file_data *data;
1218 struct bookmarked_file_info this_bookmark;
1220 /* read settings file */
1221 settings_fd=rb->open(SETTINGS_FILE, O_RDONLY);
1222 if ((settings_fd >= 0) && (rb->filesize(settings_fd) == sizeof(struct preferences)))
1224 rb->read(settings_fd, &prefs, sizeof(struct preferences));
1225 rb->close(settings_fd);
1227 else
1229 /* load default settings if there is no settings file */
1230 viewer_default_settings();
1233 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
1235 data = (struct bookmark_file_data*)buffer; /* grab the text buffer */
1236 data->bookmarked_files_count = 0;
1238 /* read bookmarks if file exists */
1239 settings_fd = rb->open(BOOKMARKS_FILE, O_RDONLY);
1240 if (settings_fd >= 0)
1242 /* figure out how many items to read */
1243 rb->read(settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1244 if (data->bookmarked_files_count > MAX_BOOKMARKED_FILES)
1245 data->bookmarked_files_count = MAX_BOOKMARKED_FILES;
1246 rb->read(settings_fd, data->bookmarks,
1247 sizeof(struct bookmarked_file_info) * data->bookmarked_files_count);
1248 rb->close(settings_fd);
1251 file_pos = 0;
1252 screen_top_ptr = buffer;
1254 /* check if current file is in list */
1255 for (i=0; i < data->bookmarked_files_count; i++)
1257 if (!rb->strcmp(file_name, data->bookmarks[i].filename))
1259 int screen_pos = data->bookmarks[i].file_position + data->bookmarks[i].top_ptr_pos;
1260 int screen_top = screen_pos % buffer_size;
1261 file_pos = screen_pos - screen_top;
1262 screen_top_ptr = buffer + screen_top;
1263 break;
1267 this_bookmark.file_position = file_pos;
1268 this_bookmark.top_ptr_pos = screen_top_ptr - buffer;
1270 rb->memset(&this_bookmark.filename[0],0,MAX_PATH);
1271 rb->strcpy(this_bookmark.filename,file_name);
1273 /* prevent potential slot overflow */
1274 if (i >= data->bookmarked_files_count)
1276 if (i < MAX_BOOKMARKED_FILES)
1277 data->bookmarked_files_count++;
1278 else
1279 i = MAX_BOOKMARKED_FILES-1;
1282 /* write bookmark file with spare slot in first position
1283 to be filled in by viewer_save_settings */
1284 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1285 if (settings_fd >=0 )
1287 /* write count */
1288 rb->write (settings_fd, &data->bookmarked_files_count, sizeof(signed int));
1290 /* write the current bookmark */
1291 rb->write (settings_fd, &this_bookmark, sizeof(struct bookmarked_file_info));
1293 /* write everything that was before this bookmark */
1294 rb->write (settings_fd, data->bookmarks, sizeof(struct bookmarked_file_info)*i);
1296 rb->close(settings_fd);
1299 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1301 if (BUFFER_OOB(screen_top_ptr))
1303 screen_top_ptr = buffer;
1306 fill_buffer(file_pos, buffer, buffer_size);
1308 /* remember the current position */
1309 start_position = file_pos + screen_top_ptr - buffer;
1311 init_need_scrollbar();
1314 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1316 int settings_fd;
1318 /* save the viewer settings if they have been changed */
1319 if (rb->memcmp(&prefs, &old_prefs, sizeof(struct preferences)))
1321 settings_fd = rb->creat(SETTINGS_FILE); /* create the settings file */
1323 if (settings_fd >= 0 )
1325 rb->write (settings_fd, &prefs, sizeof(struct preferences));
1326 rb->close(settings_fd);
1330 /* save the bookmark if the position has changed */
1331 if (file_pos + screen_top_ptr - buffer != start_position)
1333 settings_fd = rb->open(BOOKMARKS_FILE, O_WRONLY|O_CREAT);
1335 if (settings_fd >= 0 )
1337 struct bookmarked_file_info b;
1338 b.file_position = file_pos + screen_top_ptr - buffer;
1339 b.top_ptr_pos = 0; /* this is only kept for legassy reasons */
1340 rb->memset(&b.filename[0],0,MAX_PATH);
1341 rb->strcpy(b.filename,file_name);
1342 rb->lseek(settings_fd,sizeof(signed int),SEEK_SET);
1343 rb->write (settings_fd, &b, sizeof(struct bookmarked_file_info));
1344 rb->close(settings_fd);
1349 static void viewer_exit(void *parameter)
1351 (void)parameter;
1353 viewer_save_settings();
1354 rb->close(fd);
1357 static int col_limit(int col)
1359 if (col < 0)
1360 col = 0;
1361 else
1362 if (col > max_line_len - 2*glyph_width('o'))
1363 col = max_line_len - 2*glyph_width('o');
1365 return col;
1368 /* settings helper functions */
1370 static bool encoding_setting(void)
1372 static struct opt_items names[NUM_CODEPAGES];
1373 int idx;
1375 for (idx = 0; idx < NUM_CODEPAGES; idx++)
1377 names[idx].string = rb->get_codepage_name(idx);
1378 names[idx].voice_id = -1;
1381 return rb->set_option("Encoding", &prefs.encoding, INT, names,
1382 sizeof(names) / sizeof(names[0]), NULL);
1385 static bool word_wrap_setting(void)
1387 static const struct opt_items names[] = {
1388 {"On", -1},
1389 {"Off (Chop Words)", -1},
1392 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
1393 names, 2, NULL);
1396 static bool line_mode_setting(void)
1398 static const struct opt_items names[] = {
1399 {"Normal", -1},
1400 {"Join Lines", -1},
1401 {"Expand Lines", -1},
1402 #ifdef HAVE_LCD_BITMAP
1403 {"Reflow Lines", -1},
1404 #endif
1407 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
1408 sizeof(names) / sizeof(names[0]), NULL);
1411 static bool view_mode_setting(void)
1413 static const struct opt_items names[] = {
1414 {"No (Narrow)", -1},
1415 {"Yes", -1},
1417 bool ret;
1418 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
1419 names , 2, NULL);
1420 if (prefs.view_mode == NARROW)
1421 col = 0;
1422 return ret;
1425 static bool scroll_mode_setting(void)
1427 static const struct opt_items names[] = {
1428 {"Scroll by Page", -1},
1429 {"Scroll by Line", -1},
1432 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
1433 names, 2, NULL);
1436 #ifdef HAVE_LCD_BITMAP
1437 static bool page_mode_setting(void)
1439 static const struct opt_items names[] = {
1440 {"No", -1},
1441 {"Yes", -1},
1444 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
1445 names, 2, NULL);
1448 static bool scrollbar_setting(void)
1450 static const struct opt_items names[] = {
1451 {"Off", -1},
1452 {"On", -1}
1455 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
1456 names, 2, NULL);
1458 #endif
1460 static bool autoscroll_speed_setting(void)
1462 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
1463 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
1466 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
1467 NULL, NULL, Icon_NOICON);
1468 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
1469 NULL, NULL, Icon_NOICON);
1470 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
1471 NULL, NULL, Icon_NOICON);
1472 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
1473 NULL, NULL, Icon_NOICON);
1474 #ifdef HAVE_LCD_BITMAP
1475 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
1476 NULL, NULL, Icon_NOICON);
1477 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
1478 NULL, NULL, Icon_NOICON);
1479 #endif
1480 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
1481 NULL, NULL, Icon_NOICON);
1482 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
1483 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
1484 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
1485 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
1486 #ifdef HAVE_LCD_BITMAP
1487 &scrollbar_item, &page_mode_item,
1488 #endif
1489 &scroll_mode_item, &autoscroll_speed_item);
1491 static bool viewer_options_menu(void)
1493 bool result;
1494 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
1496 #ifdef HAVE_LCD_BITMAP
1497 /* Show-scrollbar mode for current view-width mode */
1498 init_need_scrollbar();
1499 #endif
1500 return result;
1503 static void viewer_menu(void)
1505 int result;
1506 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
1507 "Return", "Viewer Options",
1508 "Show Playback Menu", "Quit");
1510 result = rb->do_menu(&menu, NULL, NULL, false);
1511 switch (result)
1513 case 0: /* return */
1514 break;
1515 case 1: /* change settings */
1516 done = viewer_options_menu();
1517 break;
1518 case 2: /* playback control */
1519 playback_control(NULL);
1520 break;
1521 case 3: /* quit */
1522 viewer_exit(NULL);
1523 done = true;
1524 break;
1526 viewer_draw(col);
1529 enum plugin_status plugin_start(const void* file)
1531 int button, i, ok;
1532 int lastbutton = BUTTON_NONE;
1533 bool autoscroll = false;
1534 long old_tick;
1536 old_tick = *rb->current_tick;
1538 /* get the plugin buffer */
1539 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
1541 if (!file)
1542 return PLUGIN_ERROR;
1544 file_name = file;
1545 ok = viewer_init();
1546 if (!ok) {
1547 rb->splash(HZ, "Error opening file.");
1548 return PLUGIN_ERROR;
1551 viewer_load_settings(); /* load the preferences and bookmark */
1553 #if LCD_DEPTH > 1
1554 rb->lcd_set_backdrop(NULL);
1555 #endif
1557 viewer_draw(col);
1559 while (!done) {
1561 if(autoscroll)
1563 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
1565 viewer_scroll_down();
1566 viewer_draw(col);
1567 old_tick = *rb->current_tick;
1571 button = rb->button_get_w_tmo(HZ/10);
1572 switch (button) {
1573 case VIEWER_MENU:
1574 viewer_menu();
1575 break;
1577 case VIEWER_AUTOSCROLL:
1578 #ifdef VIEWER_AUTOSCROLL_PRE
1579 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
1580 break;
1581 #endif
1582 autoscroll = !autoscroll;
1583 break;
1585 case VIEWER_PAGE_UP:
1586 case VIEWER_PAGE_UP | BUTTON_REPEAT:
1587 if (prefs.scroll_mode == PAGE)
1589 /* Page up */
1590 #ifdef HAVE_LCD_BITMAP
1591 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
1592 #else
1593 for (i = 0; i < display_lines; i++)
1594 #endif
1595 viewer_scroll_up();
1597 else
1598 viewer_scroll_up();
1599 old_tick = *rb->current_tick;
1600 viewer_draw(col);
1601 break;
1603 case VIEWER_PAGE_DOWN:
1604 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
1605 if (prefs.scroll_mode == PAGE)
1607 /* Page down */
1608 if (next_screen_ptr != NULL)
1609 screen_top_ptr = next_screen_to_draw_ptr;
1611 else
1612 viewer_scroll_down();
1613 old_tick = *rb->current_tick;
1614 viewer_draw(col);
1615 break;
1617 case VIEWER_SCREEN_LEFT:
1618 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
1619 if (prefs.view_mode == WIDE) {
1620 /* Screen left */
1621 col -= draw_columns;
1622 col = col_limit(col);
1624 else { /* prefs.view_mode == NARROW */
1625 /* Top of file */
1626 viewer_top();
1629 viewer_draw(col);
1630 break;
1632 case VIEWER_SCREEN_RIGHT:
1633 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
1634 if (prefs.view_mode == WIDE) {
1635 /* Screen right */
1636 col += draw_columns;
1637 col = col_limit(col);
1639 else { /* prefs.view_mode == NARROW */
1640 /* Bottom of file */
1641 viewer_bottom();
1644 viewer_draw(col);
1645 break;
1647 #ifdef VIEWER_LINE_UP
1648 case VIEWER_LINE_UP:
1649 case VIEWER_LINE_UP | BUTTON_REPEAT:
1650 /* Scroll up one line */
1651 viewer_scroll_up();
1652 old_tick = *rb->current_tick;
1653 viewer_draw(col);
1654 break;
1656 case VIEWER_LINE_DOWN:
1657 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
1658 /* Scroll down one line */
1659 if (next_screen_ptr != NULL)
1660 screen_top_ptr = next_line_ptr;
1661 old_tick = *rb->current_tick;
1662 viewer_draw(col);
1663 break;
1664 #endif
1665 #ifdef VIEWER_COLUMN_LEFT
1666 case VIEWER_COLUMN_LEFT:
1667 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
1668 if (prefs.view_mode == WIDE) {
1669 /* Scroll left one column */
1670 col -= glyph_width('o');
1671 col = col_limit(col);
1672 viewer_draw(col);
1674 break;
1676 case VIEWER_COLUMN_RIGHT:
1677 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
1678 if (prefs.view_mode == WIDE) {
1679 /* Scroll right one column */
1680 col += glyph_width('o');
1681 col = col_limit(col);
1682 viewer_draw(col);
1684 break;
1685 #endif
1687 #ifdef VIEWER_RC_QUIT
1688 case VIEWER_RC_QUIT:
1689 #endif
1690 case VIEWER_QUIT:
1691 viewer_exit(NULL);
1692 done = true;
1693 break;
1695 default:
1696 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
1697 == SYS_USB_CONNECTED)
1698 return PLUGIN_USB_CONNECTED;
1699 break;
1701 if (button != BUTTON_NONE)
1703 lastbutton = button;
1704 rb->yield();
1707 return PLUGIN_OK;