fix yellow
[kugel-rb.git] / apps / plugins / viewer.c
bloba6f1bf1b5df351402cb5cf7cbebf008ef109f25d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include <ctype.h>
23 #include "lib/playback_control.h"
25 PLUGIN_HEADER
27 /* global settings file
28 * binary file, so dont use .cfg
30 * setting file format
32 * part byte count
33 * --------------------------------
34 * 'TVGS' 4
35 * version 1
36 * word_mode 1
37 * line_mode 1
38 * view_mode 1
39 * alignment 1
40 * encoding 1
41 * scrollbar_mode 1
42 * need_scrollbar 1
43 * page_mode 1
44 * page_number_mode 1
45 * title_mode 1
46 * scroll_mode 1
47 * autoscroll_speed 1
48 * font name MAX_PATH
50 #define GLOBAL_SETTINGS_FILE VIEWERS_DIR "/viewer.dat"
52 /* temporary file */
53 #define GLOBAL_SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp"
55 #define GLOBAL_SETTINGS_HEADER "\x54\x56\x47\x53" /* header="TVGS" */
56 #define GLOBAL_SETTINGS_H_SIZE 5
57 #define GLOBAL_SETTINGS_VERSION 0x32 /* version=2 */
58 #define GLOBAL_SETTINGS_FIRST_VERSION 0x31
60 /* preferences and bookmarks at each file
61 * binary file, so dont use .cfg
63 * setting file format
65 * part byte count
66 * --------------------------------
67 * 'TVS' 3
68 * version 1
69 * file count 2
70 * [1st file]
71 * file path MAX_PATH
72 * next file pos 2
73 * [preferences]
74 * word_mode 1
75 * line_mode 1
76 * view_mode 1
77 * alignment 1
78 * encoding 1
79 * scrollbar_mode 1
80 * need_scrollbar 1
81 * page_mode 1
82 * header_mode 1
83 * footer_mode 1
84 * scroll_mode 1
85 * autoscroll_speed 1
86 * font name MAX_PATH
87 * bookmark count 1
88 * [1st bookmark]
89 * file_position 4
90 * page 2
91 * line 1
92 * flag 1
93 * [2nd bookmark]
94 * ...
95 * [last bookmark]
96 * [2nd file]
97 * ...
98 * [last file]
100 #define SETTINGS_FILE VIEWERS_DIR "/viewer_file.dat"
102 /* temporary file */
103 #define SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp"
105 #define SETTINGS_HEADER "\x54\x56\x53" /* header="TVS" */
106 #define SETTINGS_H_SIZE 4
107 #define SETTINGS_VERSION 0x33 /* version=3 */
108 #define SETTINGS_FIRST_VERSION 0x32
110 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
111 #define NARROW_MAX_COLUMNS 64 /* Max displayable string len [narrow] (over-estimate) */
112 #define WIDE_MAX_COLUMNS 128 /* Max displayable string len [wide] (over-estimate) */
113 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
114 #define READ_PREV_ZONE (block_size*9/10) /* Arbitrary number less than SMALL_BLOCK_SIZE */
115 #define SMALL_BLOCK_SIZE block_size /* Smallest file chunk we will read */
116 #define LARGE_BLOCK_SIZE (block_size << 1) /* Preferable size of file chunk to read */
117 #define TOP_SECTOR buffer
118 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
119 #define BOTTOM_SECTOR (buffer + (SMALL_BLOCK_SIZE << 1))
120 #undef SCROLLBAR_WIDTH
121 #define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width
122 #define MAX_PAGE 9999
124 #define BOOKMARK_SIZE 8
125 #define MAX_BOOKMARKS 10 /* user setting bookmarks + last read page */
127 #define BOOKMARK_LAST 1
128 #define BOOKMARK_USER 2
130 #ifndef HAVE_LCD_BITMAP
131 #define BOOKMARK_ICON "\xee\x84\x81\x00"
132 #endif
134 #define PREFERENCES_SIZE (12 + MAX_PATH)
136 /* Out-Of-Bounds test for any pointer to data in the buffer */
137 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
139 /* Does the buffer contain the beginning of the file? */
140 #define BUFFER_BOF() (file_pos==0)
142 /* Does the buffer contain the end of the file? */
143 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
145 /* Formula for the endpoint address outside of buffer data */
146 #define BUFFER_END() \
147 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
149 /* Is the entire file being shown in one screen? */
150 #define ONE_SCREEN_FITS_ALL() \
151 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
153 /* Is a scrollbar called for on the current screen? */
154 #define NEED_SCROLLBAR() \
155 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
157 /* variable button definitions */
159 /* Recorder keys */
160 #if CONFIG_KEYPAD == RECORDER_PAD
161 #define VIEWER_QUIT BUTTON_OFF
162 #define VIEWER_PAGE_UP BUTTON_UP
163 #define VIEWER_PAGE_DOWN BUTTON_DOWN
164 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
165 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
166 #define VIEWER_MENU BUTTON_F1
167 #define VIEWER_AUTOSCROLL BUTTON_PLAY
168 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
169 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
170 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
171 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
172 #define VIEWER_BOOKMARK BUTTON_F2
174 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
175 #define VIEWER_QUIT BUTTON_OFF
176 #define VIEWER_PAGE_UP BUTTON_UP
177 #define VIEWER_PAGE_DOWN BUTTON_DOWN
178 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
179 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
180 #define VIEWER_MENU BUTTON_F1
181 #define VIEWER_AUTOSCROLL BUTTON_SELECT
182 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
183 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
184 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
185 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
186 #define VIEWER_BOOKMARK BUTTON_F2
188 /* Ondio keys */
189 #elif CONFIG_KEYPAD == ONDIO_PAD
190 #define VIEWER_QUIT BUTTON_OFF
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_MENU|BUTTON_REPEAT)
196 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
197 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
198 #define VIEWER_BOOKMARK (BUTTON_MENU|BUTTON_OFF)
200 /* Player keys */
201 #elif CONFIG_KEYPAD == PLAYER_PAD
202 #define VIEWER_QUIT BUTTON_STOP
203 #define VIEWER_PAGE_UP BUTTON_LEFT
204 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
205 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
206 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
207 #define VIEWER_MENU BUTTON_MENU
208 #define VIEWER_AUTOSCROLL BUTTON_PLAY
209 #define VIEWER_BOOKMARK BUTTON_ON
211 /* iRiver H1x0 && H3x0 keys */
212 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
213 (CONFIG_KEYPAD == IRIVER_H300_PAD)
214 #define VIEWER_QUIT BUTTON_OFF
215 #define VIEWER_PAGE_UP BUTTON_UP
216 #define VIEWER_PAGE_DOWN BUTTON_DOWN
217 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
218 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
219 #define VIEWER_MENU BUTTON_MODE
220 #define VIEWER_AUTOSCROLL BUTTON_SELECT
221 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
222 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
223 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
224 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
225 #define VIEWER_BOOKMARK (BUTTON_ON | BUTTON_SELECT)
227 #define VIEWER_RC_QUIT BUTTON_RC_STOP
229 /* iPods */
230 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
231 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
232 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
233 #define VIEWER_QUIT_PRE BUTTON_SELECT
234 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
235 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
236 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
237 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
238 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
239 #define VIEWER_MENU BUTTON_MENU
240 #define VIEWER_AUTOSCROLL BUTTON_PLAY
241 #define VIEWER_BOOKMARK BUTTON_SELECT
243 /* iFP7xx keys */
244 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
245 #define VIEWER_QUIT BUTTON_PLAY
246 #define VIEWER_PAGE_UP BUTTON_UP
247 #define VIEWER_PAGE_DOWN BUTTON_DOWN
248 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
249 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
250 #define VIEWER_MENU BUTTON_MODE
251 #define VIEWER_AUTOSCROLL BUTTON_SELECT
252 #define VIEWER_BOOKMARK (BUTTON_LEFT|BUTTON_SELECT)
254 /* iAudio X5 keys */
255 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
256 #define VIEWER_QUIT BUTTON_POWER
257 #define VIEWER_PAGE_UP BUTTON_UP
258 #define VIEWER_PAGE_DOWN BUTTON_DOWN
259 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
260 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
261 #define VIEWER_MENU BUTTON_SELECT
262 #define VIEWER_AUTOSCROLL BUTTON_PLAY
263 #define VIEWER_BOOKMARK BUTTON_REC
265 /* GIGABEAT keys */
266 #elif CONFIG_KEYPAD == GIGABEAT_PAD
267 #define VIEWER_QUIT BUTTON_POWER
268 #define VIEWER_PAGE_UP BUTTON_UP
269 #define VIEWER_PAGE_DOWN BUTTON_DOWN
270 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
271 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
272 #define VIEWER_MENU BUTTON_MENU
273 #define VIEWER_AUTOSCROLL BUTTON_A
274 #define VIEWER_BOOKMARK BUTTON_SELECT
276 /* Sansa E200 keys */
277 #elif CONFIG_KEYPAD == SANSA_E200_PAD
278 #define VIEWER_QUIT BUTTON_POWER
279 #define VIEWER_PAGE_UP BUTTON_UP
280 #define VIEWER_PAGE_DOWN BUTTON_DOWN
281 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
282 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
283 #define VIEWER_MENU BUTTON_SELECT
284 #define VIEWER_AUTOSCROLL BUTTON_REC
285 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
286 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
287 #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
289 /* Sansa Fuze keys */
290 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
291 #define VIEWER_QUIT (BUTTON_HOME|BUTTON_REPEAT)
292 #define VIEWER_PAGE_UP BUTTON_UP
293 #define VIEWER_PAGE_DOWN BUTTON_DOWN
294 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
295 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
296 #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT
297 #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
298 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
299 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
300 #define VIEWER_BOOKMARK BUTTON_SELECT
302 /* Sansa C200 keys */
303 #elif CONFIG_KEYPAD == SANSA_C200_PAD
304 #define VIEWER_QUIT BUTTON_POWER
305 #define VIEWER_PAGE_UP BUTTON_VOL_UP
306 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
307 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
308 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
309 #define VIEWER_MENU BUTTON_SELECT
310 #define VIEWER_AUTOSCROLL BUTTON_REC
311 #define VIEWER_LINE_UP BUTTON_UP
312 #define VIEWER_LINE_DOWN BUTTON_DOWN
313 #define VIEWER_BOOKMARK (BUTTON_DOWN | BUTTON_SELECT)
315 /* Sansa Clip keys */
316 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
317 #define VIEWER_QUIT BUTTON_POWER
318 #define VIEWER_PAGE_UP BUTTON_VOL_UP
319 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
320 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
321 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
322 #define VIEWER_MENU BUTTON_SELECT
323 #define VIEWER_AUTOSCROLL BUTTON_HOME
324 #define VIEWER_LINE_UP BUTTON_UP
325 #define VIEWER_LINE_DOWN BUTTON_DOWN
326 #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
328 /* Sansa M200 keys */
329 #elif CONFIG_KEYPAD == SANSA_M200_PAD
330 #define VIEWER_QUIT BUTTON_POWER
331 #define VIEWER_PAGE_UP BUTTON_VOL_UP
332 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
333 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
334 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
335 #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP)
336 #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
337 #define VIEWER_LINE_UP BUTTON_UP
338 #define VIEWER_LINE_DOWN BUTTON_DOWN
339 #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
341 /* iriver H10 keys */
342 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
343 #define VIEWER_QUIT BUTTON_POWER
344 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
345 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
346 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
347 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
348 #define VIEWER_MENU BUTTON_REW
349 #define VIEWER_AUTOSCROLL BUTTON_PLAY
350 #define VIEWER_BOOKMARK BUTTON_FF
352 /*M-Robe 500 keys */
353 #elif CONFIG_KEYPAD == MROBE500_PAD
354 #define VIEWER_QUIT BUTTON_POWER
355 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
356 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
357 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
358 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
359 #define VIEWER_MENU BUTTON_RC_HEART
360 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
361 #define VIEWER_BOOKMARK BUTTON_CENTER
363 /*Gigabeat S keys */
364 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
365 #define VIEWER_QUIT BUTTON_BACK
366 #define VIEWER_PAGE_UP BUTTON_PREV
367 #define VIEWER_PAGE_DOWN BUTTON_NEXT
368 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
369 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
370 #define VIEWER_MENU BUTTON_MENU
371 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
372 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
373 #define VIEWER_LINE_UP BUTTON_UP
374 #define VIEWER_LINE_DOWN BUTTON_DOWN
375 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
376 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
377 #define VIEWER_BOOKMARK BUTTON_SELECT
379 /*M-Robe 100 keys */
380 #elif CONFIG_KEYPAD == MROBE100_PAD
381 #define VIEWER_QUIT BUTTON_POWER
382 #define VIEWER_PAGE_UP BUTTON_UP
383 #define VIEWER_PAGE_DOWN BUTTON_DOWN
384 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
385 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
386 #define VIEWER_MENU BUTTON_MENU
387 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
388 #define VIEWER_BOOKMARK BUTTON_SELECT
390 /* iAUdio M3 keys */
391 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
392 #define VIEWER_QUIT BUTTON_REC
393 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
394 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
395 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
396 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
397 #define VIEWER_MENU BUTTON_RC_MENU
398 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
399 #define VIEWER_RC_QUIT BUTTON_RC_REC
400 #define VIEWER_BOOKMARK BUTTON_RC_PLAY
402 /* Cowon D2 keys */
403 #elif CONFIG_KEYPAD == COWON_D2_PAD
404 #define VIEWER_QUIT BUTTON_POWER
405 #define VIEWER_MENU BUTTON_MENU
406 #define VIEWER_PAGE_UP BUTTON_MINUS
407 #define VIEWER_PAGE_DOWN BUTTON_PLUS
408 #define VIEWER_BOOKMARK (BUTTON_MENU|BUTTON_PLUS)
410 #elif CONFIG_KEYPAD == IAUDIO67_PAD
411 #define VIEWER_QUIT BUTTON_POWER
412 #define VIEWER_PAGE_UP BUTTON_VOLUP
413 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
414 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
415 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
416 #define VIEWER_MENU BUTTON_MENU
417 #define VIEWER_AUTOSCROLL BUTTON_PLAY
418 #define VIEWER_RC_QUIT BUTTON_STOP
419 #define VIEWER_BOOKMARK (BUTTON_LEFT|BUTTON_PLAY)
421 /* Creative Zen Vision:M keys */
422 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
423 #define VIEWER_QUIT BUTTON_BACK
424 #define VIEWER_PAGE_UP BUTTON_UP
425 #define VIEWER_PAGE_DOWN BUTTON_DOWN
426 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
427 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
428 #define VIEWER_MENU BUTTON_MENU
429 #define VIEWER_AUTOSCROLL BUTTON_SELECT
430 #define VIEWER_BOOKMARK BUTTON_PLAY
432 /* Philips HDD1630 keys */
433 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
434 #define VIEWER_QUIT BUTTON_POWER
435 #define VIEWER_PAGE_UP BUTTON_UP
436 #define VIEWER_PAGE_DOWN BUTTON_DOWN
437 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
438 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
439 #define VIEWER_MENU BUTTON_MENU
440 #define VIEWER_AUTOSCROLL BUTTON_VIEW
441 #define VIEWER_BOOKMARK BUTTON_SELECT
443 /* Philips SA9200 keys */
444 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
445 #define VIEWER_QUIT BUTTON_POWER
446 #define VIEWER_PAGE_UP BUTTON_UP
447 #define VIEWER_PAGE_DOWN BUTTON_DOWN
448 #define VIEWER_SCREEN_LEFT BUTTON_PREV
449 #define VIEWER_SCREEN_RIGHT BUTTON_NEXT
450 #define VIEWER_MENU BUTTON_MENU
451 #define VIEWER_AUTOSCROLL BUTTON_PLAY
452 #define VIEWER_BOOKMARK BUTTON_RIGHT
454 /* Onda VX747 keys */
455 #elif CONFIG_KEYPAD == ONDAVX747_PAD
456 #define VIEWER_QUIT BUTTON_POWER
457 #define VIEWER_MENU BUTTON_MENU
458 #define VIEWER_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER)
460 /* Onda VX777 keys */
461 #elif CONFIG_KEYPAD == ONDAVX777_PAD
462 #define VIEWER_QUIT BUTTON_POWER
463 #define VIEWER_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER)
465 /* SAMSUNG YH-820 / YH-920 / YH-925 keys */
466 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
467 #define VIEWER_QUIT BUTTON_REC
468 #define VIEWER_PAGE_UP BUTTON_UP
469 #define VIEWER_PAGE_DOWN BUTTON_DOWN
470 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
471 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
472 #define VIEWER_MENU BUTTON_PLAY
473 #define VIEWER_AUTOSCROLL BUTTON_REW
474 #define VIEWER_BOOKMARK BUTTON_FFWD
476 /* Packard Bell Vibe 500 keys */
477 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
478 #define VIEWER_QUIT BUTTON_REC
479 #define VIEWER_PAGE_UP BUTTON_OK
480 #define VIEWER_PAGE_DOWN BUTTON_CANCEL
481 #define VIEWER_LINE_UP BUTTON_UP
482 #define VIEWER_LINE_DOWN BUTTON_DOWN
483 #define VIEWER_SCREEN_LEFT BUTTON_PREV
484 #define VIEWER_SCREEN_RIGHT BUTTON_NEXT
485 #define VIEWER_MENU BUTTON_MENU
486 #define VIEWER_AUTOSCROLL BUTTON_PLAY
487 #define VIEWER_BOOKMARK BUTTON_POWER
489 #else
490 #error No keymap defined!
491 #endif
493 #ifdef HAVE_TOUCHSCREEN
494 #ifdef VIEWER_QUIT
495 #define VIEWER_QUIT2 BUTTON_TOPLEFT
496 #else
497 #define VIEWER_QUIT BUTTON_TOPLEFT
498 #endif
499 #ifdef VIEWER_PAGE_UP
500 #define VIEWER_PAGE_UP2 BUTTON_TOPMIDDLE
501 #else
502 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
503 #endif
504 #ifdef VIEWER_PAGE_DOWN
505 #define VIEWER_PAGE_DOWN2 BUTTON_BOTTOMMIDDLE
506 #else
507 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
508 #endif
509 #ifndef VIEWER_SCREEN_LEFT
510 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
511 #endif
512 #ifndef VIEWER_SCREEN_RIGHT
513 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
514 #endif
515 #ifdef VIEWER_MENU
516 #define VIEWER_MENU2 BUTTON_TOPRIGHT
517 #else
518 #define VIEWER_MENU BUTTON_TOPRIGHT
519 #endif
520 #ifndef VIEWER_AUTOSCROLL
521 #define VIEWER_AUTOSCROLL BUTTON_CENTER
522 #endif
523 #endif
525 /* stuff for the bookmarking */
526 struct bookmark_info {
527 long file_position;
528 int page;
529 int line;
530 unsigned char flag;
533 struct preferences {
534 enum {
535 WRAP=0,
536 CHOP,
537 } word_mode;
539 enum {
540 NORMAL=0,
541 JOIN,
542 EXPAND,
543 REFLOW, /* won't be set on charcell LCD, must be last */
544 } line_mode;
546 enum {
547 NARROW=0,
548 WIDE,
549 } view_mode;
551 enum {
552 LEFT=0,
553 RIGHT,
554 } alignment;
556 enum codepages encoding;
558 enum {
559 SB_OFF=0,
560 SB_ON,
561 } scrollbar_mode;
562 bool need_scrollbar;
564 enum {
565 NO_OVERLAP=0,
566 OVERLAP,
567 } page_mode;
569 enum {
570 HD_NONE = 0,
571 HD_PATH,
572 HD_SBAR,
573 HD_BOTH,
574 } header_mode;
576 enum {
577 FT_NONE = 0,
578 FT_PAGE,
579 FT_SBAR,
580 FT_BOTH,
581 } footer_mode;
583 enum {
584 PAGE=0,
585 LINE,
586 } scroll_mode;
588 int autoscroll_speed;
590 unsigned char font[MAX_PATH];
593 enum {
594 VIEWER_FONT_MENU = 0,
595 VIEWER_FONT_TEXT,
598 struct preferences prefs;
599 struct preferences old_prefs;
601 static unsigned char *buffer;
602 static long buffer_size;
603 static long block_size = 0x1000;
604 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
605 static int display_columns; /* number of (pixel) columns on the display */
606 static int display_lines; /* number of lines on the display */
607 static int draw_columns; /* number of (pixel) columns available for text */
608 static int par_indent_spaces; /* number of spaces to indent first paragraph */
609 static int fd;
610 static const char *file_name;
611 static long file_size;
612 static long start_position; /* position in the file after the viewer is started */
613 static bool mac_text;
614 static long file_pos; /* Position of the top of the buffer in the file */
615 static long last_file_pos;
616 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
617 static int max_line_len;
618 static int max_width;
619 static int max_columns;
620 static int cline = 1;
621 static int cpage = 1;
622 static int lpage = 0;
623 static unsigned char *screen_top_ptr;
624 static unsigned char *next_screen_ptr;
625 static unsigned char *next_screen_to_draw_ptr;
626 static unsigned char *next_line_ptr;
627 static unsigned char *last_screen_top_ptr = NULL;
628 #ifdef HAVE_LCD_BITMAP
629 static struct font *pf;
630 static int header_height = 0;
631 static int footer_height = 0;
632 #endif
633 struct bookmark_info bookmarks[MAX_BOOKMARKS];
634 static int bookmark_count;
636 /* UTF-8 BOM */
637 #define BOM "\xef\xbb\xbf"
638 #define BOM_SIZE 3
640 static bool is_bom = false;
642 /* We draw a diacritic char over a non-diacritic one. Therefore, such chars are
643 * not considered to occupy space, therefore buffers might have more than
644 * max_columns characters. The DIACRITIC_FACTOR is the max ratio between all
645 * characters and non-diacritic characters in the buffer
647 #define DIACRITIC_FACTOR 2
649 /* calculate the width of a UCS character (zero width for diacritics) */
650 static int glyph_width(unsigned short ch)
652 if (ch == 0)
653 ch = ' ';
655 #ifdef HAVE_LCD_BITMAP
656 if (rb->is_diacritic(ch, NULL))
657 return 0;
659 return rb->font_get_width(pf, ch);
660 #else
661 return 1;
662 #endif
665 /* get UCS character from string */
666 static unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
668 unsigned char utf8_tmp[6];
669 int count = 2;
671 if (prefs.encoding == UTF_8)
672 return (unsigned char*)rb->utf8decode(str, ch);
674 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
675 rb->utf8decode(utf8_tmp, ch);
677 /* return a pointer after the parsed section of the string */
678 #ifdef HAVE_LCD_BITMAP
679 if (prefs.encoding >= SJIS && *str >= 0x80
680 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
681 return (unsigned char*)str+2;
682 else
683 #endif
684 return (unsigned char*)str+1;
687 /* decode iso string into UTF-8 string */
688 static unsigned char *decode2utf8(const unsigned char *src, unsigned char *dst,
689 int skip_width, int disp_width)
691 unsigned short ucs[max_columns * DIACRITIC_FACTOR + 1];
692 unsigned short ch;
693 const unsigned char *oldstr = src;
694 const unsigned char *str = src;
695 unsigned char *utf8 = dst;
696 int chars = 0;
697 int idx = 0;
698 int width = max_width;
700 if (prefs.alignment == LEFT)
702 /* skip the skip_width */
703 if (skip_width > 0)
705 while (skip_width > 0 && *str != '\0')
707 oldstr = str;
708 str = get_ucs(oldstr, &ch);
709 skip_width -= glyph_width(ch);
711 if (skip_width < 0)
712 str = oldstr;
715 /* decode until string end or disp_width reached */
716 while(*str != '\0')
718 str = get_ucs(str, &ch);
719 disp_width -= glyph_width(ch);
720 if (disp_width < 0)
721 break;
722 utf8 = rb->utf8encode(ch, utf8);
725 else
727 while (width > 0 && *str != '\0')
729 str = get_ucs(str, &ch);
730 ucs[chars++] = ch;
732 ucs[chars] = 0;
734 skip_width = max_width - skip_width - disp_width;
735 if (skip_width > 0)
737 while (skip_width > 0 && chars-- > 0)
738 skip_width -= glyph_width(ucs[chars]);
740 if (skip_width < 0)
741 chars++;
743 else
745 idx = chars;
746 while (disp_width > 0 && idx-- > 0)
747 disp_width -= glyph_width(ucs[idx]);
749 if (disp_width < 0 || idx < 0)
750 idx++;
753 for ( ; idx < chars; idx++)
754 utf8 = rb->utf8encode(ucs[idx], utf8);
757 *utf8 = '\0';
759 /* return a pointer after the dst string ends */
760 return utf8;
763 /* set max_columns and max_width */
764 static void calc_max_width(void)
766 if (prefs.view_mode == NARROW)
768 max_columns = NARROW_MAX_COLUMNS;
769 max_width = draw_columns;
771 else
773 max_columns = WIDE_MAX_COLUMNS;
774 max_width = 2 * draw_columns;
778 static bool done = false;
779 static int col = 0;
781 static inline void advance_conters(unsigned short ch, int* k, int* width)
783 #ifdef HAVE_LCD_BITMAP
784 /* diacritics do not count */
785 if (rb->is_diacritic(ch, NULL))
786 return;
787 #endif
789 *width += glyph_width(ch);
790 (*k)++;
793 static inline bool line_is_full(int k, int width)
795 return ((k >= max_columns - 1) || (width >= max_width));
798 static unsigned char* crop_at_width(const unsigned char* p)
800 int k,width;
801 unsigned short ch;
802 const unsigned char *oldp = p;
804 k=width=0;
806 while (!line_is_full(k, width)) {
807 oldp = p;
808 if (BUFFER_OOB(p))
809 break;
810 p = get_ucs(p, &ch);
811 advance_conters(ch, &k, &width);
814 return (unsigned char*)oldp;
817 static unsigned char* find_first_feed(const unsigned char* p, int size)
819 int s = 0;
820 unsigned short ch;
821 const unsigned char *oldp = p;
822 const unsigned char *lbrkp = NULL;
823 int j;
824 int width = 0;
826 while(s <= size)
828 if (*p == 0)
829 return (unsigned char*)p;
830 oldp = p;
831 p = get_ucs(p, &ch);
833 if (prefs.word_mode == WRAP)
835 for (j = 0; j < ((int) sizeof(line_break)); j++)
837 if (ch == line_break[j])
839 lbrkp = p;
840 break;
845 width += glyph_width(ch);
846 if (width > max_width)
847 return (lbrkp == NULL)? (unsigned char*)oldp : (unsigned char*)lbrkp;
849 s += (p - oldp);
852 return NULL;
855 static unsigned char* find_last_feed(const unsigned char* p, int size)
857 int i;
859 for (i=size-1; i>=0; i--)
860 if (p[i] == 0)
861 return (unsigned char*) p+i;
863 return NULL;
866 static unsigned char* find_last_space(const unsigned char* p, int size)
868 int i, j, k;
870 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
872 i = size;
873 if (!BUFFER_OOB(&p[i]))
874 for (j=k; j < ((int) sizeof(line_break)) - 1; j++) {
875 if (p[i] == line_break[j])
876 return (unsigned char*) p+i;
879 if (prefs.word_mode == WRAP) {
880 for (i=size-1; i>=0; i--) {
881 for (j=k; j < (int) sizeof(line_break) - 1; j++) {
882 if (p[i] == line_break[j])
883 return (unsigned char*) p+i;
888 return NULL;
891 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
893 const unsigned char *next_line = NULL;
894 int size, i, j, j_next, j_prev, k, width, search_len, spaces, newlines;
895 bool first_chars;
896 unsigned short ch;
898 if (is_short != NULL)
899 *is_short = true;
901 if BUFFER_OOB(cur_line)
902 return NULL;
904 if (prefs.view_mode == WIDE) {
905 search_len = MAX_WIDTH;
907 else { /* prefs.view_mode == NARROW */
908 search_len = crop_at_width(cur_line) - cur_line;
911 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
913 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
914 /* Need to scan ahead and possibly increase search_len and size,
915 or possibly set next_line at second hard return in a row. */
916 next_line = NULL;
917 first_chars=true;
918 j_next=j=k=width=spaces=newlines=0;
919 while (1) {
920 const unsigned char *p, *oldp;
922 j_prev = j;
923 j = j_next;
925 if (BUFFER_OOB(cur_line+j))
926 return NULL;
927 if (line_is_full(k, width)) {
928 size = search_len = j_prev;
929 break;
932 oldp = p = &cur_line[j];
933 p = get_ucs(p, &ch);
934 j_next = j + (p - oldp);
936 switch (ch) {
937 case ' ':
938 if (prefs.line_mode == REFLOW) {
939 if (newlines > 0) {
940 size = j;
941 next_line = cur_line + size;
942 return (unsigned char*) next_line;
944 if (j==0) /* i=1 is intentional */
945 for (i=0; i<par_indent_spaces; i++)
946 advance_conters(' ', &k, &width);
948 if (!first_chars) spaces++;
949 break;
951 case 0:
952 if (newlines > 0) {
953 size = j;
954 next_line = cur_line + size - spaces;
955 if (next_line != cur_line)
956 return (unsigned char*) next_line;
957 break;
960 newlines++;
961 size += spaces -1;
962 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
963 return NULL;
964 search_len = size;
965 spaces = first_chars? 0:1;
966 break;
968 default:
969 if (prefs.line_mode==JOIN || newlines>0) {
970 while (spaces) {
971 spaces--;
972 advance_conters(' ', &k, &width);
973 if (line_is_full(k, width)) {
974 size = search_len = j;
975 break;
978 newlines=0;
979 } else if (spaces) {
980 /* REFLOW, multiple spaces between words: count only
981 * one. If more are needed, they will be added
982 * while drawing. */
983 search_len = size;
984 spaces=0;
985 advance_conters(' ', &k, &width);
986 if (line_is_full(k, width)) {
987 size = search_len = j;
988 break;
991 first_chars = false;
992 advance_conters(ch, &k, &width);
993 break;
997 else {
998 /* find first hard return */
999 next_line = find_first_feed(cur_line, size);
1002 if (next_line == NULL)
1003 if (size == search_len) {
1004 if (prefs.word_mode == WRAP) /* Find last space */
1005 next_line = find_last_space(cur_line, size);
1007 if (next_line == NULL) {
1008 next_line = crop_at_width(cur_line);
1010 else {
1011 if (prefs.word_mode == WRAP) {
1012 for (i=0;i<WRAP_TRIM;i++) {
1013 if (!(isspace(next_line[0]) && !BUFFER_OOB(next_line)))
1014 break;
1015 next_line++;
1021 if (prefs.line_mode == EXPAND)
1022 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1023 if (next_line[0] == 0)
1024 if (next_line != cur_line)
1025 return (unsigned char*) next_line;
1027 /* If next_line is pointing to a zero, increment it; i.e.,
1028 leave the terminator at the end of cur_line. If pointing
1029 to a hyphen, increment only if there is room to display
1030 the hyphen on current line (won't apply in WIDE mode,
1031 since it's guarenteed there won't be room). */
1032 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1033 if (next_line[0] == 0)/* ||
1034 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
1035 next_line++;
1037 if (BUFFER_OOB(next_line))
1039 if (BUFFER_EOF() && next_line != cur_line)
1040 return (unsigned char*) next_line;
1041 return NULL;
1044 if (is_short)
1045 *is_short = false;
1047 return (unsigned char*) next_line;
1050 static unsigned char* find_prev_line(const unsigned char* cur_line)
1052 const unsigned char *prev_line = NULL;
1053 const unsigned char *p;
1055 if BUFFER_OOB(cur_line)
1056 return NULL;
1058 /* To wrap consistently at the same places, we must
1059 start with a known hard return, then work downwards.
1060 We can either search backwards for a hard return,
1061 or simply start wrapping downwards from top of buffer.
1062 If current line is not near top of buffer, this is
1063 a file with long lines (paragraphs). We would need to
1064 read earlier sectors before we could decide how to
1065 properly wrap the lines above the current line, but
1066 it probably is not worth the disk access. Instead,
1067 start with top of buffer and wrap down from there.
1068 This may result in some lines wrapping at different
1069 points from where they wrap when scrolling down.
1070 If buffer is at top of file, start at top of buffer. */
1072 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
1073 prev_line = p = NULL;
1074 else
1075 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
1076 /* Null means no line feeds in buffer above current line. */
1078 if (prev_line == NULL)
1079 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
1080 prev_line = p = buffer;
1081 /* (else return NULL and read previous block) */
1083 /* Wrap downwards until too far, then use the one before. */
1084 while (p != NULL && p < cur_line) {
1085 prev_line = p;
1086 p = find_next_line(prev_line, NULL);
1089 if (BUFFER_OOB(prev_line))
1090 return NULL;
1092 return (unsigned char*) prev_line;
1095 static void check_bom(void)
1097 unsigned char bom[BOM_SIZE];
1098 off_t orig = rb->lseek(fd, 0, SEEK_CUR);
1100 is_bom = false;
1102 rb->lseek(fd, 0, SEEK_SET);
1104 if (rb->read(fd, bom, BOM_SIZE) == BOM_SIZE)
1105 is_bom = !memcmp(bom, BOM, BOM_SIZE);
1107 rb->lseek(fd, orig, SEEK_SET);
1110 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
1112 /* Read from file and preprocess the data */
1113 /* To minimize disk access, always read on sector boundaries */
1114 unsigned numread, i;
1115 bool found_CR = false;
1116 off_t offset = rb->lseek(fd, pos, SEEK_SET);
1118 if (offset == 0 && prefs.encoding == UTF_8 && is_bom)
1119 rb->lseek(fd, BOM_SIZE, SEEK_SET);
1121 numread = rb->read(fd, buf, size - 1);
1122 buf[numread] = 0;
1123 rb->button_clear_queue(); /* clear button queue */
1125 for(i = 0; i < numread; i++) {
1126 switch(buf[i]) {
1127 case '\r':
1128 if (mac_text) {
1129 buf[i] = 0;
1131 else {
1132 buf[i] = ' ';
1133 found_CR = true;
1135 break;
1137 case '\n':
1138 buf[i] = 0;
1139 found_CR = false;
1140 break;
1142 case 0: /* No break between case 0 and default, intentionally */
1143 buf[i] = ' ';
1144 default:
1145 if (found_CR) {
1146 buf[i - 1] = 0;
1147 found_CR = false;
1148 mac_text = true;
1150 break;
1155 static int viewer_find_bookmark(int page, int line)
1157 int i;
1159 for (i = 0; i < bookmark_count; i++)
1161 if (bookmarks[i].page == page && bookmarks[i].line == line)
1162 return i;
1164 return -1;
1167 static int read_and_synch(int direction)
1169 /* Read next (or prev) block, and reposition global pointers. */
1170 /* direction: 1 for down (i.e., further into file), -1 for up */
1171 int move_size, move_vector, offset;
1172 unsigned char *fill_buf;
1174 if (direction == -1) /* up */ {
1175 move_size = SMALL_BLOCK_SIZE;
1176 offset = 0;
1177 fill_buf = TOP_SECTOR;
1178 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1179 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
1181 else /* down */ {
1182 if (prefs.view_mode == WIDE) {
1183 /* WIDE mode needs more buffer so we have to read smaller blocks */
1184 move_size = SMALL_BLOCK_SIZE;
1185 offset = LARGE_BLOCK_SIZE;
1186 fill_buf = BOTTOM_SECTOR;
1187 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1188 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1190 else {
1191 move_size = LARGE_BLOCK_SIZE;
1192 offset = SMALL_BLOCK_SIZE;
1193 fill_buf = MID_SECTOR;
1194 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1197 move_vector = direction * move_size;
1198 screen_top_ptr -= move_vector;
1199 file_pos += move_vector;
1200 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1201 fill_buffer(file_pos + offset, fill_buf, move_size);
1202 return move_vector;
1205 static void get_next_line_position(unsigned char **line_begin,
1206 unsigned char **line_end,
1207 bool *is_short)
1209 int resynch_move;
1211 *line_begin = *line_end;
1212 *line_end = find_next_line(*line_begin, is_short);
1214 if (*line_end == NULL && !BUFFER_EOF())
1216 resynch_move = read_and_synch(1); /* Read block & move ptrs */
1217 *line_begin -= resynch_move;
1218 if (next_line_ptr > buffer)
1219 next_line_ptr -= resynch_move;
1221 *line_end = find_next_line(*line_begin, is_short);
1225 static void increment_current_line(void)
1227 if (cline < display_lines)
1228 cline++;
1229 else if (cpage < MAX_PAGE)
1231 cpage++;
1232 cline = 1;
1236 static void decrement_current_line(void)
1238 if (cline > 1)
1239 cline--;
1240 else if (cpage > 1)
1242 cpage--;
1243 cline = display_lines;
1247 static void viewer_scroll_up(void)
1249 unsigned char *p;
1251 p = find_prev_line(screen_top_ptr);
1252 if (p == NULL && !BUFFER_BOF()) {
1253 read_and_synch(-1);
1254 p = find_prev_line(screen_top_ptr);
1256 if (p != NULL)
1257 screen_top_ptr = p;
1259 decrement_current_line();
1262 static void viewer_scroll_down(bool autoscroll)
1264 if (cpage == lpage)
1265 return;
1267 if (next_line_ptr != NULL)
1268 screen_top_ptr = next_line_ptr;
1270 if (prefs.scroll_mode == LINE || autoscroll)
1271 increment_current_line();
1274 static void viewer_scroll_to_top_line(void)
1276 int line;
1278 for (line = cline; line > 1; line--)
1279 viewer_scroll_up();
1282 #ifdef HAVE_LCD_BITMAP
1283 static void viewer_scrollbar(void) {
1284 int items, min_shown, max_shown, sb_begin_y, sb_height;
1286 items = (int) file_size; /* (SH1 int is same as long) */
1287 min_shown = (int) file_pos + (screen_top_ptr - buffer);
1289 if (next_screen_ptr == NULL)
1290 max_shown = items;
1291 else
1292 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
1294 sb_begin_y = header_height;
1295 sb_height = LCD_HEIGHT - header_height - footer_height;
1297 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, sb_begin_y,
1298 SCROLLBAR_WIDTH-1, sb_height,
1299 items, min_shown, max_shown, VERTICAL);
1301 #endif
1303 #ifdef HAVE_LCD_BITMAP
1304 static void viewer_show_header(void)
1306 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1307 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1309 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1310 rb->lcd_putsxy(0, header_height - pf->height, file_name);
1313 static void viewer_show_footer(void)
1315 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1316 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1318 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1320 unsigned char buf[12];
1322 if (cline == 1)
1323 rb->snprintf(buf, sizeof(buf), "%d", cpage);
1324 else
1325 rb->snprintf(buf, sizeof(buf), "%d - %d", cpage, cpage+1);
1327 rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf);
1330 #endif
1332 static void viewer_draw(int col)
1334 int i, j, k, line_len, line_width, spaces, left_col=0;
1335 int width, extra_spaces, indent_spaces, spaces_per_word, spaces_width, disp_width = 0;
1336 bool multiple_spacing, line_is_short;
1337 unsigned short ch;
1338 unsigned char *str, *oldstr;
1339 unsigned char *line_begin;
1340 unsigned char *line_end;
1341 unsigned char c;
1342 int max_chars = max_columns * DIACRITIC_FACTOR;
1343 unsigned char scratch_buffer[max_chars * 4 + 1];
1344 unsigned char utf8_buffer[max_chars * 4 + 1];
1346 /* If col==-1 do all calculations but don't display */
1347 if (col != -1) {
1348 #ifdef HAVE_LCD_BITMAP
1349 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
1350 #else
1351 left_col = 0;
1352 #endif
1353 rb->lcd_clear_display();
1355 max_line_len = 0;
1356 line_begin = line_end = screen_top_ptr;
1358 for (i = 0; i < display_lines; i++) {
1359 if (BUFFER_OOB(line_end))
1361 if (lpage == cpage)
1362 break; /* Happens after display last line at BUFFER_EOF() */
1364 if (lpage == 0 && cline == 1)
1366 lpage = cpage;
1367 last_screen_top_ptr = screen_top_ptr;
1368 last_file_pos = file_pos;
1372 get_next_line_position(&line_begin, &line_end, &line_is_short);
1373 if (line_end == NULL)
1375 if (BUFFER_OOB(line_begin))
1376 break;
1377 line_end = buffer_end + 1;
1380 line_len = line_end - line_begin;
1382 /* calculate line_len */
1383 str = oldstr = line_begin;
1384 j = -1;
1385 while (str < line_end) {
1386 oldstr = str;
1387 str = crop_at_width(str);
1388 j++;
1389 if (oldstr == str)
1391 oldstr = line_end;
1392 break;
1395 /* width of un-displayed part of the line */
1396 line_width = j*draw_columns;
1397 spaces_width = 0;
1398 while (oldstr < line_end) {
1399 oldstr = get_ucs(oldstr, &ch);
1400 /* add width of displayed part of the line */
1401 if (ch)
1403 int dw = glyph_width(ch);
1405 /* avoid counting spaces at the end of the line */
1406 if (ch == ' ')
1408 spaces_width += dw;
1410 else
1412 line_width += dw + spaces_width;
1413 spaces_width = 0;
1418 if (prefs.line_mode == JOIN) {
1419 if (line_begin[0] == 0) {
1420 line_begin++;
1421 if (prefs.word_mode == CHOP)
1422 line_end++;
1423 else
1424 line_len--;
1426 for (j=k=spaces=0; j < line_len; j++) {
1427 if (k == max_chars)
1428 break;
1430 c = line_begin[j];
1431 switch (c) {
1432 case ' ':
1433 spaces++;
1434 break;
1435 case 0:
1436 spaces = 0;
1437 scratch_buffer[k++] = ' ';
1438 break;
1439 default:
1440 while (spaces) {
1441 spaces--;
1442 scratch_buffer[k++] = ' ';
1443 if (k == max_chars - 1)
1444 break;
1446 scratch_buffer[k++] = c;
1447 break;
1450 scratch_buffer[k] = 0;
1452 else if (prefs.line_mode == REFLOW) {
1453 if (line_begin[0] == 0) {
1454 line_begin++;
1455 if (prefs.word_mode == CHOP)
1456 line_end++;
1457 else
1458 line_len--;
1461 indent_spaces = 0;
1462 if (!line_is_short) {
1463 multiple_spacing = false;
1464 width=spaces=0;
1465 for (str = line_begin; str < line_end; ) {
1466 str = get_ucs(str, &ch);
1467 switch (ch) {
1468 case 0:
1469 case ' ':
1470 if (str == line_begin)
1472 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1474 /* special case: indent the paragraph,
1475 * don't count spaces */
1476 indent_spaces = par_indent_spaces;
1479 else if (!multiple_spacing)
1480 spaces++;
1481 multiple_spacing = true;
1482 break;
1483 default:
1484 multiple_spacing = false;
1485 width += glyph_width(ch);
1486 break;
1489 if (multiple_spacing) spaces--;
1491 if (spaces) {
1492 /* total number of spaces to insert between words */
1493 extra_spaces = (max_width-width)/glyph_width(' ')
1494 - indent_spaces;
1495 /* number of spaces between each word*/
1496 spaces_per_word = extra_spaces / spaces;
1497 /* number of words with n+1 spaces (to fill up) */
1498 extra_spaces = extra_spaces % spaces;
1499 if (spaces_per_word > 2) { /* too much spacing is awful */
1500 spaces_per_word = 3;
1501 extra_spaces = 0;
1503 } else { /* this doesn't matter much... no spaces anyway */
1504 spaces_per_word = extra_spaces = 0;
1506 } else { /* end of a paragraph: don't fill line */
1507 spaces_per_word = 1;
1508 extra_spaces = 0;
1510 multiple_spacing = false;
1511 for (j=k=spaces=0; j < line_len; j++) {
1512 if (k == max_chars)
1513 break;
1515 c = line_begin[j];
1516 switch (c) {
1517 case 0:
1518 if (j == line_len - 1)
1519 break;
1520 case ' ':
1521 if (j==0) {
1522 /* indent paragraph */
1523 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1525 for (j=0; j<par_indent_spaces; j++)
1526 scratch_buffer[k++] = ' ';
1527 j=0;
1530 else if (!multiple_spacing) {
1531 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1532 scratch_buffer[k++] = ' ';
1533 spaces++;
1535 multiple_spacing = true;
1536 break;
1537 default:
1538 scratch_buffer[k++] = c;
1539 multiple_spacing = false;
1540 break;
1543 while (scratch_buffer[k-1] == ' ')
1544 k--;
1545 scratch_buffer[k] = 0;
1547 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1548 if (col != -1)
1550 rb->memcpy(scratch_buffer, line_begin, line_len);
1551 scratch_buffer[line_len] = '\0';
1555 /* create displayed line */
1556 if (col != -1)
1558 decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
1559 rb->lcd_getstringsize(utf8_buffer, &disp_width, NULL);
1562 /* display on screen the displayed part of the line */
1563 if (col != -1)
1565 int dpage = (cline+i <= display_lines)?cpage:cpage+1;
1566 int dline = cline+i - ((cline+i <= display_lines)?0:display_lines);
1567 bool bflag = (viewer_find_bookmark(dpage, dline) >= 0);
1568 #ifdef HAVE_LCD_BITMAP
1569 int dy = i * pf->height + header_height;
1570 int dx = (prefs.alignment == LEFT)? left_col : LCD_WIDTH - disp_width;
1571 #endif
1572 if (bflag)
1573 #ifdef HAVE_LCD_BITMAP
1575 rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG);
1576 rb->lcd_fillrect(left_col, dy, LCD_WIDTH - left_col, pf->height);
1577 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1579 rb->lcd_putsxy(dx, dy, utf8_buffer);
1580 rb->lcd_set_drawmode(DRMODE_SOLID);
1581 #else
1583 rb->lcd_puts(left_col, i, BOOKMARK_ICON);
1585 rb->lcd_puts(left_col+1, i, utf8_buffer);
1586 #endif
1588 if (line_width > max_line_len)
1589 max_line_len = line_width;
1591 if (i == 0)
1592 next_line_ptr = line_end;
1594 next_screen_ptr = line_end;
1595 if (BUFFER_OOB(next_screen_ptr))
1596 next_screen_ptr = NULL;
1598 #ifdef HAVE_LCD_BITMAP
1599 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1601 if (prefs.need_scrollbar)
1602 viewer_scrollbar();
1603 #else
1604 next_screen_to_draw_ptr = next_screen_ptr;
1605 #endif
1607 #ifdef HAVE_LCD_BITMAP
1608 /* show header */
1609 viewer_show_header();
1611 /* show footer */
1612 viewer_show_footer();
1613 #endif
1615 if (col != -1)
1616 rb->lcd_update();
1619 static void viewer_top(void)
1621 /* Read top of file into buffer
1622 and point screen pointer to top */
1623 if (file_pos != 0)
1625 rb->splash(0, "Loading...");
1627 file_pos = 0;
1628 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1629 fill_buffer(0, buffer, buffer_size);
1632 screen_top_ptr = buffer;
1633 cpage = 1;
1634 cline = 1;
1637 static void viewer_bottom(void)
1639 unsigned char *line_begin;
1640 unsigned char *line_end;
1642 rb->splash(0, "Loading...");
1644 if (last_screen_top_ptr)
1646 cpage = lpage;
1647 cline = 1;
1648 screen_top_ptr = last_screen_top_ptr;
1649 file_pos = last_file_pos;
1650 fill_buffer(file_pos, buffer, buffer_size);
1651 buffer_end = BUFFER_END();
1652 return;
1655 line_end = screen_top_ptr;
1657 while (!BUFFER_EOF() || !BUFFER_OOB(line_end))
1659 get_next_line_position(&line_begin, &line_end, NULL);
1660 if (line_end == NULL)
1661 break;
1663 increment_current_line();
1664 if (cline == 1)
1665 screen_top_ptr = line_end;
1667 lpage = cpage;
1668 cline = 1;
1669 last_screen_top_ptr = screen_top_ptr;
1670 last_file_pos = file_pos;
1671 buffer_end = BUFFER_END();
1674 #ifdef HAVE_LCD_BITMAP
1675 static void init_need_scrollbar(void) {
1676 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1677 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1678 viewer_draw(-1);
1679 prefs.need_scrollbar = NEED_SCROLLBAR();
1680 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1681 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1682 calc_max_width();
1685 static void init_header_and_footer(void)
1687 header_height = 0;
1688 footer_height = 0;
1689 if (rb->global_settings->statusbar == STATUSBAR_TOP)
1691 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1692 header_height = STATUSBAR_HEIGHT;
1694 if (prefs.footer_mode == FT_SBAR)
1695 prefs.footer_mode = FT_NONE;
1696 else if (prefs.footer_mode == FT_BOTH)
1697 prefs.footer_mode = FT_PAGE;
1699 else if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
1701 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1702 footer_height = STATUSBAR_HEIGHT;
1704 if (prefs.header_mode == HD_SBAR)
1705 prefs.header_mode = HD_NONE;
1706 else if (prefs.header_mode == HD_BOTH)
1707 prefs.header_mode = HD_PATH;
1709 else /* STATUSBAR_OFF || STATUSBAR_CUSTOM */
1711 if (prefs.header_mode == HD_SBAR)
1712 prefs.header_mode = HD_NONE;
1713 else if (prefs.header_mode == HD_BOTH)
1714 prefs.header_mode = HD_PATH;
1716 if (prefs.footer_mode == FT_SBAR)
1717 prefs.footer_mode = FT_NONE;
1718 else if (prefs.footer_mode == FT_BOTH)
1719 prefs.footer_mode = FT_PAGE;
1722 if (prefs.header_mode == HD_NONE || prefs.header_mode == HD_PATH ||
1723 prefs.footer_mode == FT_NONE || prefs.footer_mode == FT_PAGE)
1724 rb->gui_syncstatusbar_draw(rb->statusbars, false);
1726 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1727 header_height += pf->height;
1728 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1729 footer_height += pf->height;
1731 display_lines = (LCD_HEIGHT - header_height - footer_height) / pf->height;
1733 lpage = 0;
1734 last_file_pos = 0;
1735 last_screen_top_ptr = NULL;
1738 static bool change_font(unsigned char *font)
1740 unsigned char buf[MAX_PATH];
1742 if (font == NULL || *font == '\0')
1743 return false;
1745 rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font);
1746 if (rb->font_load(NULL, buf) < 0) {
1747 rb->splash(HZ/2, "Font load failed.");
1748 return false;
1751 return true;
1753 #endif
1755 static bool viewer_init(void)
1757 #ifdef HAVE_LCD_BITMAP
1758 /* initialize fonts */
1759 pf = rb->font_get(FONT_UI);
1760 if (pf == NULL)
1761 return false;
1763 draw_columns = display_columns = LCD_WIDTH;
1764 #else
1765 /* REAL fixed pitch :) all chars use up 1 cell */
1766 display_lines = 2;
1767 draw_columns = display_columns = 11;
1768 par_indent_spaces = 2;
1769 #endif
1771 fd = rb->open(file_name, O_RDONLY);
1772 if (fd < 0)
1773 return false;
1775 /* Init mac_text value used in processing buffer */
1776 mac_text = false;
1778 return true;
1781 /* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8,
1782 * then file size decreases only BOM_SIZE.
1784 static void get_filesize(void)
1786 file_size = rb->filesize(fd);
1787 if (file_size == -1)
1788 return;
1790 if (prefs.encoding == UTF_8 && is_bom)
1791 file_size -= BOM_SIZE;
1794 static int bm_comp(const void *a, const void *b)
1796 struct bookmark_info *pa;
1797 struct bookmark_info *pb;
1799 pa = (struct bookmark_info*)a;
1800 pb = (struct bookmark_info*)b;
1802 if (pa->page != pb->page)
1803 return pa->page - pb->page;
1805 return pa->line - pb->line;
1808 static void viewer_add_bookmark(void)
1810 if (bookmark_count >= MAX_BOOKMARKS-1)
1811 return;
1813 bookmarks[bookmark_count].file_position
1814 = file_pos + screen_top_ptr - buffer;
1815 bookmarks[bookmark_count].page = cpage;
1816 bookmarks[bookmark_count].line = cline;
1817 bookmarks[bookmark_count].flag = BOOKMARK_USER;
1818 bookmark_count++;
1821 static int viewer_add_last_read_bookmark(void)
1823 int i;
1825 i = viewer_find_bookmark(cpage, cline);
1826 if (i >= 0)
1827 bookmarks[i].flag |= BOOKMARK_LAST;
1828 else
1830 viewer_add_bookmark();
1831 i = bookmark_count-1;
1832 bookmarks[i].flag = BOOKMARK_LAST;
1834 return i;
1837 static void viewer_remove_bookmark(int i)
1839 int j;
1841 if (i < 0 || i >= bookmark_count)
1842 return;
1844 for (j = i+1; j < bookmark_count; j++)
1845 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1846 sizeof(struct bookmark_info));
1848 bookmark_count--;
1851 static void viewer_remove_last_read_bookmark(void)
1853 int i, j;
1855 for (i = 0; i < bookmark_count; i++)
1857 if (bookmarks[i].flag & BOOKMARK_LAST)
1859 if (bookmarks[i].flag == BOOKMARK_LAST)
1861 for (j = i+1; j < bookmark_count; j++)
1862 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1863 sizeof(struct bookmark_info));
1865 bookmark_count--;
1867 else
1868 bookmarks[i].flag = BOOKMARK_USER;
1869 break;
1874 static int viewer_get_last_read_bookmark(void)
1876 int i;
1878 for (i = 0; i < bookmark_count; i++)
1880 if (bookmarks[i].flag & BOOKMARK_LAST)
1881 return i;
1883 return -1;
1886 static void viewer_select_bookmark(int initval)
1888 int i;
1889 int ipage = 0;
1890 int iline = 0;
1891 int screen_pos;
1892 int screen_top;
1893 int selected = -1;
1895 struct opt_items items[bookmark_count];
1896 unsigned char names[bookmark_count][38];
1898 if (initval >= 0 && initval < bookmark_count)
1900 ipage = bookmarks[initval].page;
1901 iline = bookmarks[initval].line;
1904 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
1905 bm_comp);
1907 for (i = 0; i < bookmark_count; i++)
1909 rb->snprintf(names[i], sizeof(names[0]),
1910 #if CONFIG_KEYPAD != PLAYER_PAD
1911 "%sPage: %d Line: %d",
1912 #else
1913 "%sP:%d L:%d",
1914 #endif
1915 (bookmarks[i].flag&BOOKMARK_LAST)? "*":" ",
1916 bookmarks[i].page,
1917 bookmarks[i].line);
1918 items[i].string = names[i];
1919 items[i].voice_id = -1;
1920 if (selected < 0 && bookmarks[i].page == ipage && bookmarks[i].line == iline)
1921 selected = i;
1924 rb->set_option("Select bookmark", &selected, INT, items,
1925 sizeof(items) / sizeof(items[0]), NULL);
1927 if (selected < 0 || selected >= bookmark_count)
1929 if (initval < 0 || (selected = viewer_get_last_read_bookmark()) < 0)
1931 if (initval < 0)
1932 rb->splash(HZ, "Start the first page.");
1933 file_pos = 0;
1934 screen_top_ptr = buffer;
1935 cpage = 1;
1936 cline = 1;
1937 buffer_end = BUFFER_END();
1938 return;
1942 screen_pos = bookmarks[selected].file_position;
1943 screen_top = screen_pos % buffer_size;
1944 file_pos = screen_pos - screen_top;
1945 screen_top_ptr = buffer + screen_top;
1946 cpage = bookmarks[selected].page;
1947 cline = bookmarks[selected].line;
1948 buffer_end = BUFFER_END();
1951 static void viewer_default_preferences(void)
1953 prefs.word_mode = WRAP;
1954 prefs.line_mode = NORMAL;
1955 prefs.view_mode = NARROW;
1956 prefs.alignment = LEFT;
1957 prefs.scroll_mode = PAGE;
1958 prefs.page_mode = NO_OVERLAP;
1959 prefs.scrollbar_mode = SB_OFF;
1960 rb->memset(prefs.font, 0, MAX_PATH);
1961 #ifdef HAVE_LCD_BITMAP
1962 prefs.header_mode = HD_BOTH;
1963 prefs.footer_mode = FT_BOTH;
1964 rb->snprintf(prefs.font, MAX_PATH, "%s", rb->global_settings->font_file);
1965 #else
1966 prefs.header_mode = HD_NONE;
1967 prefs.footer_mode = FT_NONE;
1968 #endif
1969 prefs.autoscroll_speed = 1;
1970 /* Set codepage to system default */
1971 prefs.encoding = rb->global_settings->default_codepage;
1974 static bool viewer_read_preferences(int pfd, int version, struct preferences *prf)
1976 unsigned char buf[PREFERENCES_SIZE];
1977 unsigned char *p = buf;
1978 int read_size = PREFERENCES_SIZE;
1980 if (version == 0)
1981 read_size--;
1983 if (rb->read(pfd, buf, read_size) != read_size)
1984 return false;
1986 prf->word_mode = *p++;
1987 prf->line_mode = *p++;
1988 prf->view_mode = *p++;
1989 if (version > 0)
1990 prf->alignment = *p++;
1991 else
1992 prf->alignment = LEFT;
1993 prf->encoding = *p++;
1994 prf->scrollbar_mode = *p++;
1995 prf->need_scrollbar = *p++;
1996 prf->page_mode = *p++;
1997 prf->header_mode = *p++;
1998 prf->footer_mode = *p++;
1999 prf->scroll_mode = *p++;
2000 prf->autoscroll_speed = *p++;
2001 rb->memcpy(prf->font, p, MAX_PATH);
2002 return true;
2005 static bool viewer_write_preferences(int pfd, const struct preferences *prf)
2007 unsigned char buf[PREFERENCES_SIZE];
2008 unsigned char *p = buf;
2010 *p++ = prf->word_mode;
2011 *p++ = prf->line_mode;
2012 *p++ = prf->view_mode;
2013 *p++ = prf->alignment;
2014 *p++ = prf->encoding;
2015 *p++ = prf->scrollbar_mode;
2016 *p++ = prf->need_scrollbar;
2017 *p++ = prf->page_mode;
2018 *p++ = prf->header_mode;
2019 *p++ = prf->footer_mode;
2020 *p++ = prf->scroll_mode;
2021 *p++ = prf->autoscroll_speed;
2022 rb->memcpy(p, prf->font, MAX_PATH);
2024 return (rb->write(pfd, buf, sizeof(buf)) == sizeof(buf));
2027 static bool viewer_read_bookmark_info(int bfd, struct bookmark_info *b)
2029 unsigned char buf[BOOKMARK_SIZE];
2031 if (rb->read(bfd, buf, sizeof(buf)) != sizeof(buf))
2032 return false;
2034 b->file_position = (buf[0] << 24)|(buf[1] << 16)|(buf[2] << 8)|buf[3];
2035 b->page = (buf[4] << 8)|buf[5];
2036 b->line = buf[6];
2037 b->flag = buf[7];
2039 return true;
2042 static bool viewer_read_bookmark_infos(int bfd)
2044 unsigned char c;
2045 int i;
2047 if (rb->read(bfd, &c, 1) != 1)
2049 bookmark_count = 0;
2050 return false;
2053 bookmark_count = c;
2054 if (bookmark_count > MAX_BOOKMARKS)
2055 bookmark_count = MAX_BOOKMARKS;
2057 for (i = 0; i < bookmark_count; i++)
2059 if (!viewer_read_bookmark_info(bfd, &bookmarks[i]))
2061 bookmark_count = i;
2062 return false;
2065 return true;
2068 static bool viewer_write_bookmark_info(int bfd, struct bookmark_info *b)
2070 unsigned char buf[BOOKMARK_SIZE];
2071 unsigned char *p = buf;
2072 unsigned long ul;
2074 ul = b->file_position;
2075 *p++ = ul >> 24;
2076 *p++ = ul >> 16;
2077 *p++ = ul >> 8;
2078 *p++ = ul;
2080 ul = b->page;
2081 *p++ = ul >> 8;
2082 *p++ = ul;
2084 *p++ = b->line;
2085 *p = b->flag;
2087 return (rb->write(bfd, buf, sizeof(buf)) == sizeof(buf));
2090 static bool viewer_write_bookmark_infos(int bfd)
2092 unsigned char c = bookmark_count;
2093 int i;
2095 if (rb->write(bfd, &c, 1) != 1)
2096 return false;
2098 for (i = 0; i < bookmark_count; i++)
2100 if (!viewer_write_bookmark_info(bfd, &bookmarks[i]))
2101 return false;
2104 return true;
2107 static bool viewer_load_global_settings(void)
2109 unsigned buf[GLOBAL_SETTINGS_H_SIZE];
2110 int sfd = rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY);
2111 int version;
2112 bool res = false;
2114 if (sfd < 0)
2115 return false;
2117 if ((rb->read(sfd, buf, GLOBAL_SETTINGS_H_SIZE) == GLOBAL_SETTINGS_H_SIZE) ||
2118 (rb->memcmp(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1) == 0))
2120 version = buf[GLOBAL_SETTINGS_H_SIZE - 1] - GLOBAL_SETTINGS_FIRST_VERSION;
2121 res = viewer_read_preferences(sfd, version, &prefs);
2123 rb->close(sfd);
2124 return res;
2127 static bool viewer_save_global_settings(void)
2129 int sfd = rb->open(GLOBAL_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC);
2130 unsigned char buf[GLOBAL_SETTINGS_H_SIZE];
2132 if (sfd < 0)
2133 return false;
2135 rb->memcpy(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1);
2136 buf[GLOBAL_SETTINGS_H_SIZE - 1] = GLOBAL_SETTINGS_VERSION;
2138 if (rb->write(sfd, buf, GLOBAL_SETTINGS_H_SIZE) != GLOBAL_SETTINGS_H_SIZE ||
2139 !viewer_write_preferences(sfd, &prefs))
2141 rb->close(sfd);
2142 rb->remove(GLOBAL_SETTINGS_TMP_FILE);
2143 return false;
2145 rb->close(sfd);
2146 rb->remove(GLOBAL_SETTINGS_FILE);
2147 rb->rename(GLOBAL_SETTINGS_TMP_FILE, GLOBAL_SETTINGS_FILE);
2148 return true;
2151 static bool viewer_convert_settings(int sfd, int dfd, int old_ver)
2153 struct preferences new_prefs;
2154 off_t old_pos;
2155 off_t new_pos;
2156 unsigned char buf[MAX_PATH + 2];
2157 int settings_size;
2159 rb->read(sfd, buf, MAX_PATH + 2);
2160 rb->write(dfd, buf, MAX_PATH + 2);
2162 settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
2164 old_pos = rb->lseek(sfd, 0, SEEK_CUR);
2165 new_pos = rb->lseek(dfd, 0, SEEK_CUR);
2168 * when the settings size != preferences size + bookmarks size,
2169 * settings data are considered to be old version.
2171 if (old_ver > 0 && ((settings_size - PREFERENCES_SIZE) % 8) == 0)
2172 old_ver = 0;
2174 if (!viewer_read_preferences(sfd, old_ver, &new_prefs))
2175 return false;
2177 if (!viewer_write_preferences(dfd, &new_prefs))
2178 return false;
2180 settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos);
2182 if (settings_size > 0)
2184 rb->read(sfd, buf, settings_size);
2185 rb->write(dfd, buf, settings_size);
2188 settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos;
2189 buf[0] = settings_size >> 8;
2190 buf[1] = settings_size;
2191 rb->lseek(dfd, new_pos - 2, SEEK_SET);
2192 rb->write(dfd, buf, 2);
2193 rb->lseek(dfd, settings_size, SEEK_CUR);
2194 return true;
2197 static bool viewer_convert_settings_file(void)
2199 unsigned char buf[SETTINGS_H_SIZE+2];
2200 int sfd;
2201 int tfd;
2202 int i;
2203 int fcount;
2204 int version;
2205 bool res = true;
2207 if ((sfd = rb->open(SETTINGS_FILE, O_RDONLY)) < 0)
2208 return false;
2210 if ((tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
2212 rb->close(sfd);
2213 return false;
2216 rb->read(sfd, buf, SETTINGS_H_SIZE + 2);
2218 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2219 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE + 1];
2220 buf[SETTINGS_H_SIZE - 1] = SETTINGS_VERSION;
2222 rb->write(tfd, buf, SETTINGS_H_SIZE + 2);
2224 for (i = 0; i < fcount; i++)
2226 if (!viewer_convert_settings(sfd, tfd, version))
2228 res = false;
2229 break;
2233 rb->close(sfd);
2234 rb->close(tfd);
2236 if (!res)
2237 rb->remove(SETTINGS_TMP_FILE);
2238 else
2240 rb->remove(SETTINGS_FILE);
2241 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2243 return res;
2246 static bool viewer_load_settings(void)
2248 unsigned char buf[MAX_PATH+2];
2249 unsigned int fcount;
2250 unsigned int i;
2251 bool res = false;
2252 int sfd;
2253 unsigned int size;
2254 int version;
2256 sfd = rb->open(SETTINGS_FILE, O_RDONLY);
2257 if (sfd < 0)
2258 goto read_end;
2260 if ((rb->read(sfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2261 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2263 /* illegal setting file */
2264 rb->close(sfd);
2266 if (rb->file_exists(SETTINGS_FILE))
2267 rb->remove(SETTINGS_FILE);
2269 goto read_end;
2272 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2274 rb->close(sfd);
2275 if (!viewer_convert_settings_file())
2276 goto read_end;
2278 return viewer_load_settings();
2281 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2282 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2283 for (i = 0; i < fcount; i++)
2285 if (rb->read(sfd, buf, MAX_PATH+2) != MAX_PATH+2)
2286 break;
2288 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2291 * when the settings size != preferences size + bookmarks size,
2292 * the settings file converts to the newer.
2294 if (version > 0 && ((size - PREFERENCES_SIZE) % 8) == 0)
2296 rb->close(sfd);
2297 if (!viewer_convert_settings_file())
2298 break;
2300 return viewer_load_settings();
2303 if (rb->strcmp(buf, file_name))
2305 if (rb->lseek(sfd, size, SEEK_CUR) < 0)
2306 break;
2307 continue;
2309 if (!viewer_read_preferences(sfd, version, &prefs))
2310 break;
2312 res = viewer_read_bookmark_infos(sfd);
2313 break;
2316 rb->close(sfd);
2318 read_end:
2319 if (!res)
2321 /* load global settings */
2322 if (!viewer_load_global_settings())
2323 viewer_default_preferences();
2325 file_pos = 0;
2326 screen_top_ptr = buffer;
2327 cpage = 1;
2328 cline = 1;
2329 bookmark_count = 0;
2332 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
2333 calc_max_width();
2335 if (bookmark_count > 1)
2336 viewer_select_bookmark(-1);
2337 else if (bookmark_count == 1)
2339 int screen_pos;
2340 int screen_top;
2342 screen_pos = bookmarks[0].file_position;
2343 screen_top = screen_pos % buffer_size;
2344 file_pos = screen_pos - screen_top;
2345 screen_top_ptr = buffer + screen_top;
2346 cpage = bookmarks[0].page;
2347 cline = bookmarks[0].line;
2350 viewer_remove_last_read_bookmark();
2352 check_bom();
2353 get_filesize();
2355 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
2357 if (BUFFER_OOB(screen_top_ptr))
2358 screen_top_ptr = buffer;
2360 fill_buffer(file_pos, buffer, buffer_size);
2361 if (prefs.scroll_mode == PAGE && cline > 1)
2362 viewer_scroll_to_top_line();
2364 /* remember the current position */
2365 start_position = file_pos + screen_top_ptr - buffer;
2367 #ifdef HAVE_LCD_BITMAP
2368 /* load prefs font if it is different than the global settings font */
2369 if (rb->strcmp(prefs.font, rb->global_settings->font_file)) {
2370 if (!change_font(prefs.font)) {
2371 /* fallback by resetting prefs font to the global settings font */
2372 rb->memset(prefs.font, 0, MAX_PATH);
2373 rb->snprintf(prefs.font, MAX_PATH, "%s",
2374 rb->global_settings->font_file);
2376 if (!change_font(prefs.font))
2377 return false;
2381 init_need_scrollbar();
2382 init_header_and_footer();
2383 #endif
2385 return true;
2388 static bool copy_bookmark_file(int sfd, int dfd, off_t start, off_t size)
2390 off_t rsize;
2392 if (rb->lseek(sfd, start, SEEK_SET) < 0)
2393 return false;
2395 while (size > 0)
2397 if (size > buffer_size)
2398 rsize = buffer_size;
2399 else
2400 rsize = size;
2401 size -= rsize;
2403 if (rb->read(sfd, buffer, rsize) != rsize ||
2404 rb->write(dfd, buffer, rsize) != rsize)
2405 return false;
2407 return true;
2410 static bool viewer_save_settings(void)
2412 unsigned char buf[MAX_PATH+2];
2413 unsigned int fcount = 0;
2414 unsigned int i;
2415 int idx;
2416 int ofd;
2417 int tfd;
2418 off_t first_copy_size = 0;
2419 off_t second_copy_start_pos = 0;
2420 off_t size;
2422 /* add reading page to bookmarks */
2423 idx = viewer_find_bookmark(cpage, cline);
2424 if (idx >= 0)
2425 bookmarks[idx].flag |= BOOKMARK_LAST;
2426 else
2428 viewer_add_bookmark();
2429 bookmarks[bookmark_count-1].flag = BOOKMARK_LAST;
2432 tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC);
2433 if (tfd < 0)
2434 return false;
2436 ofd = rb->open(SETTINGS_FILE, O_RDWR);
2437 if (ofd >= 0)
2439 if ((rb->read(ofd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2440 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2442 rb->close(ofd);
2443 goto save_err;
2446 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2448 rb->close(ofd);
2449 if (!viewer_convert_settings_file())
2450 goto save_err;
2452 viewer_save_settings();
2455 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2457 for (i = 0; i < fcount; i++)
2459 if (rb->read(ofd, buf, MAX_PATH+2) != MAX_PATH+2)
2461 rb->close(ofd);
2462 goto save_err;
2464 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2465 if (rb->strcmp(buf, file_name))
2467 if (rb->lseek(ofd, size, SEEK_CUR) < 0)
2469 rb->close(ofd);
2470 goto save_err;
2473 else
2475 first_copy_size = rb->lseek(ofd, 0, SEEK_CUR);
2476 if (first_copy_size < 0)
2478 rb->close(ofd);
2479 goto save_err;
2481 second_copy_start_pos = first_copy_size + size;
2482 first_copy_size -= MAX_PATH+2;
2483 fcount--;
2484 break;
2487 if (first_copy_size == 0)
2488 first_copy_size = rb->filesize(ofd);
2490 if (!copy_bookmark_file(ofd, tfd, 0, first_copy_size))
2492 rb->close(ofd);
2493 goto save_err;
2495 if (second_copy_start_pos > 0)
2497 if (!copy_bookmark_file(ofd, tfd, second_copy_start_pos,
2498 rb->filesize(ofd) - second_copy_start_pos))
2500 rb->close(ofd);
2501 goto save_err;
2504 rb->close(ofd);
2506 else
2508 rb->memcpy(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1);
2509 buf[SETTINGS_H_SIZE-1] = SETTINGS_VERSION;
2510 buf[SETTINGS_H_SIZE ] = 0;
2511 buf[SETTINGS_H_SIZE+1] = 0;
2512 if (rb->write(tfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2)
2513 goto save_err;
2516 /* copy to current read file's bookmarks */
2517 rb->memset(buf, 0, MAX_PATH);
2518 rb->snprintf(buf, MAX_PATH, "%s", file_name);
2520 size = PREFERENCES_SIZE + bookmark_count * BOOKMARK_SIZE + 1;
2521 buf[MAX_PATH] = size >> 8;
2522 buf[MAX_PATH+1] = size;
2524 if (rb->write(tfd, buf, MAX_PATH+2) != MAX_PATH+2)
2525 goto save_err;
2527 if (!viewer_write_preferences(tfd, &prefs))
2528 goto save_err;
2530 if (!viewer_write_bookmark_infos(tfd))
2531 goto save_err;
2533 if (rb->lseek(tfd, SETTINGS_H_SIZE, SEEK_SET) < 0)
2534 goto save_err;
2536 fcount++;
2537 buf[0] = fcount >> 8;
2538 buf[1] = fcount;
2540 if (rb->write(tfd, buf, 2) != 2)
2541 goto save_err;
2543 rb->close(tfd);
2545 rb->remove(SETTINGS_FILE);
2546 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2548 return true;
2550 save_err:
2551 rb->close(tfd);
2552 rb->remove(SETTINGS_TMP_FILE);
2553 return false;
2556 static void viewer_exit(void *parameter)
2558 (void)parameter;
2560 /* save preference and bookmarks */
2561 if (!viewer_save_settings())
2562 rb->splash(HZ, "Can't save preference and bookmarks.");
2564 rb->close(fd);
2565 #ifdef HAVE_LCD_BITMAP
2566 if (rb->strcmp(prefs.font, rb->global_settings->font_file))
2567 change_font(rb->global_settings->font_file);
2568 #endif
2571 static void calc_page(void)
2573 int i;
2574 unsigned char *line_begin;
2575 unsigned char *line_end;
2576 off_t sfp;
2577 unsigned char *sstp;
2579 rb->splash(0, "Calculating page/line number...");
2581 /* add reading page to bookmarks */
2582 viewer_add_last_read_bookmark();
2584 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
2585 bm_comp);
2587 cpage = 1;
2588 cline = 1;
2589 file_pos = 0;
2590 screen_top_ptr = buffer;
2591 buffer_end = BUFFER_END();
2593 fill_buffer(file_pos, buffer, buffer_size);
2594 line_end = line_begin = buffer;
2596 for (i = 0; i < bookmark_count; i++)
2598 sfp = bookmarks[i].file_position;
2599 sstp = buffer;
2601 while ((line_begin > sstp || sstp >= line_end) ||
2602 (file_pos > sfp || sfp >= file_pos + BUFFER_END() - buffer))
2604 get_next_line_position(&line_begin, &line_end, NULL);
2605 if (line_end == NULL)
2606 break;
2608 next_line_ptr = line_end;
2610 if (sstp == buffer &&
2611 file_pos <= sfp && sfp < file_pos + BUFFER_END() - buffer)
2612 sstp = sfp - file_pos + buffer;
2614 increment_current_line();
2617 decrement_current_line();
2618 bookmarks[i].page = cpage;
2619 bookmarks[i].line = cline;
2620 bookmarks[i].file_position = file_pos + (line_begin - buffer);
2621 increment_current_line();
2624 /* remove reading page's bookmark */
2625 for (i = 0; i < bookmark_count; i++)
2627 if (bookmarks[i].flag & BOOKMARK_LAST)
2629 int screen_pos;
2630 int screen_top;
2632 screen_pos = bookmarks[i].file_position;
2633 screen_top = screen_pos % buffer_size;
2634 file_pos = screen_pos - screen_top;
2635 screen_top_ptr = buffer + screen_top;
2637 cpage = bookmarks[i].page;
2638 cline = bookmarks[i].line;
2639 bookmarks[i].flag ^= BOOKMARK_LAST;
2640 buffer_end = BUFFER_END();
2642 fill_buffer(file_pos, buffer, buffer_size);
2644 if (bookmarks[i].flag == 0)
2645 viewer_remove_bookmark(i);
2647 if (prefs.scroll_mode == PAGE && cline > 1)
2648 viewer_scroll_to_top_line();
2649 break;
2654 static int col_limit(int col)
2656 if (col < 0)
2657 col = 0;
2658 else
2659 if (col >= max_width - draw_columns)
2660 col = max_width - draw_columns;
2662 return col;
2665 /* settings helper functions */
2667 static bool encoding_setting(void)
2669 static struct opt_items names[NUM_CODEPAGES];
2670 int idx;
2671 bool res;
2672 enum codepages oldenc = prefs.encoding;
2674 for (idx = 0; idx < NUM_CODEPAGES; idx++)
2676 names[idx].string = rb->get_codepage_name(idx);
2677 names[idx].voice_id = -1;
2680 res = rb->set_option("Encoding", &prefs.encoding, INT, names,
2681 sizeof(names) / sizeof(names[0]), NULL);
2683 /* When prefs.encoding changes into UTF-8 or changes from UTF-8,
2684 * filesize (file_size) might change.
2685 * In addition, if prefs.encoding is UTF-8, then BOM does not read.
2687 if (oldenc != prefs.encoding && (oldenc == UTF_8 || prefs.encoding == UTF_8))
2689 check_bom();
2690 get_filesize();
2691 fill_buffer(file_pos, buffer, buffer_size);
2694 return res;
2697 static bool word_wrap_setting(void)
2699 static const struct opt_items names[] = {
2700 {"On", -1},
2701 {"Off (Chop Words)", -1},
2704 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
2705 names, 2, NULL);
2708 static bool line_mode_setting(void)
2710 static const struct opt_items names[] = {
2711 {"Normal", -1},
2712 {"Join Lines", -1},
2713 {"Expand Lines", -1},
2714 #ifdef HAVE_LCD_BITMAP
2715 {"Reflow Lines", -1},
2716 #endif
2719 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
2720 sizeof(names) / sizeof(names[0]), NULL);
2723 static bool view_mode_setting(void)
2725 static const struct opt_items names[] = {
2726 {"No (Narrow)", -1},
2727 {"Yes", -1},
2729 bool ret;
2730 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
2731 names , 2, NULL);
2732 if (prefs.view_mode == NARROW)
2733 col = 0;
2734 calc_max_width();
2735 return ret;
2738 static bool scroll_mode_setting(void)
2740 static const struct opt_items names[] = {
2741 {"Scroll by Page", -1},
2742 {"Scroll by Line", -1},
2745 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
2746 names, 2, NULL);
2749 #ifdef HAVE_LCD_BITMAP
2750 static bool page_mode_setting(void)
2752 static const struct opt_items names[] = {
2753 {"No", -1},
2754 {"Yes", -1},
2757 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
2758 names, 2, NULL);
2761 static bool scrollbar_setting(void)
2763 static const struct opt_items names[] = {
2764 {"Off", -1},
2765 {"On", -1}
2768 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
2769 names, 2, NULL);
2772 static bool header_setting(void)
2774 int len = (rb->global_settings->statusbar == STATUSBAR_TOP)? 4 : 2;
2775 struct opt_items names[len];
2777 names[0].string = "None";
2778 names[0].voice_id = -1;
2779 names[1].string = "File path";
2780 names[1].voice_id = -1;
2782 if (rb->global_settings->statusbar == STATUSBAR_TOP)
2784 names[2].string = "Status bar";
2785 names[2].voice_id = -1;
2786 names[3].string = "Both";
2787 names[3].voice_id = -1;
2790 return rb->set_option("Show Header", &prefs.header_mode, INT,
2791 names, len, NULL);
2794 static bool footer_setting(void)
2796 int len = (rb->global_settings->statusbar == STATUSBAR_BOTTOM)? 4 : 2;
2797 struct opt_items names[len];
2799 names[0].string = "None";
2800 names[0].voice_id = -1;
2801 names[1].string = "Page Num";
2802 names[1].voice_id = -1;
2804 if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
2806 names[2].string = "Status bar";
2807 names[2].voice_id = -1;
2808 names[3].string = "Both";
2809 names[3].voice_id = -1;
2812 return rb->set_option("Show Footer", &prefs.footer_mode, INT,
2813 names, len, NULL);
2816 static int font_comp(const void *a, const void *b)
2818 struct opt_items *pa;
2819 struct opt_items *pb;
2821 pa = (struct opt_items *)a;
2822 pb = (struct opt_items *)b;
2824 return rb->strcmp(pa->string, pb->string);
2827 static bool font_setting(void)
2829 int count = 0;
2830 DIR *dir;
2831 struct dirent *entry;
2832 int i = 0;
2833 int len;
2834 int new_font = 0;
2835 int old_font;
2836 bool res;
2837 int size = 0;
2839 dir = rb->opendir(FONT_DIR);
2840 if (!dir)
2842 rb->splash(HZ/2, "Font dir is not accessible");
2843 return false;
2846 while (1)
2848 entry = rb->readdir(dir);
2850 if (entry == NULL)
2851 break;
2853 len = rb->strlen(entry->d_name);
2854 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2855 continue;
2856 size += len-3;
2857 count++;
2859 rb->closedir(dir);
2861 struct opt_items names[count];
2862 unsigned char font_names[size];
2863 unsigned char *p = font_names;
2865 dir = rb->opendir(FONT_DIR);
2866 if (!dir)
2868 rb->splash(HZ/2, "Font dir is not accessible");
2869 return false;
2872 while (1)
2874 entry = rb->readdir(dir);
2876 if (entry == NULL)
2877 break;
2879 len = rb->strlen(entry->d_name);
2880 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2881 continue;
2883 rb->snprintf(p, len-3, "%s", entry->d_name);
2884 names[i].string = p;
2885 names[i].voice_id = -1;
2886 p += len-3;
2887 i++;
2888 if (i >= count)
2889 break;
2891 rb->closedir(dir);
2893 rb->qsort(names, count, sizeof(struct opt_items), font_comp);
2895 for (i = 0; i < count; i++)
2897 if (!rb->strcmp(names[i].string, prefs.font))
2899 new_font = i;
2900 break;
2903 old_font = new_font;
2905 res = rb->set_option("Select Font", &new_font, INT,
2906 names, count, NULL);
2908 if (new_font != old_font)
2910 /* load selected font */
2911 if (!change_font((unsigned char *)names[new_font].string)) {
2912 /* revert by re-loading the preferences font */
2913 change_font(prefs.font);
2914 return false;
2916 rb->memset(prefs.font, 0, MAX_PATH);
2917 rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string);
2920 return res;
2923 static bool alignment_setting(void)
2925 static const struct opt_items names[] = {
2926 {"Left", -1},
2927 {"Right", -1},
2930 return rb->set_option("Alignment", &prefs.alignment, INT,
2931 names , 2, NULL);
2933 #endif
2935 static bool autoscroll_speed_setting(void)
2937 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
2938 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
2941 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
2942 NULL, NULL, Icon_NOICON);
2943 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
2944 NULL, NULL, Icon_NOICON);
2945 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
2946 NULL, NULL, Icon_NOICON);
2947 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
2948 NULL, NULL, Icon_NOICON);
2949 #ifdef HAVE_LCD_BITMAP
2950 MENUITEM_FUNCTION(alignment_item, 0, "Alignment", alignment_setting,
2951 NULL, NULL, Icon_NOICON);
2952 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
2953 NULL, NULL, Icon_NOICON);
2954 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
2955 NULL, NULL, Icon_NOICON);
2956 MENUITEM_FUNCTION(header_item, 0, "Show Header", header_setting,
2957 NULL, NULL, Icon_NOICON);
2958 MENUITEM_FUNCTION(footer_item, 0, "Show Footer", footer_setting,
2959 NULL, NULL, Icon_NOICON);
2960 MENUITEM_FUNCTION(font_item, 0, "Font", font_setting,
2961 NULL, NULL, Icon_NOICON);
2962 #endif
2963 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
2964 NULL, NULL, Icon_NOICON);
2965 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
2966 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
2967 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
2968 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
2969 #ifdef HAVE_LCD_BITMAP
2970 &alignment_item, &scrollbar_item, &page_mode_item, &header_item,
2971 &footer_item, &font_item,
2972 #endif
2973 &scroll_mode_item, &autoscroll_speed_item);
2975 static bool viewer_options_menu(bool is_global)
2977 bool result;
2978 struct preferences tmp_prefs;
2980 rb->memcpy(&tmp_prefs, &prefs, sizeof(struct preferences));
2982 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
2984 if (!is_global && rb->memcmp(&tmp_prefs, &prefs, sizeof(struct preferences)))
2986 /* Show-scrollbar mode for current view-width mode */
2987 #ifdef HAVE_LCD_BITMAP
2988 init_need_scrollbar();
2989 init_header_and_footer();
2990 #endif
2991 calc_page();
2993 return result;
2996 static void viewer_menu(void)
2998 int result;
3000 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
3001 "Return", "Viewer Options",
3002 "Show Playback Menu", "Select Bookmark",
3003 "Global Settings", "Quit");
3005 result = rb->do_menu(&menu, NULL, NULL, false);
3006 switch (result)
3008 case 0: /* return */
3009 break;
3010 case 1: /* change settings */
3011 done = viewer_options_menu(false);
3012 break;
3013 case 2: /* playback control */
3014 playback_control(NULL);
3015 break;
3016 case 3: /* select bookmark */
3017 viewer_select_bookmark(viewer_add_last_read_bookmark());
3018 viewer_remove_last_read_bookmark();
3019 fill_buffer(file_pos, buffer, buffer_size);
3020 if (prefs.scroll_mode == PAGE && cline > 1)
3021 viewer_scroll_to_top_line();
3022 break;
3023 case 4: /* change global settings */
3025 struct preferences orig_prefs;
3027 rb->memcpy(&orig_prefs, &prefs, sizeof(struct preferences));
3028 if (!viewer_load_global_settings())
3029 viewer_default_preferences();
3030 done = viewer_options_menu(true);
3031 viewer_save_global_settings();
3032 rb->memcpy(&prefs, &orig_prefs, sizeof(struct preferences));
3034 break;
3035 case 5: /* quit */
3036 viewer_exit(NULL);
3037 done = true;
3038 break;
3040 viewer_draw(col);
3043 enum plugin_status plugin_start(const void* file)
3045 int button, i, ok;
3046 int lastbutton = BUTTON_NONE;
3047 bool autoscroll = false;
3048 long old_tick;
3050 old_tick = *rb->current_tick;
3052 /* get the plugin buffer */
3053 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
3054 if (buffer_size == 0)
3056 rb->splash(HZ, "buffer does not allocate !!");
3057 return PLUGIN_ERROR;
3059 block_size = buffer_size / 3;
3060 buffer_size = 3 * block_size;
3062 if (!file)
3063 return PLUGIN_ERROR;
3065 file_name = file;
3066 ok = viewer_init();
3067 if (!ok) {
3068 rb->splash(HZ, "Error opening file.");
3069 return PLUGIN_ERROR;
3072 if (!viewer_load_settings()) /* load the preferences and bookmark */
3073 return PLUGIN_ERROR;
3075 #if LCD_DEPTH > 1
3076 rb->lcd_set_backdrop(NULL);
3077 #endif
3079 viewer_draw(col);
3081 while (!done) {
3083 if(autoscroll)
3085 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
3087 viewer_scroll_down(true);
3088 viewer_draw(col);
3089 old_tick = *rb->current_tick;
3093 button = rb->button_get_w_tmo(HZ/10);
3095 if (prefs.view_mode != WIDE) {
3096 /* when not in wide view mode, the SCREEN_LEFT and SCREEN_RIGHT
3097 buttons jump to the beginning and end of the file. To stop
3098 users doing this by accident, replace non-held occurrences
3099 with page up/down instead. */
3100 if (button == VIEWER_SCREEN_LEFT)
3101 button = VIEWER_PAGE_UP;
3102 else if (button == VIEWER_SCREEN_RIGHT)
3103 button = VIEWER_PAGE_DOWN;
3106 switch (button) {
3107 case VIEWER_MENU:
3108 #ifdef VIEWER_MENU2
3109 case VIEWER_MENU2:
3110 #endif
3111 viewer_menu();
3112 break;
3114 case VIEWER_AUTOSCROLL:
3115 #ifdef VIEWER_AUTOSCROLL_PRE
3116 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
3117 break;
3118 #endif
3119 autoscroll = !autoscroll;
3120 break;
3122 case VIEWER_PAGE_UP:
3123 case VIEWER_PAGE_UP | BUTTON_REPEAT:
3124 #ifdef VIEWER_PAGE_UP2
3125 case VIEWER_PAGE_UP2:
3126 case VIEWER_PAGE_UP2 | BUTTON_REPEAT:
3127 #endif
3128 if (prefs.scroll_mode == PAGE)
3130 /* Page up */
3131 #ifdef HAVE_LCD_BITMAP
3132 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
3133 #else
3134 for (i = 0; i < display_lines; i++)
3135 #endif
3136 viewer_scroll_up();
3138 else
3139 viewer_scroll_up();
3140 old_tick = *rb->current_tick;
3141 viewer_draw(col);
3142 break;
3144 case VIEWER_PAGE_DOWN:
3145 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
3146 #ifdef VIEWER_PAGE_DOWN2
3147 case VIEWER_PAGE_DOWN2:
3148 case VIEWER_PAGE_DOWN2 | BUTTON_REPEAT:
3149 #endif
3150 if (prefs.scroll_mode == PAGE)
3152 /* Page down */
3153 if (next_screen_ptr != NULL)
3155 screen_top_ptr = next_screen_to_draw_ptr;
3156 if (cpage < MAX_PAGE)
3157 cpage++;
3160 else
3161 viewer_scroll_down(autoscroll);
3162 old_tick = *rb->current_tick;
3163 viewer_draw(col);
3164 break;
3166 case VIEWER_SCREEN_LEFT:
3167 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
3168 if (prefs.view_mode == WIDE) {
3169 /* Screen left */
3170 col = col_limit(col - draw_columns);
3172 else { /* prefs.view_mode == NARROW */
3173 /* Top of file */
3174 viewer_top();
3177 viewer_draw(col);
3178 break;
3180 case VIEWER_SCREEN_RIGHT:
3181 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
3182 if (prefs.view_mode == WIDE) {
3183 /* Screen right */
3184 col = col_limit(col + draw_columns);
3186 else { /* prefs.view_mode == NARROW */
3187 /* Bottom of file */
3188 viewer_bottom();
3191 viewer_draw(col);
3192 break;
3194 #ifdef VIEWER_LINE_UP
3195 case VIEWER_LINE_UP:
3196 case VIEWER_LINE_UP | BUTTON_REPEAT:
3197 /* Scroll up one line */
3198 viewer_scroll_up();
3199 old_tick = *rb->current_tick;
3200 viewer_draw(col);
3201 break;
3203 case VIEWER_LINE_DOWN:
3204 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
3205 /* Scroll down one line */
3206 viewer_scroll_down(autoscroll);
3207 increment_current_line();
3208 old_tick = *rb->current_tick;
3209 viewer_draw(col);
3210 break;
3211 #endif
3212 #ifdef VIEWER_COLUMN_LEFT
3213 case VIEWER_COLUMN_LEFT:
3214 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
3215 if (prefs.view_mode == WIDE) {
3216 /* Scroll left one column */
3217 col = col_limit(col - glyph_width('o'));
3218 viewer_draw(col);
3220 break;
3222 case VIEWER_COLUMN_RIGHT:
3223 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
3224 if (prefs.view_mode == WIDE) {
3225 /* Scroll right one column */
3226 col = col_limit(col + glyph_width('o'));
3227 viewer_draw(col);
3229 break;
3230 #endif
3232 #ifdef VIEWER_RC_QUIT
3233 case VIEWER_RC_QUIT:
3234 #endif
3235 case VIEWER_QUIT:
3236 #ifdef VIEWER_QUIT2
3237 case VIEWER_QUIT2:
3238 #endif
3239 viewer_exit(NULL);
3240 done = true;
3241 break;
3243 case VIEWER_BOOKMARK:
3245 int idx = viewer_find_bookmark(cpage, cline);
3247 if (idx < 0)
3249 if (bookmark_count >= MAX_BOOKMARKS-1)
3250 rb->splash(HZ/2, "No more add bookmark.");
3251 else
3253 viewer_add_bookmark();
3254 rb->splash(HZ/2, "Bookmark add.");
3257 else
3259 viewer_remove_bookmark(idx);
3260 rb->splash(HZ/2, "Bookmark remove.");
3262 viewer_draw(col);
3264 break;
3266 default:
3267 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
3268 == SYS_USB_CONNECTED)
3269 return PLUGIN_USB_CONNECTED;
3270 break;
3272 if (button != BUTTON_NONE)
3274 lastbutton = button;
3275 rb->yield();
3278 return PLUGIN_OK;