fix FS#11264 - frequency bar and presets not working in the sbs
[kugel-rb.git] / apps / plugins / viewer.c
blobf000628ca37539a9b3eed0a4ae2abc7da98b378e
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 #elif CONFIG_KEYPAD == MPIO_HD200_PAD
490 #define VIEWER_QUIT (BUTTON_REC | BUTTON_PLAY)
491 #define VIEWER_PAGE_UP BUTTON_PREV
492 #define VIEWER_PAGE_DOWN BUTTON_NEXT
493 #define VIEWER_SCREEN_LEFT BUTTON_VOL_DOWN
494 #define VIEWER_SCREEN_RIGHT BUTTON_VOL_UP
495 #define VIEWER_MENU BUTTON_SELECT
496 #define VIEWER_AUTOSCROLL BUTTON_PLAY
497 #define VIEWER_BOOKMARK BUTTON_REC
499 #else
500 #error No keymap defined!
501 #endif
503 #ifdef HAVE_TOUCHSCREEN
504 #ifdef VIEWER_QUIT
505 #define VIEWER_QUIT2 BUTTON_TOPLEFT
506 #else
507 #define VIEWER_QUIT BUTTON_TOPLEFT
508 #endif
509 #ifdef VIEWER_PAGE_UP
510 #define VIEWER_PAGE_UP2 BUTTON_TOPMIDDLE
511 #else
512 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
513 #endif
514 #ifdef VIEWER_PAGE_DOWN
515 #define VIEWER_PAGE_DOWN2 BUTTON_BOTTOMMIDDLE
516 #else
517 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
518 #endif
519 #ifndef VIEWER_SCREEN_LEFT
520 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
521 #endif
522 #ifndef VIEWER_SCREEN_RIGHT
523 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
524 #endif
525 #ifdef VIEWER_MENU
526 #define VIEWER_MENU2 BUTTON_TOPRIGHT
527 #else
528 #define VIEWER_MENU BUTTON_TOPRIGHT
529 #endif
530 #ifndef VIEWER_AUTOSCROLL
531 #define VIEWER_AUTOSCROLL BUTTON_CENTER
532 #endif
533 #endif
535 /* stuff for the bookmarking */
536 struct bookmark_info {
537 long file_position;
538 int page;
539 int line;
540 unsigned char flag;
543 struct preferences {
544 enum {
545 WRAP=0,
546 CHOP,
547 } word_mode;
549 enum {
550 NORMAL=0,
551 JOIN,
552 EXPAND,
553 REFLOW, /* won't be set on charcell LCD, must be last */
554 } line_mode;
556 enum {
557 NARROW=0,
558 WIDE,
559 } view_mode;
561 enum {
562 LEFT=0,
563 RIGHT,
564 } alignment;
566 enum codepages encoding;
568 enum {
569 SB_OFF=0,
570 SB_ON,
571 } scrollbar_mode;
572 bool need_scrollbar;
574 enum {
575 NO_OVERLAP=0,
576 OVERLAP,
577 } page_mode;
579 enum {
580 HD_NONE = 0,
581 HD_PATH,
582 HD_SBAR,
583 HD_BOTH,
584 } header_mode;
586 enum {
587 FT_NONE = 0,
588 FT_PAGE,
589 FT_SBAR,
590 FT_BOTH,
591 } footer_mode;
593 enum {
594 PAGE=0,
595 LINE,
596 } scroll_mode;
598 int autoscroll_speed;
600 unsigned char font[MAX_PATH];
603 enum {
604 VIEWER_FONT_MENU = 0,
605 VIEWER_FONT_TEXT,
608 struct preferences prefs;
609 struct preferences old_prefs;
611 static unsigned char *buffer;
612 static long buffer_size;
613 static long block_size = 0x1000;
614 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
615 static int display_columns; /* number of (pixel) columns on the display */
616 static int display_lines; /* number of lines on the display */
617 static int draw_columns; /* number of (pixel) columns available for text */
618 static int par_indent_spaces; /* number of spaces to indent first paragraph */
619 static int fd;
620 static const char *file_name;
621 static long file_size;
622 static long start_position; /* position in the file after the viewer is started */
623 static bool mac_text;
624 static long file_pos; /* Position of the top of the buffer in the file */
625 static long last_file_pos;
626 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
627 static int max_width;
628 static int max_columns;
629 static int cline = 1;
630 static int cpage = 1;
631 static int lpage = 0;
632 static unsigned char *screen_top_ptr;
633 static unsigned char *next_screen_ptr;
634 static unsigned char *next_screen_to_draw_ptr;
635 static unsigned char *next_line_ptr;
636 static unsigned char *last_screen_top_ptr = NULL;
637 #ifdef HAVE_LCD_BITMAP
638 static struct font *pf;
639 static int header_height = 0;
640 static int footer_height = 0;
641 #endif
642 struct bookmark_info bookmarks[MAX_BOOKMARKS];
643 static int bookmark_count;
645 /* UTF-8 BOM */
646 #define BOM "\xef\xbb\xbf"
647 #define BOM_SIZE 3
649 static bool is_bom = false;
651 /* We draw a diacritic char over a non-diacritic one. Therefore, such chars are
652 * not considered to occupy space, therefore buffers might have more than
653 * max_columns characters. The DIACRITIC_FACTOR is the max ratio between all
654 * characters and non-diacritic characters in the buffer
656 #define DIACRITIC_FACTOR 2
658 /* calculate the width of a UCS character (zero width for diacritics) */
659 static int glyph_width(unsigned short ch)
661 if (ch == 0)
662 ch = ' ';
664 #ifdef HAVE_LCD_BITMAP
665 if (rb->is_diacritic(ch, NULL))
666 return 0;
668 return rb->font_get_width(pf, ch);
669 #else
670 return 1;
671 #endif
674 /* get UCS character from string */
675 static unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
677 unsigned char utf8_tmp[6];
678 int count = 2;
680 if (prefs.encoding == UTF_8)
681 return (unsigned char*)rb->utf8decode(str, ch);
683 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
684 rb->utf8decode(utf8_tmp, ch);
686 /* return a pointer after the parsed section of the string */
687 #ifdef HAVE_LCD_BITMAP
688 if (prefs.encoding >= SJIS && *str >= 0x80
689 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
690 return (unsigned char*)str+2;
691 else
692 #endif
693 return (unsigned char*)str+1;
696 /* decode iso string into UTF-8 string */
697 static unsigned char *decode2utf8(const unsigned char *src, unsigned char *dst,
698 int skip_width, int disp_width)
700 unsigned short ucs[max_columns * DIACRITIC_FACTOR + 1];
701 unsigned short ch;
702 const unsigned char *oldstr = src;
703 const unsigned char *str = src;
704 unsigned char *utf8 = dst;
705 int chars = 0;
706 int idx = 0;
707 int width = max_width;
709 if (prefs.alignment == LEFT)
711 /* skip the skip_width */
712 if (skip_width > 0)
714 while (skip_width > 0 && *str != '\0')
716 oldstr = str;
717 str = get_ucs(oldstr, &ch);
718 skip_width -= glyph_width(ch);
720 if (skip_width < 0)
721 str = oldstr;
724 /* decode until string end or disp_width reached */
725 while(*str != '\0')
727 str = get_ucs(str, &ch);
728 disp_width -= glyph_width(ch);
729 if (disp_width < 0)
730 break;
731 utf8 = rb->utf8encode(ch, utf8);
734 else
736 while (width > 0 && *str != '\0')
738 str = get_ucs(str, &ch);
739 ucs[chars++] = ch;
741 ucs[chars] = 0;
743 skip_width = max_width - skip_width - disp_width;
744 if (skip_width > 0)
746 while (skip_width > 0 && chars-- > 0)
747 skip_width -= glyph_width(ucs[chars]);
749 if (skip_width < 0)
750 chars++;
752 else
754 idx = chars;
755 while (disp_width > 0 && idx-- > 0)
756 disp_width -= glyph_width(ucs[idx]);
758 if (disp_width < 0 || idx < 0)
759 idx++;
762 for ( ; idx < chars; idx++)
763 utf8 = rb->utf8encode(ucs[idx], utf8);
766 *utf8 = '\0';
768 /* return a pointer after the dst string ends */
769 return utf8;
772 /* set max_columns and max_width */
773 static void calc_max_width(void)
775 if (prefs.view_mode == NARROW)
777 max_columns = NARROW_MAX_COLUMNS;
778 max_width = draw_columns;
780 else
782 max_columns = WIDE_MAX_COLUMNS;
783 max_width = 2 * draw_columns;
787 static bool done = false;
788 static int col = 0;
790 static inline void advance_conters(unsigned short ch, int* k, int* width)
792 #ifdef HAVE_LCD_BITMAP
793 /* diacritics do not count */
794 if (rb->is_diacritic(ch, NULL))
795 return;
796 #endif
798 *width += glyph_width(ch);
799 (*k)++;
802 static inline bool line_is_full(int k, int width)
804 return ((k >= max_columns - 1) || (width >= max_width));
807 static unsigned char* crop_at_width(const unsigned char* p)
809 int k,width;
810 unsigned short ch;
811 const unsigned char *oldp = p;
813 k=width=0;
815 while (!line_is_full(k, width)) {
816 oldp = p;
817 if (BUFFER_OOB(p))
818 break;
819 p = get_ucs(p, &ch);
820 advance_conters(ch, &k, &width);
823 return (unsigned char*)oldp;
826 static unsigned char* find_first_feed(const unsigned char* p, int size)
828 int s = 0;
829 unsigned short ch;
830 const unsigned char *oldp = p;
831 const unsigned char *lbrkp = NULL;
832 int j;
833 int width = 0;
835 while(s <= size)
837 if (*p == 0)
838 return (unsigned char*)p;
839 oldp = p;
840 p = get_ucs(p, &ch);
842 if (prefs.word_mode == WRAP)
844 for (j = 0; j < ((int) sizeof(line_break)); j++)
846 if (ch == line_break[j])
848 lbrkp = p;
849 break;
854 width += glyph_width(ch);
855 if (width > max_width)
856 return (lbrkp == NULL)? (unsigned char*)oldp : (unsigned char*)lbrkp;
858 s += (p - oldp);
861 return NULL;
864 static unsigned char* find_last_feed(const unsigned char* p, int size)
866 int i;
868 for (i=size-1; i>=0; i--)
869 if (p[i] == 0)
870 return (unsigned char*) p+i;
872 return NULL;
875 static unsigned char* find_last_space(const unsigned char* p, int size)
877 int i, j, k;
879 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
881 i = size;
882 if (!BUFFER_OOB(&p[i]))
883 for (j=k; j < ((int) sizeof(line_break)) - 1; j++) {
884 if (p[i] == line_break[j])
885 return (unsigned char*) p+i;
888 if (prefs.word_mode == WRAP) {
889 for (i=size-1; i>=0; i--) {
890 for (j=k; j < (int) sizeof(line_break) - 1; j++) {
891 if (p[i] == line_break[j])
892 return (unsigned char*) p+i;
897 return NULL;
900 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
902 const unsigned char *next_line = NULL;
903 int size, i, j, j_next, j_prev, k, width, search_len, spaces, newlines;
904 bool first_chars;
905 unsigned short ch;
907 if (is_short != NULL)
908 *is_short = true;
910 if BUFFER_OOB(cur_line)
911 return NULL;
913 if (prefs.view_mode == WIDE) {
914 search_len = MAX_WIDTH;
916 else { /* prefs.view_mode == NARROW */
917 search_len = crop_at_width(cur_line) - cur_line;
920 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
922 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
923 /* Need to scan ahead and possibly increase search_len and size,
924 or possibly set next_line at second hard return in a row. */
925 next_line = NULL;
926 first_chars=true;
927 j_next=j=k=width=spaces=newlines=0;
928 while (1) {
929 const unsigned char *p, *oldp;
931 j_prev = j;
932 j = j_next;
934 if (BUFFER_OOB(cur_line+j))
935 return NULL;
936 if (line_is_full(k, width)) {
937 size = search_len = j_prev;
938 break;
941 oldp = p = &cur_line[j];
942 p = get_ucs(p, &ch);
943 j_next = j + (p - oldp);
945 switch (ch) {
946 case ' ':
947 if (prefs.line_mode == REFLOW) {
948 if (newlines > 0) {
949 size = j;
950 next_line = cur_line + size;
951 return (unsigned char*) next_line;
953 if (j==0) /* i=1 is intentional */
954 for (i=0; i<par_indent_spaces; i++)
955 advance_conters(' ', &k, &width);
957 if (!first_chars) spaces++;
958 break;
960 case 0:
961 if (newlines > 0) {
962 size = j;
963 next_line = cur_line + size - spaces;
964 if (next_line != cur_line)
965 return (unsigned char*) next_line;
966 break;
969 newlines++;
970 size += spaces -1;
971 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
972 return NULL;
973 search_len = size;
974 spaces = first_chars? 0:1;
975 break;
977 default:
978 if (prefs.line_mode==JOIN || newlines>0) {
979 while (spaces) {
980 spaces--;
981 advance_conters(' ', &k, &width);
982 if (line_is_full(k, width)) {
983 size = search_len = j;
984 break;
987 newlines=0;
988 } else if (spaces) {
989 /* REFLOW, multiple spaces between words: count only
990 * one. If more are needed, they will be added
991 * while drawing. */
992 search_len = size;
993 spaces=0;
994 advance_conters(' ', &k, &width);
995 if (line_is_full(k, width)) {
996 size = search_len = j;
997 break;
1000 first_chars = false;
1001 advance_conters(ch, &k, &width);
1002 break;
1006 else {
1007 /* find first hard return */
1008 next_line = find_first_feed(cur_line, size);
1011 if (next_line == NULL)
1012 if (size == search_len) {
1013 if (prefs.word_mode == WRAP) /* Find last space */
1014 next_line = find_last_space(cur_line, size);
1016 if (next_line == NULL) {
1017 next_line = crop_at_width(cur_line);
1019 else {
1020 if (prefs.word_mode == WRAP) {
1021 for (i=0;i<WRAP_TRIM;i++) {
1022 if (!(isspace(next_line[0]) && !BUFFER_OOB(next_line)))
1023 break;
1024 next_line++;
1030 if (prefs.line_mode == EXPAND)
1031 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1032 if (next_line[0] == 0)
1033 if (next_line != cur_line)
1034 return (unsigned char*) next_line;
1036 /* If next_line is pointing to a zero, increment it; i.e.,
1037 leave the terminator at the end of cur_line. If pointing
1038 to a hyphen, increment only if there is room to display
1039 the hyphen on current line (won't apply in WIDE mode,
1040 since it's guarenteed there won't be room). */
1041 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1042 if (next_line[0] == 0)/* ||
1043 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
1044 next_line++;
1046 if (BUFFER_OOB(next_line))
1048 if (BUFFER_EOF() && next_line != cur_line)
1049 return (unsigned char*) next_line;
1050 return NULL;
1053 if (is_short)
1054 *is_short = false;
1056 return (unsigned char*) next_line;
1059 static unsigned char* find_prev_line(const unsigned char* cur_line)
1061 const unsigned char *prev_line = NULL;
1062 const unsigned char *p;
1064 if BUFFER_OOB(cur_line)
1065 return NULL;
1067 /* To wrap consistently at the same places, we must
1068 start with a known hard return, then work downwards.
1069 We can either search backwards for a hard return,
1070 or simply start wrapping downwards from top of buffer.
1071 If current line is not near top of buffer, this is
1072 a file with long lines (paragraphs). We would need to
1073 read earlier sectors before we could decide how to
1074 properly wrap the lines above the current line, but
1075 it probably is not worth the disk access. Instead,
1076 start with top of buffer and wrap down from there.
1077 This may result in some lines wrapping at different
1078 points from where they wrap when scrolling down.
1079 If buffer is at top of file, start at top of buffer. */
1081 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
1082 prev_line = p = NULL;
1083 else
1084 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
1085 /* Null means no line feeds in buffer above current line. */
1087 if (prev_line == NULL)
1088 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
1089 prev_line = p = buffer;
1090 /* (else return NULL and read previous block) */
1092 /* Wrap downwards until too far, then use the one before. */
1093 while (p != NULL && p < cur_line) {
1094 prev_line = p;
1095 p = find_next_line(prev_line, NULL);
1098 if (BUFFER_OOB(prev_line))
1099 return NULL;
1101 return (unsigned char*) prev_line;
1104 static void check_bom(void)
1106 unsigned char bom[BOM_SIZE];
1107 off_t orig = rb->lseek(fd, 0, SEEK_CUR);
1109 is_bom = false;
1111 rb->lseek(fd, 0, SEEK_SET);
1113 if (rb->read(fd, bom, BOM_SIZE) == BOM_SIZE)
1114 is_bom = !memcmp(bom, BOM, BOM_SIZE);
1116 rb->lseek(fd, orig, SEEK_SET);
1119 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
1121 /* Read from file and preprocess the data */
1122 /* To minimize disk access, always read on sector boundaries */
1123 unsigned numread, i;
1124 bool found_CR = false;
1125 off_t offset = rb->lseek(fd, pos, SEEK_SET);
1127 if (offset == 0 && prefs.encoding == UTF_8 && is_bom)
1128 rb->lseek(fd, BOM_SIZE, SEEK_SET);
1130 numread = rb->read(fd, buf, size - 1);
1131 buf[numread] = 0;
1132 rb->button_clear_queue(); /* clear button queue */
1134 for(i = 0; i < numread; i++) {
1135 switch(buf[i]) {
1136 case '\r':
1137 if (mac_text) {
1138 buf[i] = 0;
1140 else {
1141 buf[i] = ' ';
1142 found_CR = true;
1144 break;
1146 case '\n':
1147 buf[i] = 0;
1148 found_CR = false;
1149 break;
1151 case 0: /* No break between case 0 and default, intentionally */
1152 buf[i] = ' ';
1153 default:
1154 if (found_CR) {
1155 buf[i - 1] = 0;
1156 found_CR = false;
1157 mac_text = true;
1159 break;
1164 static int viewer_find_bookmark(int page, int line)
1166 int i;
1168 for (i = 0; i < bookmark_count; i++)
1170 if (bookmarks[i].page == page && bookmarks[i].line == line)
1171 return i;
1173 return -1;
1176 static int read_and_synch(int direction)
1178 /* Read next (or prev) block, and reposition global pointers. */
1179 /* direction: 1 for down (i.e., further into file), -1 for up */
1180 int move_size, move_vector, offset;
1181 unsigned char *fill_buf;
1183 if (direction == -1) /* up */ {
1184 move_size = SMALL_BLOCK_SIZE;
1185 offset = 0;
1186 fill_buf = TOP_SECTOR;
1187 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1188 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
1190 else /* down */ {
1191 if (prefs.view_mode == WIDE) {
1192 /* WIDE mode needs more buffer so we have to read smaller blocks */
1193 move_size = SMALL_BLOCK_SIZE;
1194 offset = LARGE_BLOCK_SIZE;
1195 fill_buf = BOTTOM_SECTOR;
1196 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1197 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1199 else {
1200 move_size = LARGE_BLOCK_SIZE;
1201 offset = SMALL_BLOCK_SIZE;
1202 fill_buf = MID_SECTOR;
1203 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1206 move_vector = direction * move_size;
1207 screen_top_ptr -= move_vector;
1208 file_pos += move_vector;
1209 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1210 fill_buffer(file_pos + offset, fill_buf, move_size);
1211 return move_vector;
1214 static void get_next_line_position(unsigned char **line_begin,
1215 unsigned char **line_end,
1216 bool *is_short)
1218 int resynch_move;
1220 *line_begin = *line_end;
1221 *line_end = find_next_line(*line_begin, is_short);
1223 if (*line_end == NULL && !BUFFER_EOF())
1225 resynch_move = read_and_synch(1); /* Read block & move ptrs */
1226 *line_begin -= resynch_move;
1227 if (next_line_ptr > buffer)
1228 next_line_ptr -= resynch_move;
1230 *line_end = find_next_line(*line_begin, is_short);
1234 static void increment_current_line(void)
1236 if (cline < display_lines)
1237 cline++;
1238 else if (cpage < MAX_PAGE)
1240 cpage++;
1241 cline = 1;
1245 static void decrement_current_line(void)
1247 if (cline > 1)
1248 cline--;
1249 else if (cpage > 1)
1251 cpage--;
1252 cline = display_lines;
1256 static void viewer_scroll_up(void)
1258 unsigned char *p;
1260 p = find_prev_line(screen_top_ptr);
1261 if (p == NULL && !BUFFER_BOF()) {
1262 read_and_synch(-1);
1263 p = find_prev_line(screen_top_ptr);
1265 if (p != NULL)
1266 screen_top_ptr = p;
1268 decrement_current_line();
1271 static void viewer_scroll_down(bool autoscroll)
1273 if (cpage == lpage)
1274 return;
1276 if (next_line_ptr != NULL)
1277 screen_top_ptr = next_line_ptr;
1279 if (prefs.scroll_mode == LINE || autoscroll)
1280 increment_current_line();
1281 cline++;
1282 if (cline > display_lines) {
1283 cline = 1;
1284 cpage++;
1288 static void viewer_scroll_to_top_line(void)
1290 int line;
1292 for (line = cline; line > 1; line--)
1293 viewer_scroll_up();
1296 #ifdef HAVE_LCD_BITMAP
1297 static void viewer_scrollbar(void) {
1298 int items, min_shown, max_shown, sb_begin_y, sb_height;
1300 items = (int) file_size; /* (SH1 int is same as long) */
1301 min_shown = (int) file_pos + (screen_top_ptr - buffer);
1303 if (next_screen_ptr == NULL)
1304 max_shown = items;
1305 else
1306 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
1308 sb_begin_y = header_height;
1309 sb_height = LCD_HEIGHT - header_height - footer_height;
1311 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, sb_begin_y,
1312 SCROLLBAR_WIDTH-1, sb_height,
1313 items, min_shown, max_shown, VERTICAL);
1315 #endif
1317 #ifdef HAVE_LCD_BITMAP
1318 static void viewer_show_header(void)
1320 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1321 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1323 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1324 rb->lcd_putsxy(0, header_height - pf->height, file_name);
1327 static void viewer_show_footer(void)
1329 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1330 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1332 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1334 unsigned char buf[12];
1336 if (cline == 1)
1337 rb->snprintf(buf, sizeof(buf), "%d", cpage);
1338 else
1339 rb->snprintf(buf, sizeof(buf), "%d - %d", cpage, cpage+1);
1341 rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf);
1344 #endif
1346 static void viewer_draw(int col)
1348 int i, j, k, line_len, line_width, spaces, left_col=0;
1349 int width, extra_spaces, indent_spaces, spaces_per_word, spaces_width, disp_width = 0;
1350 bool multiple_spacing, line_is_short;
1351 unsigned short ch;
1352 unsigned char *str, *oldstr;
1353 unsigned char *line_begin;
1354 unsigned char *line_end;
1355 unsigned char c;
1356 int max_chars = max_columns * DIACRITIC_FACTOR;
1357 unsigned char scratch_buffer[max_chars * 4 + 1];
1358 unsigned char utf8_buffer[max_chars * 4 + 1];
1360 /* If col==-1 do all calculations but don't display */
1361 if (col != -1) {
1362 #ifdef HAVE_LCD_BITMAP
1363 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
1364 #else
1365 left_col = 0;
1366 #endif
1367 rb->lcd_clear_display();
1369 line_begin = line_end = screen_top_ptr;
1371 for (i = 0; i < display_lines; i++) {
1372 if (BUFFER_OOB(line_end))
1374 if (lpage == cpage)
1375 break; /* Happens after display last line at BUFFER_EOF() */
1377 if (lpage == 0 && cline == 1)
1379 lpage = cpage;
1380 last_screen_top_ptr = screen_top_ptr;
1381 last_file_pos = file_pos;
1385 get_next_line_position(&line_begin, &line_end, &line_is_short);
1386 if (line_end == NULL)
1388 if (BUFFER_OOB(line_begin))
1389 break;
1390 line_end = buffer_end + 1;
1393 line_len = line_end - line_begin;
1395 /* calculate line_len */
1396 str = oldstr = line_begin;
1397 j = -1;
1398 while (str < line_end) {
1399 oldstr = str;
1400 str = crop_at_width(str);
1401 j++;
1402 if (oldstr == str)
1404 oldstr = line_end;
1405 break;
1408 /* width of un-displayed part of the line */
1409 line_width = j*draw_columns;
1410 spaces_width = 0;
1411 while (oldstr < line_end) {
1412 oldstr = get_ucs(oldstr, &ch);
1413 /* add width of displayed part of the line */
1414 if (ch)
1416 int dw = glyph_width(ch);
1418 /* avoid counting spaces at the end of the line */
1419 if (ch == ' ')
1421 spaces_width += dw;
1423 else
1425 line_width += dw + spaces_width;
1426 spaces_width = 0;
1431 if (prefs.line_mode == JOIN) {
1432 if (line_begin[0] == 0) {
1433 line_begin++;
1434 if (prefs.word_mode == CHOP)
1435 line_end++;
1436 else
1437 line_len--;
1439 for (j=k=spaces=0; j < line_len; j++) {
1440 if (k == max_chars)
1441 break;
1443 c = line_begin[j];
1444 switch (c) {
1445 case ' ':
1446 spaces++;
1447 break;
1448 case 0:
1449 spaces = 0;
1450 scratch_buffer[k++] = ' ';
1451 break;
1452 default:
1453 while (spaces) {
1454 spaces--;
1455 scratch_buffer[k++] = ' ';
1456 if (k == max_chars - 1)
1457 break;
1459 scratch_buffer[k++] = c;
1460 break;
1463 scratch_buffer[k] = 0;
1465 else if (prefs.line_mode == REFLOW) {
1466 if (line_begin[0] == 0) {
1467 line_begin++;
1468 if (prefs.word_mode == CHOP)
1469 line_end++;
1470 else
1471 line_len--;
1474 indent_spaces = 0;
1475 if (!line_is_short) {
1476 multiple_spacing = false;
1477 width=spaces=0;
1478 for (str = line_begin; str < line_end; ) {
1479 str = get_ucs(str, &ch);
1480 switch (ch) {
1481 case 0:
1482 case ' ':
1483 if (str == line_begin)
1485 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1487 /* special case: indent the paragraph,
1488 * don't count spaces */
1489 indent_spaces = par_indent_spaces;
1492 else if (!multiple_spacing)
1493 spaces++;
1494 multiple_spacing = true;
1495 break;
1496 default:
1497 multiple_spacing = false;
1498 width += glyph_width(ch);
1499 break;
1502 if (multiple_spacing) spaces--;
1504 if (spaces) {
1505 /* total number of spaces to insert between words */
1506 extra_spaces = (max_width-width)/glyph_width(' ')
1507 - indent_spaces;
1508 /* number of spaces between each word*/
1509 spaces_per_word = extra_spaces / spaces;
1510 /* number of words with n+1 spaces (to fill up) */
1511 extra_spaces = extra_spaces % spaces;
1512 if (spaces_per_word > 2) { /* too much spacing is awful */
1513 spaces_per_word = 3;
1514 extra_spaces = 0;
1516 } else { /* this doesn't matter much... no spaces anyway */
1517 spaces_per_word = extra_spaces = 0;
1519 } else { /* end of a paragraph: don't fill line */
1520 spaces_per_word = 1;
1521 extra_spaces = 0;
1523 multiple_spacing = false;
1524 for (j=k=spaces=0; j < line_len; j++) {
1525 if (k == max_chars)
1526 break;
1528 c = line_begin[j];
1529 switch (c) {
1530 case 0:
1531 if (j == line_len - 1)
1532 break;
1533 case ' ':
1534 if (j==0) {
1535 /* indent paragraph */
1536 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1538 for (j=0; j<par_indent_spaces; j++)
1539 scratch_buffer[k++] = ' ';
1540 j=0;
1543 else if (!multiple_spacing) {
1544 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1545 scratch_buffer[k++] = ' ';
1546 spaces++;
1548 multiple_spacing = true;
1549 break;
1550 default:
1551 scratch_buffer[k++] = c;
1552 multiple_spacing = false;
1553 break;
1556 while (scratch_buffer[k-1] == ' ')
1557 k--;
1558 scratch_buffer[k] = 0;
1560 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1561 if (col != -1)
1563 rb->memcpy(scratch_buffer, line_begin, line_len);
1564 scratch_buffer[line_len] = '\0';
1568 /* create displayed line */
1569 if (col != -1)
1571 decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
1572 rb->lcd_getstringsize(utf8_buffer, &disp_width, NULL);
1575 /* display on screen the displayed part of the line */
1576 if (col != -1)
1578 bool in_page = (cline+i <= display_lines);
1579 int dpage = cpage + (in_page ? 0 : 1);
1580 int dline = cline + i - (in_page ? 0 : display_lines);
1581 bool bflag = (viewer_find_bookmark(dpage, dline) >= 0);
1582 #ifdef HAVE_LCD_BITMAP
1583 int dy = i * pf->height + header_height;
1584 int dx = (prefs.alignment == LEFT)? left_col : LCD_WIDTH - disp_width;
1585 #endif
1586 if (bflag)
1587 #ifdef HAVE_LCD_BITMAP
1589 rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG);
1590 rb->lcd_fillrect(left_col, dy, LCD_WIDTH - left_col, pf->height);
1591 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1593 rb->lcd_putsxy(dx, dy, utf8_buffer);
1594 rb->lcd_set_drawmode(DRMODE_SOLID);
1595 #else
1597 rb->lcd_puts(left_col, i, BOOKMARK_ICON);
1599 rb->lcd_puts(left_col+1, i, utf8_buffer);
1600 #endif
1603 if (i == 0)
1604 next_line_ptr = line_end;
1606 next_screen_ptr = line_end;
1607 if (BUFFER_OOB(next_screen_ptr))
1608 next_screen_ptr = NULL;
1610 #ifdef HAVE_LCD_BITMAP
1611 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1613 if (prefs.need_scrollbar)
1614 viewer_scrollbar();
1615 #else
1616 next_screen_to_draw_ptr = next_screen_ptr;
1617 #endif
1619 #ifdef HAVE_LCD_BITMAP
1620 /* show header */
1621 viewer_show_header();
1623 /* show footer */
1624 viewer_show_footer();
1625 #endif
1627 if (col != -1)
1628 rb->lcd_update();
1631 static void viewer_top(void)
1633 /* Read top of file into buffer
1634 and point screen pointer to top */
1635 if (file_pos != 0)
1637 rb->splash(0, "Loading...");
1639 file_pos = 0;
1640 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1641 fill_buffer(0, buffer, buffer_size);
1644 screen_top_ptr = buffer;
1645 cpage = 1;
1646 cline = 1;
1649 static void viewer_bottom(void)
1651 unsigned char *line_begin;
1652 unsigned char *line_end;
1654 rb->splash(0, "Loading...");
1656 if (last_screen_top_ptr)
1658 cpage = lpage;
1659 cline = 1;
1660 screen_top_ptr = last_screen_top_ptr;
1661 file_pos = last_file_pos;
1662 fill_buffer(file_pos, buffer, buffer_size);
1663 buffer_end = BUFFER_END();
1664 return;
1667 line_end = screen_top_ptr;
1669 while (!BUFFER_EOF() || !BUFFER_OOB(line_end))
1671 get_next_line_position(&line_begin, &line_end, NULL);
1672 if (line_end == NULL)
1673 break;
1675 increment_current_line();
1676 if (cline == 1)
1677 screen_top_ptr = line_end;
1679 lpage = cpage;
1680 cline = 1;
1681 last_screen_top_ptr = screen_top_ptr;
1682 last_file_pos = file_pos;
1683 buffer_end = BUFFER_END();
1686 #ifdef HAVE_LCD_BITMAP
1687 static void init_need_scrollbar(void) {
1688 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1689 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1690 viewer_draw(-1);
1691 prefs.need_scrollbar = NEED_SCROLLBAR();
1692 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1693 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1694 calc_max_width();
1697 static void init_header_and_footer(void)
1699 header_height = 0;
1700 footer_height = 0;
1701 if (rb->global_settings->statusbar == STATUSBAR_TOP)
1703 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1704 header_height = STATUSBAR_HEIGHT;
1706 if (prefs.footer_mode == FT_SBAR)
1707 prefs.footer_mode = FT_NONE;
1708 else if (prefs.footer_mode == FT_BOTH)
1709 prefs.footer_mode = FT_PAGE;
1711 else if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
1713 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1714 footer_height = STATUSBAR_HEIGHT;
1716 if (prefs.header_mode == HD_SBAR)
1717 prefs.header_mode = HD_NONE;
1718 else if (prefs.header_mode == HD_BOTH)
1719 prefs.header_mode = HD_PATH;
1721 else /* STATUSBAR_OFF || STATUSBAR_CUSTOM */
1723 if (prefs.header_mode == HD_SBAR)
1724 prefs.header_mode = HD_NONE;
1725 else if (prefs.header_mode == HD_BOTH)
1726 prefs.header_mode = HD_PATH;
1728 if (prefs.footer_mode == FT_SBAR)
1729 prefs.footer_mode = FT_NONE;
1730 else if (prefs.footer_mode == FT_BOTH)
1731 prefs.footer_mode = FT_PAGE;
1734 if (prefs.header_mode == HD_NONE || prefs.header_mode == HD_PATH ||
1735 prefs.footer_mode == FT_NONE || prefs.footer_mode == FT_PAGE)
1736 rb->gui_syncstatusbar_draw(rb->statusbars, false);
1738 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1739 header_height += pf->height;
1740 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1741 footer_height += pf->height;
1743 display_lines = (LCD_HEIGHT - header_height - footer_height) / pf->height;
1745 lpage = 0;
1746 last_file_pos = 0;
1747 last_screen_top_ptr = NULL;
1750 static bool change_font(unsigned char *font)
1752 unsigned char buf[MAX_PATH];
1754 if (font == NULL || *font == '\0')
1755 return false;
1757 rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font);
1758 if (rb->font_load(NULL, buf) < 0) {
1759 rb->splash(HZ/2, "Font load failed.");
1760 return false;
1763 return true;
1765 #endif
1767 static bool viewer_init(void)
1769 #ifdef HAVE_LCD_BITMAP
1770 /* initialize fonts */
1771 pf = rb->font_get(FONT_UI);
1772 if (pf == NULL)
1773 return false;
1775 draw_columns = display_columns = LCD_WIDTH;
1776 #else
1777 /* REAL fixed pitch :) all chars use up 1 cell */
1778 display_lines = 2;
1779 draw_columns = display_columns = 11;
1780 par_indent_spaces = 2;
1781 #endif
1783 fd = rb->open(file_name, O_RDONLY);
1784 if (fd < 0)
1785 return false;
1787 /* Init mac_text value used in processing buffer */
1788 mac_text = false;
1790 return true;
1793 /* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8,
1794 * then file size decreases only BOM_SIZE.
1796 static void get_filesize(void)
1798 file_size = rb->filesize(fd);
1799 if (file_size == -1)
1800 return;
1802 if (prefs.encoding == UTF_8 && is_bom)
1803 file_size -= BOM_SIZE;
1806 static int bm_comp(const void *a, const void *b)
1808 struct bookmark_info *pa;
1809 struct bookmark_info *pb;
1811 pa = (struct bookmark_info*)a;
1812 pb = (struct bookmark_info*)b;
1814 if (pa->page != pb->page)
1815 return pa->page - pb->page;
1817 return pa->line - pb->line;
1820 static void viewer_add_bookmark(void)
1822 if (bookmark_count >= MAX_BOOKMARKS-1)
1823 return;
1825 bookmarks[bookmark_count].file_position
1826 = file_pos + screen_top_ptr - buffer;
1827 bookmarks[bookmark_count].page = cpage;
1828 bookmarks[bookmark_count].line = cline;
1829 bookmarks[bookmark_count].flag = BOOKMARK_USER;
1830 bookmark_count++;
1833 static int viewer_add_last_read_bookmark(void)
1835 int i;
1837 i = viewer_find_bookmark(cpage, cline);
1838 if (i >= 0)
1839 bookmarks[i].flag |= BOOKMARK_LAST;
1840 else
1842 viewer_add_bookmark();
1843 i = bookmark_count-1;
1844 bookmarks[i].flag = BOOKMARK_LAST;
1846 return i;
1849 static void viewer_remove_bookmark(int i)
1851 int j;
1853 if (i < 0 || i >= bookmark_count)
1854 return;
1856 for (j = i+1; j < bookmark_count; j++)
1857 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1858 sizeof(struct bookmark_info));
1860 bookmark_count--;
1863 static void viewer_remove_last_read_bookmark(void)
1865 int i, j;
1867 for (i = 0; i < bookmark_count; i++)
1869 if (bookmarks[i].flag & BOOKMARK_LAST)
1871 if (bookmarks[i].flag == BOOKMARK_LAST)
1873 for (j = i+1; j < bookmark_count; j++)
1874 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1875 sizeof(struct bookmark_info));
1877 bookmark_count--;
1879 else
1880 bookmarks[i].flag = BOOKMARK_USER;
1881 break;
1886 static int viewer_get_last_read_bookmark(void)
1888 int i;
1890 for (i = 0; i < bookmark_count; i++)
1892 if (bookmarks[i].flag & BOOKMARK_LAST)
1893 return i;
1895 return -1;
1898 static void viewer_select_bookmark(int initval)
1900 int i;
1901 int ipage = 0;
1902 int iline = 0;
1903 int screen_pos;
1904 int screen_top;
1905 int selected = -1;
1907 struct opt_items items[bookmark_count];
1908 unsigned char names[bookmark_count][38];
1910 if (initval >= 0 && initval < bookmark_count)
1912 ipage = bookmarks[initval].page;
1913 iline = bookmarks[initval].line;
1916 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
1917 bm_comp);
1919 for (i = 0; i < bookmark_count; i++)
1921 rb->snprintf(names[i], sizeof(names[0]),
1922 #if CONFIG_KEYPAD != PLAYER_PAD
1923 "%sPage: %d Line: %d",
1924 #else
1925 "%sP:%d L:%d",
1926 #endif
1927 (bookmarks[i].flag&BOOKMARK_LAST)? "*":" ",
1928 bookmarks[i].page,
1929 bookmarks[i].line);
1930 items[i].string = names[i];
1931 items[i].voice_id = -1;
1932 if (selected < 0 && bookmarks[i].page == ipage && bookmarks[i].line == iline)
1933 selected = i;
1936 rb->set_option("Select bookmark", &selected, INT, items,
1937 sizeof(items) / sizeof(items[0]), NULL);
1939 if (selected < 0 || selected >= bookmark_count)
1941 if (initval < 0 || (selected = viewer_get_last_read_bookmark()) < 0)
1943 if (initval < 0)
1944 rb->splash(HZ, "Start the first page.");
1945 file_pos = 0;
1946 screen_top_ptr = buffer;
1947 cpage = 1;
1948 cline = 1;
1949 buffer_end = BUFFER_END();
1950 return;
1954 screen_pos = bookmarks[selected].file_position;
1955 screen_top = screen_pos % buffer_size;
1956 file_pos = screen_pos - screen_top;
1957 screen_top_ptr = buffer + screen_top;
1958 cpage = bookmarks[selected].page;
1959 cline = bookmarks[selected].line;
1960 buffer_end = BUFFER_END();
1963 static void viewer_default_preferences(void)
1965 prefs.word_mode = WRAP;
1966 prefs.line_mode = NORMAL;
1967 prefs.view_mode = NARROW;
1968 prefs.alignment = LEFT;
1969 prefs.scroll_mode = PAGE;
1970 prefs.page_mode = NO_OVERLAP;
1971 prefs.scrollbar_mode = SB_OFF;
1972 rb->memset(prefs.font, 0, MAX_PATH);
1973 #ifdef HAVE_LCD_BITMAP
1974 prefs.header_mode = HD_BOTH;
1975 prefs.footer_mode = FT_BOTH;
1976 rb->snprintf(prefs.font, MAX_PATH, "%s", rb->global_settings->font_file);
1977 #else
1978 prefs.header_mode = HD_NONE;
1979 prefs.footer_mode = FT_NONE;
1980 #endif
1981 prefs.autoscroll_speed = 1;
1982 /* Set codepage to system default */
1983 prefs.encoding = rb->global_settings->default_codepage;
1986 static bool viewer_read_preferences(int pfd, int version, struct preferences *prf)
1988 unsigned char buf[PREFERENCES_SIZE];
1989 unsigned char *p = buf;
1990 int read_size = PREFERENCES_SIZE;
1992 if (version == 0)
1993 read_size--;
1995 if (rb->read(pfd, buf, read_size) != read_size)
1996 return false;
1998 prf->word_mode = *p++;
1999 prf->line_mode = *p++;
2000 prf->view_mode = *p++;
2001 if (version > 0)
2002 prf->alignment = *p++;
2003 else
2004 prf->alignment = LEFT;
2005 prf->encoding = *p++;
2006 prf->scrollbar_mode = *p++;
2007 prf->need_scrollbar = *p++;
2008 prf->page_mode = *p++;
2009 prf->header_mode = *p++;
2010 prf->footer_mode = *p++;
2011 prf->scroll_mode = *p++;
2012 prf->autoscroll_speed = *p++;
2013 rb->memcpy(prf->font, p, MAX_PATH);
2014 return true;
2017 static bool viewer_write_preferences(int pfd, const struct preferences *prf)
2019 unsigned char buf[PREFERENCES_SIZE];
2020 unsigned char *p = buf;
2022 *p++ = prf->word_mode;
2023 *p++ = prf->line_mode;
2024 *p++ = prf->view_mode;
2025 *p++ = prf->alignment;
2026 *p++ = prf->encoding;
2027 *p++ = prf->scrollbar_mode;
2028 *p++ = prf->need_scrollbar;
2029 *p++ = prf->page_mode;
2030 *p++ = prf->header_mode;
2031 *p++ = prf->footer_mode;
2032 *p++ = prf->scroll_mode;
2033 *p++ = prf->autoscroll_speed;
2034 rb->memcpy(p, prf->font, MAX_PATH);
2036 return (rb->write(pfd, buf, sizeof(buf)) == sizeof(buf));
2039 static bool viewer_read_bookmark_info(int bfd, struct bookmark_info *b)
2041 unsigned char buf[BOOKMARK_SIZE];
2043 if (rb->read(bfd, buf, sizeof(buf)) != sizeof(buf))
2044 return false;
2046 b->file_position = (buf[0] << 24)|(buf[1] << 16)|(buf[2] << 8)|buf[3];
2047 b->page = (buf[4] << 8)|buf[5];
2048 b->line = buf[6];
2049 b->flag = buf[7];
2051 return true;
2054 static bool viewer_read_bookmark_infos(int bfd)
2056 unsigned char c;
2057 int i;
2059 if (rb->read(bfd, &c, 1) != 1)
2061 bookmark_count = 0;
2062 return false;
2065 bookmark_count = c;
2066 if (bookmark_count > MAX_BOOKMARKS)
2067 bookmark_count = MAX_BOOKMARKS;
2069 for (i = 0; i < bookmark_count; i++)
2071 if (!viewer_read_bookmark_info(bfd, &bookmarks[i]))
2073 bookmark_count = i;
2074 return false;
2077 return true;
2080 static bool viewer_write_bookmark_info(int bfd, struct bookmark_info *b)
2082 unsigned char buf[BOOKMARK_SIZE];
2083 unsigned char *p = buf;
2084 unsigned long ul;
2086 ul = b->file_position;
2087 *p++ = ul >> 24;
2088 *p++ = ul >> 16;
2089 *p++ = ul >> 8;
2090 *p++ = ul;
2092 ul = b->page;
2093 *p++ = ul >> 8;
2094 *p++ = ul;
2096 *p++ = b->line;
2097 *p = b->flag;
2099 return (rb->write(bfd, buf, sizeof(buf)) == sizeof(buf));
2102 static bool viewer_write_bookmark_infos(int bfd)
2104 unsigned char c = bookmark_count;
2105 int i;
2107 if (rb->write(bfd, &c, 1) != 1)
2108 return false;
2110 for (i = 0; i < bookmark_count; i++)
2112 if (!viewer_write_bookmark_info(bfd, &bookmarks[i]))
2113 return false;
2116 return true;
2119 static bool viewer_load_global_settings(void)
2121 unsigned buf[GLOBAL_SETTINGS_H_SIZE];
2122 int sfd = rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY);
2123 int version;
2124 bool res = false;
2126 if (sfd < 0)
2127 return false;
2129 if ((rb->read(sfd, buf, GLOBAL_SETTINGS_H_SIZE) == GLOBAL_SETTINGS_H_SIZE) ||
2130 (rb->memcmp(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1) == 0))
2132 version = buf[GLOBAL_SETTINGS_H_SIZE - 1] - GLOBAL_SETTINGS_FIRST_VERSION;
2133 res = viewer_read_preferences(sfd, version, &prefs);
2135 rb->close(sfd);
2136 return res;
2139 static bool viewer_save_global_settings(void)
2141 int sfd = rb->open(GLOBAL_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
2142 unsigned char buf[GLOBAL_SETTINGS_H_SIZE];
2144 if (sfd < 0)
2145 return false;
2147 rb->memcpy(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1);
2148 buf[GLOBAL_SETTINGS_H_SIZE - 1] = GLOBAL_SETTINGS_VERSION;
2150 if (rb->write(sfd, buf, GLOBAL_SETTINGS_H_SIZE) != GLOBAL_SETTINGS_H_SIZE ||
2151 !viewer_write_preferences(sfd, &prefs))
2153 rb->close(sfd);
2154 rb->remove(GLOBAL_SETTINGS_TMP_FILE);
2155 return false;
2157 rb->close(sfd);
2158 rb->remove(GLOBAL_SETTINGS_FILE);
2159 rb->rename(GLOBAL_SETTINGS_TMP_FILE, GLOBAL_SETTINGS_FILE);
2160 return true;
2163 static bool viewer_convert_settings(int sfd, int dfd, int old_ver)
2165 struct preferences new_prefs;
2166 off_t old_pos;
2167 off_t new_pos;
2168 unsigned char buf[MAX_PATH + 2];
2169 int settings_size;
2171 rb->read(sfd, buf, MAX_PATH + 2);
2172 rb->write(dfd, buf, MAX_PATH + 2);
2174 settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
2176 old_pos = rb->lseek(sfd, 0, SEEK_CUR);
2177 new_pos = rb->lseek(dfd, 0, SEEK_CUR);
2180 * when the settings size != preferences size + bookmarks size,
2181 * settings data are considered to be old version.
2183 if (old_ver > 0 && ((settings_size - PREFERENCES_SIZE) % 8) == 0)
2184 old_ver = 0;
2186 if (!viewer_read_preferences(sfd, old_ver, &new_prefs))
2187 return false;
2189 if (!viewer_write_preferences(dfd, &new_prefs))
2190 return false;
2192 settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos);
2194 if (settings_size > 0)
2196 rb->read(sfd, buf, settings_size);
2197 rb->write(dfd, buf, settings_size);
2200 settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos;
2201 buf[0] = settings_size >> 8;
2202 buf[1] = settings_size;
2203 rb->lseek(dfd, new_pos - 2, SEEK_SET);
2204 rb->write(dfd, buf, 2);
2205 rb->lseek(dfd, settings_size, SEEK_CUR);
2206 return true;
2209 static bool viewer_convert_settings_file(void)
2211 unsigned char buf[SETTINGS_H_SIZE+2];
2212 int sfd;
2213 int tfd;
2214 int i;
2215 int fcount;
2216 int version;
2217 bool res = true;
2219 if ((sfd = rb->open(SETTINGS_FILE, O_RDONLY)) < 0)
2220 return false;
2222 if ((tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
2224 rb->close(sfd);
2225 return false;
2228 rb->read(sfd, buf, SETTINGS_H_SIZE + 2);
2230 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2231 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE + 1];
2232 buf[SETTINGS_H_SIZE - 1] = SETTINGS_VERSION;
2234 rb->write(tfd, buf, SETTINGS_H_SIZE + 2);
2236 for (i = 0; i < fcount; i++)
2238 if (!viewer_convert_settings(sfd, tfd, version))
2240 res = false;
2241 break;
2245 rb->close(sfd);
2246 rb->close(tfd);
2248 if (!res)
2249 rb->remove(SETTINGS_TMP_FILE);
2250 else
2252 rb->remove(SETTINGS_FILE);
2253 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2255 return res;
2258 static bool viewer_load_settings(void)
2260 unsigned char buf[MAX_PATH+2];
2261 unsigned int fcount;
2262 unsigned int i;
2263 bool res = false;
2264 int sfd;
2265 unsigned int size;
2266 int version;
2268 sfd = rb->open(SETTINGS_FILE, O_RDONLY);
2269 if (sfd < 0)
2270 goto read_end;
2272 if ((rb->read(sfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2273 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2275 /* illegal setting file */
2276 rb->close(sfd);
2278 if (rb->file_exists(SETTINGS_FILE))
2279 rb->remove(SETTINGS_FILE);
2281 goto read_end;
2284 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2286 rb->close(sfd);
2287 if (!viewer_convert_settings_file())
2288 goto read_end;
2290 return viewer_load_settings();
2293 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2294 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2295 for (i = 0; i < fcount; i++)
2297 if (rb->read(sfd, buf, MAX_PATH+2) != MAX_PATH+2)
2298 break;
2300 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2303 * when the settings size != preferences size + bookmarks size,
2304 * the settings file converts to the newer.
2306 if (version > 0 && ((size - PREFERENCES_SIZE) % 8) == 0)
2308 rb->close(sfd);
2309 if (!viewer_convert_settings_file())
2310 break;
2312 return viewer_load_settings();
2315 if (rb->strcmp(buf, file_name))
2317 if (rb->lseek(sfd, size, SEEK_CUR) < 0)
2318 break;
2319 continue;
2321 if (!viewer_read_preferences(sfd, version, &prefs))
2322 break;
2324 res = viewer_read_bookmark_infos(sfd);
2325 break;
2328 rb->close(sfd);
2330 read_end:
2331 if (!res)
2333 /* load global settings */
2334 if (!viewer_load_global_settings())
2335 viewer_default_preferences();
2337 file_pos = 0;
2338 screen_top_ptr = buffer;
2339 cpage = 1;
2340 cline = 1;
2341 bookmark_count = 0;
2344 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
2345 calc_max_width();
2347 if (bookmark_count > 1)
2348 viewer_select_bookmark(-1);
2349 else if (bookmark_count == 1)
2351 int screen_pos;
2352 int screen_top;
2354 screen_pos = bookmarks[0].file_position;
2355 screen_top = screen_pos % buffer_size;
2356 file_pos = screen_pos - screen_top;
2357 screen_top_ptr = buffer + screen_top;
2358 cpage = bookmarks[0].page;
2359 cline = bookmarks[0].line;
2362 viewer_remove_last_read_bookmark();
2364 check_bom();
2365 get_filesize();
2367 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
2369 if (BUFFER_OOB(screen_top_ptr))
2370 screen_top_ptr = buffer;
2372 fill_buffer(file_pos, buffer, buffer_size);
2373 if (prefs.scroll_mode == PAGE && cline > 1)
2374 viewer_scroll_to_top_line();
2376 /* remember the current position */
2377 start_position = file_pos + screen_top_ptr - buffer;
2379 #ifdef HAVE_LCD_BITMAP
2380 /* load prefs font if it is different than the global settings font */
2381 if (rb->strcmp(prefs.font, rb->global_settings->font_file)) {
2382 if (!change_font(prefs.font)) {
2383 /* fallback by resetting prefs font to the global settings font */
2384 rb->memset(prefs.font, 0, MAX_PATH);
2385 rb->snprintf(prefs.font, MAX_PATH, "%s",
2386 rb->global_settings->font_file);
2388 if (!change_font(prefs.font))
2389 return false;
2393 init_need_scrollbar();
2394 init_header_and_footer();
2395 #endif
2397 return true;
2400 static bool copy_bookmark_file(int sfd, int dfd, off_t start, off_t size)
2402 off_t rsize;
2404 if (rb->lseek(sfd, start, SEEK_SET) < 0)
2405 return false;
2407 while (size > 0)
2409 if (size > buffer_size)
2410 rsize = buffer_size;
2411 else
2412 rsize = size;
2413 size -= rsize;
2415 if (rb->read(sfd, buffer, rsize) != rsize ||
2416 rb->write(dfd, buffer, rsize) != rsize)
2417 return false;
2419 return true;
2422 static bool viewer_save_settings(void)
2424 unsigned char buf[MAX_PATH+2];
2425 unsigned int fcount = 0;
2426 unsigned int i;
2427 int idx;
2428 int ofd;
2429 int tfd;
2430 off_t first_copy_size = 0;
2431 off_t second_copy_start_pos = 0;
2432 off_t size;
2434 /* add reading page to bookmarks */
2435 idx = viewer_find_bookmark(cpage, cline);
2436 if (idx >= 0)
2437 bookmarks[idx].flag |= BOOKMARK_LAST;
2438 else
2440 viewer_add_bookmark();
2441 bookmarks[bookmark_count-1].flag = BOOKMARK_LAST;
2444 tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
2445 if (tfd < 0)
2446 return false;
2448 ofd = rb->open(SETTINGS_FILE, O_RDWR);
2449 if (ofd >= 0)
2451 if ((rb->read(ofd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2452 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2454 rb->close(ofd);
2455 goto save_err;
2458 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2460 rb->close(ofd);
2461 if (!viewer_convert_settings_file())
2462 goto save_err;
2464 viewer_save_settings();
2467 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2469 for (i = 0; i < fcount; i++)
2471 if (rb->read(ofd, buf, MAX_PATH+2) != MAX_PATH+2)
2473 rb->close(ofd);
2474 goto save_err;
2476 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2477 if (rb->strcmp(buf, file_name))
2479 if (rb->lseek(ofd, size, SEEK_CUR) < 0)
2481 rb->close(ofd);
2482 goto save_err;
2485 else
2487 first_copy_size = rb->lseek(ofd, 0, SEEK_CUR);
2488 if (first_copy_size < 0)
2490 rb->close(ofd);
2491 goto save_err;
2493 second_copy_start_pos = first_copy_size + size;
2494 first_copy_size -= MAX_PATH+2;
2495 fcount--;
2496 break;
2499 if (first_copy_size == 0)
2500 first_copy_size = rb->filesize(ofd);
2502 if (!copy_bookmark_file(ofd, tfd, 0, first_copy_size))
2504 rb->close(ofd);
2505 goto save_err;
2507 if (second_copy_start_pos > 0)
2509 if (!copy_bookmark_file(ofd, tfd, second_copy_start_pos,
2510 rb->filesize(ofd) - second_copy_start_pos))
2512 rb->close(ofd);
2513 goto save_err;
2516 rb->close(ofd);
2518 else
2520 rb->memcpy(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1);
2521 buf[SETTINGS_H_SIZE-1] = SETTINGS_VERSION;
2522 buf[SETTINGS_H_SIZE ] = 0;
2523 buf[SETTINGS_H_SIZE+1] = 0;
2524 if (rb->write(tfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2)
2525 goto save_err;
2528 /* copy to current read file's bookmarks */
2529 rb->memset(buf, 0, MAX_PATH);
2530 rb->snprintf(buf, MAX_PATH, "%s", file_name);
2532 size = PREFERENCES_SIZE + bookmark_count * BOOKMARK_SIZE + 1;
2533 buf[MAX_PATH] = size >> 8;
2534 buf[MAX_PATH+1] = size;
2536 if (rb->write(tfd, buf, MAX_PATH+2) != MAX_PATH+2)
2537 goto save_err;
2539 if (!viewer_write_preferences(tfd, &prefs))
2540 goto save_err;
2542 if (!viewer_write_bookmark_infos(tfd))
2543 goto save_err;
2545 if (rb->lseek(tfd, SETTINGS_H_SIZE, SEEK_SET) < 0)
2546 goto save_err;
2548 fcount++;
2549 buf[0] = fcount >> 8;
2550 buf[1] = fcount;
2552 if (rb->write(tfd, buf, 2) != 2)
2553 goto save_err;
2555 rb->close(tfd);
2557 rb->remove(SETTINGS_FILE);
2558 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2560 return true;
2562 save_err:
2563 rb->close(tfd);
2564 rb->remove(SETTINGS_TMP_FILE);
2565 return false;
2568 static void viewer_exit(void *parameter)
2570 (void)parameter;
2572 /* save preference and bookmarks */
2573 if (!viewer_save_settings())
2574 rb->splash(HZ, "Can't save preference and bookmarks.");
2576 rb->close(fd);
2577 #ifdef HAVE_LCD_BITMAP
2578 if (rb->strcmp(prefs.font, rb->global_settings->font_file))
2579 change_font(rb->global_settings->font_file);
2580 #endif
2583 static void calc_page(void)
2585 int i;
2586 unsigned char *line_begin;
2587 unsigned char *line_end;
2588 off_t sfp;
2589 unsigned char *sstp;
2591 rb->splash(0, "Calculating page/line number...");
2593 /* add reading page to bookmarks */
2594 viewer_add_last_read_bookmark();
2596 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
2597 bm_comp);
2599 cpage = 1;
2600 cline = 1;
2601 file_pos = 0;
2602 screen_top_ptr = buffer;
2603 buffer_end = BUFFER_END();
2605 fill_buffer(file_pos, buffer, buffer_size);
2606 line_end = line_begin = buffer;
2608 /* update page and line of all bookmark */
2609 for (i = 0; i < bookmark_count; i++)
2611 sfp = bookmarks[i].file_position;
2612 sstp = buffer;
2614 while ((line_begin > sstp || sstp >= line_end) ||
2615 (file_pos > sfp || sfp >= file_pos + BUFFER_END() - buffer))
2617 get_next_line_position(&line_begin, &line_end, NULL);
2618 if (line_end == NULL)
2619 break;
2621 next_line_ptr = line_end;
2623 if (sstp == buffer &&
2624 file_pos <= sfp && sfp < file_pos + BUFFER_END() - buffer)
2625 sstp = sfp - file_pos + buffer;
2627 increment_current_line();
2630 decrement_current_line();
2631 bookmarks[i].page = cpage;
2632 bookmarks[i].line = cline;
2633 bookmarks[i].file_position = file_pos + (line_begin - buffer);
2634 increment_current_line();
2637 /* remove reading page's bookmark */
2638 for (i = 0; i < bookmark_count; i++)
2640 if (bookmarks[i].flag & BOOKMARK_LAST)
2642 int screen_pos;
2643 int screen_top;
2645 screen_pos = bookmarks[i].file_position;
2646 screen_top = screen_pos % buffer_size;
2647 file_pos = screen_pos - screen_top;
2648 screen_top_ptr = buffer + screen_top;
2650 cpage = bookmarks[i].page;
2651 cline = bookmarks[i].line;
2652 bookmarks[i].flag ^= BOOKMARK_LAST;
2653 buffer_end = BUFFER_END();
2655 fill_buffer(file_pos, buffer, buffer_size);
2657 if (bookmarks[i].flag == 0)
2658 viewer_remove_bookmark(i);
2660 if (prefs.scroll_mode == PAGE && cline > 1)
2661 viewer_scroll_to_top_line();
2662 break;
2667 static int col_limit(int col)
2669 if (col < 0)
2670 col = 0;
2671 else
2672 if (col >= max_width - draw_columns)
2673 col = max_width - draw_columns;
2675 return col;
2678 /* settings helper functions */
2680 static bool encoding_setting(void)
2682 static struct opt_items names[NUM_CODEPAGES];
2683 int idx;
2684 bool res;
2685 enum codepages oldenc = prefs.encoding;
2687 for (idx = 0; idx < NUM_CODEPAGES; idx++)
2689 names[idx].string = rb->get_codepage_name(idx);
2690 names[idx].voice_id = -1;
2693 res = rb->set_option("Encoding", &prefs.encoding, INT, names,
2694 sizeof(names) / sizeof(names[0]), NULL);
2696 /* When prefs.encoding changes into UTF-8 or changes from UTF-8,
2697 * filesize (file_size) might change.
2698 * In addition, if prefs.encoding is UTF-8, then BOM does not read.
2700 if (oldenc != prefs.encoding && (oldenc == UTF_8 || prefs.encoding == UTF_8))
2702 check_bom();
2703 get_filesize();
2704 fill_buffer(file_pos, buffer, buffer_size);
2707 return res;
2710 static bool word_wrap_setting(void)
2712 static const struct opt_items names[] = {
2713 {"On", -1},
2714 {"Off (Chop Words)", -1},
2717 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
2718 names, 2, NULL);
2721 static bool line_mode_setting(void)
2723 static const struct opt_items names[] = {
2724 {"Normal", -1},
2725 {"Join Lines", -1},
2726 {"Expand Lines", -1},
2727 #ifdef HAVE_LCD_BITMAP
2728 {"Reflow Lines", -1},
2729 #endif
2732 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
2733 sizeof(names) / sizeof(names[0]), NULL);
2736 static bool view_mode_setting(void)
2738 static const struct opt_items names[] = {
2739 {"No (Narrow)", -1},
2740 {"Yes", -1},
2742 bool ret;
2743 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
2744 names , 2, NULL);
2745 if (prefs.view_mode == NARROW)
2746 col = 0;
2747 calc_max_width();
2748 return ret;
2751 static bool scroll_mode_setting(void)
2753 static const struct opt_items names[] = {
2754 {"Scroll by Page", -1},
2755 {"Scroll by Line", -1},
2758 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
2759 names, 2, NULL);
2762 #ifdef HAVE_LCD_BITMAP
2763 static bool page_mode_setting(void)
2765 static const struct opt_items names[] = {
2766 {"No", -1},
2767 {"Yes", -1},
2770 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
2771 names, 2, NULL);
2774 static bool scrollbar_setting(void)
2776 static const struct opt_items names[] = {
2777 {"Off", -1},
2778 {"On", -1}
2781 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
2782 names, 2, NULL);
2785 static bool header_setting(void)
2787 int len = (rb->global_settings->statusbar == STATUSBAR_TOP)? 4 : 2;
2788 struct opt_items names[len];
2790 names[0].string = "None";
2791 names[0].voice_id = -1;
2792 names[1].string = "File path";
2793 names[1].voice_id = -1;
2795 if (rb->global_settings->statusbar == STATUSBAR_TOP)
2797 names[2].string = "Status bar";
2798 names[2].voice_id = -1;
2799 names[3].string = "Both";
2800 names[3].voice_id = -1;
2803 return rb->set_option("Show Header", &prefs.header_mode, INT,
2804 names, len, NULL);
2807 static bool footer_setting(void)
2809 int len = (rb->global_settings->statusbar == STATUSBAR_BOTTOM)? 4 : 2;
2810 struct opt_items names[len];
2812 names[0].string = "None";
2813 names[0].voice_id = -1;
2814 names[1].string = "Page Num";
2815 names[1].voice_id = -1;
2817 if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
2819 names[2].string = "Status bar";
2820 names[2].voice_id = -1;
2821 names[3].string = "Both";
2822 names[3].voice_id = -1;
2825 return rb->set_option("Show Footer", &prefs.footer_mode, INT,
2826 names, len, NULL);
2829 static int font_comp(const void *a, const void *b)
2831 struct opt_items *pa;
2832 struct opt_items *pb;
2834 pa = (struct opt_items *)a;
2835 pb = (struct opt_items *)b;
2837 return rb->strcmp(pa->string, pb->string);
2840 static bool font_setting(void)
2842 int count = 0;
2843 DIR *dir;
2844 struct dirent *entry;
2845 int i = 0;
2846 int len;
2847 int new_font = 0;
2848 int old_font;
2849 bool res;
2850 int size = 0;
2852 dir = rb->opendir(FONT_DIR);
2853 if (!dir)
2855 rb->splash(HZ/2, "Font dir is not accessible");
2856 return false;
2859 while (1)
2861 entry = rb->readdir(dir);
2863 if (entry == NULL)
2864 break;
2866 len = rb->strlen(entry->d_name);
2867 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2868 continue;
2869 size += len-3;
2870 count++;
2872 rb->closedir(dir);
2874 struct opt_items names[count];
2875 unsigned char font_names[size];
2876 unsigned char *p = font_names;
2878 dir = rb->opendir(FONT_DIR);
2879 if (!dir)
2881 rb->splash(HZ/2, "Font dir is not accessible");
2882 return false;
2885 while (1)
2887 entry = rb->readdir(dir);
2889 if (entry == NULL)
2890 break;
2892 len = rb->strlen(entry->d_name);
2893 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2894 continue;
2896 rb->snprintf(p, len-3, "%s", entry->d_name);
2897 names[i].string = p;
2898 names[i].voice_id = -1;
2899 p += len-3;
2900 i++;
2901 if (i >= count)
2902 break;
2904 rb->closedir(dir);
2906 rb->qsort(names, count, sizeof(struct opt_items), font_comp);
2908 for (i = 0; i < count; i++)
2910 if (!rb->strcmp(names[i].string, prefs.font))
2912 new_font = i;
2913 break;
2916 old_font = new_font;
2918 res = rb->set_option("Select Font", &new_font, INT,
2919 names, count, NULL);
2921 if (new_font != old_font)
2923 /* load selected font */
2924 if (!change_font((unsigned char *)names[new_font].string)) {
2925 /* revert by re-loading the preferences font */
2926 change_font(prefs.font);
2927 return false;
2929 rb->memset(prefs.font, 0, MAX_PATH);
2930 rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string);
2933 return res;
2936 static bool alignment_setting(void)
2938 static const struct opt_items names[] = {
2939 {"Left", -1},
2940 {"Right", -1},
2943 return rb->set_option("Alignment", &prefs.alignment, INT,
2944 names , 2, NULL);
2946 #endif
2948 static bool autoscroll_speed_setting(void)
2950 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
2951 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
2954 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
2955 NULL, NULL, Icon_NOICON);
2956 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
2957 NULL, NULL, Icon_NOICON);
2958 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
2959 NULL, NULL, Icon_NOICON);
2960 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
2961 NULL, NULL, Icon_NOICON);
2962 #ifdef HAVE_LCD_BITMAP
2963 MENUITEM_FUNCTION(alignment_item, 0, "Alignment", alignment_setting,
2964 NULL, NULL, Icon_NOICON);
2965 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
2966 NULL, NULL, Icon_NOICON);
2967 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
2968 NULL, NULL, Icon_NOICON);
2969 MENUITEM_FUNCTION(header_item, 0, "Show Header", header_setting,
2970 NULL, NULL, Icon_NOICON);
2971 MENUITEM_FUNCTION(footer_item, 0, "Show Footer", footer_setting,
2972 NULL, NULL, Icon_NOICON);
2973 MENUITEM_FUNCTION(font_item, 0, "Font", font_setting,
2974 NULL, NULL, Icon_NOICON);
2975 #endif
2976 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
2977 NULL, NULL, Icon_NOICON);
2978 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
2979 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
2980 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
2981 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
2982 #ifdef HAVE_LCD_BITMAP
2983 &alignment_item, &scrollbar_item, &page_mode_item, &header_item,
2984 &footer_item, &font_item,
2985 #endif
2986 &scroll_mode_item, &autoscroll_speed_item);
2988 static bool viewer_options_menu(bool is_global)
2990 bool result;
2991 struct preferences tmp_prefs;
2993 rb->memcpy(&tmp_prefs, &prefs, sizeof(struct preferences));
2995 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
2997 if (!is_global && rb->memcmp(&tmp_prefs, &prefs, sizeof(struct preferences)))
2999 /* Show-scrollbar mode for current view-width mode */
3000 #ifdef HAVE_LCD_BITMAP
3001 init_need_scrollbar();
3002 init_header_and_footer();
3003 #endif
3004 calc_page();
3006 return result;
3009 static void viewer_menu(void)
3011 int result;
3013 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
3014 "Return", "Viewer Options",
3015 "Show Playback Menu", "Select Bookmark",
3016 "Global Settings", "Quit");
3018 result = rb->do_menu(&menu, NULL, NULL, false);
3019 switch (result)
3021 case 0: /* return */
3022 break;
3023 case 1: /* change settings */
3024 done = viewer_options_menu(false);
3025 break;
3026 case 2: /* playback control */
3027 playback_control(NULL);
3028 break;
3029 case 3: /* select bookmark */
3030 viewer_select_bookmark(viewer_add_last_read_bookmark());
3031 viewer_remove_last_read_bookmark();
3032 fill_buffer(file_pos, buffer, buffer_size);
3033 if (prefs.scroll_mode == PAGE && cline > 1)
3034 viewer_scroll_to_top_line();
3035 break;
3036 case 4: /* change global settings */
3038 struct preferences orig_prefs;
3040 rb->memcpy(&orig_prefs, &prefs, sizeof(struct preferences));
3041 if (!viewer_load_global_settings())
3042 viewer_default_preferences();
3043 done = viewer_options_menu(true);
3044 viewer_save_global_settings();
3045 rb->memcpy(&prefs, &orig_prefs, sizeof(struct preferences));
3047 break;
3048 case 5: /* quit */
3049 viewer_exit(NULL);
3050 done = true;
3051 break;
3053 viewer_draw(col);
3056 enum plugin_status plugin_start(const void* file)
3058 int button, i, ok;
3059 int lastbutton = BUTTON_NONE;
3060 bool autoscroll = false;
3061 long old_tick;
3062 size_t buf_size;
3064 old_tick = *rb->current_tick;
3066 /* get the plugin buffer */
3067 buffer = rb->plugin_get_buffer(&buf_size);
3068 buffer_size = buf_size;
3069 if (buffer_size == 0)
3071 rb->splash(HZ, "buffer does not allocate !!");
3072 return PLUGIN_ERROR;
3074 block_size = buffer_size / 3;
3075 buffer_size = 3 * block_size;
3077 if (!file)
3078 return PLUGIN_ERROR;
3080 file_name = file;
3081 ok = viewer_init();
3082 if (!ok) {
3083 rb->splash(HZ, "Error opening file.");
3084 return PLUGIN_ERROR;
3087 if (!viewer_load_settings()) /* load the preferences and bookmark */
3088 return PLUGIN_ERROR;
3090 #if LCD_DEPTH > 1
3091 rb->lcd_set_backdrop(NULL);
3092 #endif
3094 viewer_draw(col);
3096 while (!done) {
3098 if(autoscroll)
3100 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
3102 viewer_scroll_down(true);
3103 viewer_draw(col);
3104 old_tick = *rb->current_tick;
3108 button = rb->button_get_w_tmo(HZ/10);
3110 if (prefs.view_mode != WIDE) {
3111 /* when not in wide view mode, the SCREEN_LEFT and SCREEN_RIGHT
3112 buttons jump to the beginning and end of the file. To stop
3113 users doing this by accident, replace non-held occurrences
3114 with page up/down instead. */
3115 if (button == VIEWER_SCREEN_LEFT)
3116 button = VIEWER_PAGE_UP;
3117 else if (button == VIEWER_SCREEN_RIGHT)
3118 button = VIEWER_PAGE_DOWN;
3121 switch (button) {
3122 case VIEWER_MENU:
3123 #ifdef VIEWER_MENU2
3124 case VIEWER_MENU2:
3125 #endif
3126 viewer_menu();
3127 break;
3129 case VIEWER_AUTOSCROLL:
3130 #ifdef VIEWER_AUTOSCROLL_PRE
3131 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
3132 break;
3133 #endif
3134 autoscroll = !autoscroll;
3135 break;
3137 case VIEWER_PAGE_UP:
3138 case VIEWER_PAGE_UP | BUTTON_REPEAT:
3139 #ifdef VIEWER_PAGE_UP2
3140 case VIEWER_PAGE_UP2:
3141 case VIEWER_PAGE_UP2 | BUTTON_REPEAT:
3142 #endif
3143 if (prefs.scroll_mode == PAGE)
3145 /* Page up */
3146 #ifdef HAVE_LCD_BITMAP
3147 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
3148 #else
3149 for (i = 0; i < display_lines; i++)
3150 #endif
3151 viewer_scroll_up();
3153 else
3154 viewer_scroll_up();
3155 old_tick = *rb->current_tick;
3156 viewer_draw(col);
3157 break;
3159 case VIEWER_PAGE_DOWN:
3160 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
3161 #ifdef VIEWER_PAGE_DOWN2
3162 case VIEWER_PAGE_DOWN2:
3163 case VIEWER_PAGE_DOWN2 | BUTTON_REPEAT:
3164 #endif
3165 if (prefs.scroll_mode == PAGE)
3167 /* Page down */
3168 if (next_screen_ptr != NULL)
3170 screen_top_ptr = next_screen_to_draw_ptr;
3171 if (cpage < MAX_PAGE)
3172 cpage++;
3175 else
3176 viewer_scroll_down(autoscroll);
3177 old_tick = *rb->current_tick;
3178 viewer_draw(col);
3179 break;
3181 case VIEWER_SCREEN_LEFT:
3182 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
3183 if (prefs.view_mode == WIDE) {
3184 /* Screen left */
3185 col = col_limit(col - draw_columns);
3187 else { /* prefs.view_mode == NARROW */
3188 /* Top of file */
3189 viewer_top();
3192 viewer_draw(col);
3193 break;
3195 case VIEWER_SCREEN_RIGHT:
3196 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
3197 if (prefs.view_mode == WIDE) {
3198 /* Screen right */
3199 col = col_limit(col + draw_columns);
3201 else { /* prefs.view_mode == NARROW */
3202 /* Bottom of file */
3203 viewer_bottom();
3206 viewer_draw(col);
3207 break;
3209 #ifdef VIEWER_LINE_UP
3210 case VIEWER_LINE_UP:
3211 case VIEWER_LINE_UP | BUTTON_REPEAT:
3212 /* Scroll up one line */
3213 viewer_scroll_up();
3214 old_tick = *rb->current_tick;
3215 viewer_draw(col);
3216 break;
3218 case VIEWER_LINE_DOWN:
3219 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
3220 /* Scroll down one line */
3221 viewer_scroll_down(autoscroll);
3222 increment_current_line();
3223 old_tick = *rb->current_tick;
3224 viewer_draw(col);
3225 break;
3226 #endif
3227 #ifdef VIEWER_COLUMN_LEFT
3228 case VIEWER_COLUMN_LEFT:
3229 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
3230 if (prefs.view_mode == WIDE) {
3231 /* Scroll left one column */
3232 col = col_limit(col - glyph_width('o'));
3233 viewer_draw(col);
3235 break;
3237 case VIEWER_COLUMN_RIGHT:
3238 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
3239 if (prefs.view_mode == WIDE) {
3240 /* Scroll right one column */
3241 col = col_limit(col + glyph_width('o'));
3242 viewer_draw(col);
3244 break;
3245 #endif
3247 #ifdef VIEWER_RC_QUIT
3248 case VIEWER_RC_QUIT:
3249 #endif
3250 case VIEWER_QUIT:
3251 #ifdef VIEWER_QUIT2
3252 case VIEWER_QUIT2:
3253 #endif
3254 viewer_exit(NULL);
3255 done = true;
3256 break;
3258 case VIEWER_BOOKMARK:
3260 int idx = viewer_find_bookmark(cpage, cline);
3262 if (idx < 0)
3264 if (bookmark_count >= MAX_BOOKMARKS-1)
3265 rb->splash(HZ/2, "No more bookmarks");
3266 else
3268 viewer_add_bookmark();
3269 rb->splash(HZ/2, "Bookmark added");
3272 else
3274 viewer_remove_bookmark(idx);
3275 rb->splash(HZ/2, "Bookmark removed");
3277 viewer_draw(col);
3279 break;
3281 default:
3282 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
3283 == SYS_USB_CONNECTED)
3284 return PLUGIN_USB_CONNECTED;
3285 break;
3287 if (button != BUTTON_NONE)
3289 lastbutton = button;
3290 rb->yield();
3293 return PLUGIN_OK;