Use a more natural guard for the callback definition
[kugel-rb.git] / apps / plugins / viewer.c
blob9ebc1d9cf6b52c706572a8ad2f0b211b7554c140
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_width;
618 static int max_columns;
619 static int cline = 1;
620 static int cpage = 1;
621 static int lpage = 0;
622 static unsigned char *screen_top_ptr;
623 static unsigned char *next_screen_ptr;
624 static unsigned char *next_screen_to_draw_ptr;
625 static unsigned char *next_line_ptr;
626 static unsigned char *last_screen_top_ptr = NULL;
627 #ifdef HAVE_LCD_BITMAP
628 static struct font *pf;
629 static int header_height = 0;
630 static int footer_height = 0;
631 #endif
632 struct bookmark_info bookmarks[MAX_BOOKMARKS];
633 static int bookmark_count;
635 /* UTF-8 BOM */
636 #define BOM "\xef\xbb\xbf"
637 #define BOM_SIZE 3
639 static bool is_bom = false;
641 /* We draw a diacritic char over a non-diacritic one. Therefore, such chars are
642 * not considered to occupy space, therefore buffers might have more than
643 * max_columns characters. The DIACRITIC_FACTOR is the max ratio between all
644 * characters and non-diacritic characters in the buffer
646 #define DIACRITIC_FACTOR 2
648 /* calculate the width of a UCS character (zero width for diacritics) */
649 static int glyph_width(unsigned short ch)
651 if (ch == 0)
652 ch = ' ';
654 #ifdef HAVE_LCD_BITMAP
655 if (rb->is_diacritic(ch, NULL))
656 return 0;
658 return rb->font_get_width(pf, ch);
659 #else
660 return 1;
661 #endif
664 /* get UCS character from string */
665 static unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
667 unsigned char utf8_tmp[6];
668 int count = 2;
670 if (prefs.encoding == UTF_8)
671 return (unsigned char*)rb->utf8decode(str, ch);
673 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
674 rb->utf8decode(utf8_tmp, ch);
676 /* return a pointer after the parsed section of the string */
677 #ifdef HAVE_LCD_BITMAP
678 if (prefs.encoding >= SJIS && *str >= 0x80
679 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
680 return (unsigned char*)str+2;
681 else
682 #endif
683 return (unsigned char*)str+1;
686 /* decode iso string into UTF-8 string */
687 static unsigned char *decode2utf8(const unsigned char *src, unsigned char *dst,
688 int skip_width, int disp_width)
690 unsigned short ucs[max_columns * DIACRITIC_FACTOR + 1];
691 unsigned short ch;
692 const unsigned char *oldstr = src;
693 const unsigned char *str = src;
694 unsigned char *utf8 = dst;
695 int chars = 0;
696 int idx = 0;
697 int width = max_width;
699 if (prefs.alignment == LEFT)
701 /* skip the skip_width */
702 if (skip_width > 0)
704 while (skip_width > 0 && *str != '\0')
706 oldstr = str;
707 str = get_ucs(oldstr, &ch);
708 skip_width -= glyph_width(ch);
710 if (skip_width < 0)
711 str = oldstr;
714 /* decode until string end or disp_width reached */
715 while(*str != '\0')
717 str = get_ucs(str, &ch);
718 disp_width -= glyph_width(ch);
719 if (disp_width < 0)
720 break;
721 utf8 = rb->utf8encode(ch, utf8);
724 else
726 while (width > 0 && *str != '\0')
728 str = get_ucs(str, &ch);
729 ucs[chars++] = ch;
731 ucs[chars] = 0;
733 skip_width = max_width - skip_width - disp_width;
734 if (skip_width > 0)
736 while (skip_width > 0 && chars-- > 0)
737 skip_width -= glyph_width(ucs[chars]);
739 if (skip_width < 0)
740 chars++;
742 else
744 idx = chars;
745 while (disp_width > 0 && idx-- > 0)
746 disp_width -= glyph_width(ucs[idx]);
748 if (disp_width < 0 || idx < 0)
749 idx++;
752 for ( ; idx < chars; idx++)
753 utf8 = rb->utf8encode(ucs[idx], utf8);
756 *utf8 = '\0';
758 /* return a pointer after the dst string ends */
759 return utf8;
762 /* set max_columns and max_width */
763 static void calc_max_width(void)
765 if (prefs.view_mode == NARROW)
767 max_columns = NARROW_MAX_COLUMNS;
768 max_width = draw_columns;
770 else
772 max_columns = WIDE_MAX_COLUMNS;
773 max_width = 2 * draw_columns;
777 static bool done = false;
778 static int col = 0;
780 static inline void advance_conters(unsigned short ch, int* k, int* width)
782 #ifdef HAVE_LCD_BITMAP
783 /* diacritics do not count */
784 if (rb->is_diacritic(ch, NULL))
785 return;
786 #endif
788 *width += glyph_width(ch);
789 (*k)++;
792 static inline bool line_is_full(int k, int width)
794 return ((k >= max_columns - 1) || (width >= max_width));
797 static unsigned char* crop_at_width(const unsigned char* p)
799 int k,width;
800 unsigned short ch;
801 const unsigned char *oldp = p;
803 k=width=0;
805 while (!line_is_full(k, width)) {
806 oldp = p;
807 if (BUFFER_OOB(p))
808 break;
809 p = get_ucs(p, &ch);
810 advance_conters(ch, &k, &width);
813 return (unsigned char*)oldp;
816 static unsigned char* find_first_feed(const unsigned char* p, int size)
818 int s = 0;
819 unsigned short ch;
820 const unsigned char *oldp = p;
821 const unsigned char *lbrkp = NULL;
822 int j;
823 int width = 0;
825 while(s <= size)
827 if (*p == 0)
828 return (unsigned char*)p;
829 oldp = p;
830 p = get_ucs(p, &ch);
832 if (prefs.word_mode == WRAP)
834 for (j = 0; j < ((int) sizeof(line_break)); j++)
836 if (ch == line_break[j])
838 lbrkp = p;
839 break;
844 width += glyph_width(ch);
845 if (width > max_width)
846 return (lbrkp == NULL)? (unsigned char*)oldp : (unsigned char*)lbrkp;
848 s += (p - oldp);
851 return NULL;
854 static unsigned char* find_last_feed(const unsigned char* p, int size)
856 int i;
858 for (i=size-1; i>=0; i--)
859 if (p[i] == 0)
860 return (unsigned char*) p+i;
862 return NULL;
865 static unsigned char* find_last_space(const unsigned char* p, int size)
867 int i, j, k;
869 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
871 i = size;
872 if (!BUFFER_OOB(&p[i]))
873 for (j=k; j < ((int) sizeof(line_break)) - 1; j++) {
874 if (p[i] == line_break[j])
875 return (unsigned char*) p+i;
878 if (prefs.word_mode == WRAP) {
879 for (i=size-1; i>=0; i--) {
880 for (j=k; j < (int) sizeof(line_break) - 1; j++) {
881 if (p[i] == line_break[j])
882 return (unsigned char*) p+i;
887 return NULL;
890 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
892 const unsigned char *next_line = NULL;
893 int size, i, j, j_next, j_prev, k, width, search_len, spaces, newlines;
894 bool first_chars;
895 unsigned short ch;
897 if (is_short != NULL)
898 *is_short = true;
900 if BUFFER_OOB(cur_line)
901 return NULL;
903 if (prefs.view_mode == WIDE) {
904 search_len = MAX_WIDTH;
906 else { /* prefs.view_mode == NARROW */
907 search_len = crop_at_width(cur_line) - cur_line;
910 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
912 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
913 /* Need to scan ahead and possibly increase search_len and size,
914 or possibly set next_line at second hard return in a row. */
915 next_line = NULL;
916 first_chars=true;
917 j_next=j=k=width=spaces=newlines=0;
918 while (1) {
919 const unsigned char *p, *oldp;
921 j_prev = j;
922 j = j_next;
924 if (BUFFER_OOB(cur_line+j))
925 return NULL;
926 if (line_is_full(k, width)) {
927 size = search_len = j_prev;
928 break;
931 oldp = p = &cur_line[j];
932 p = get_ucs(p, &ch);
933 j_next = j + (p - oldp);
935 switch (ch) {
936 case ' ':
937 if (prefs.line_mode == REFLOW) {
938 if (newlines > 0) {
939 size = j;
940 next_line = cur_line + size;
941 return (unsigned char*) next_line;
943 if (j==0) /* i=1 is intentional */
944 for (i=0; i<par_indent_spaces; i++)
945 advance_conters(' ', &k, &width);
947 if (!first_chars) spaces++;
948 break;
950 case 0:
951 if (newlines > 0) {
952 size = j;
953 next_line = cur_line + size - spaces;
954 if (next_line != cur_line)
955 return (unsigned char*) next_line;
956 break;
959 newlines++;
960 size += spaces -1;
961 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
962 return NULL;
963 search_len = size;
964 spaces = first_chars? 0:1;
965 break;
967 default:
968 if (prefs.line_mode==JOIN || newlines>0) {
969 while (spaces) {
970 spaces--;
971 advance_conters(' ', &k, &width);
972 if (line_is_full(k, width)) {
973 size = search_len = j;
974 break;
977 newlines=0;
978 } else if (spaces) {
979 /* REFLOW, multiple spaces between words: count only
980 * one. If more are needed, they will be added
981 * while drawing. */
982 search_len = size;
983 spaces=0;
984 advance_conters(' ', &k, &width);
985 if (line_is_full(k, width)) {
986 size = search_len = j;
987 break;
990 first_chars = false;
991 advance_conters(ch, &k, &width);
992 break;
996 else {
997 /* find first hard return */
998 next_line = find_first_feed(cur_line, size);
1001 if (next_line == NULL)
1002 if (size == search_len) {
1003 if (prefs.word_mode == WRAP) /* Find last space */
1004 next_line = find_last_space(cur_line, size);
1006 if (next_line == NULL) {
1007 next_line = crop_at_width(cur_line);
1009 else {
1010 if (prefs.word_mode == WRAP) {
1011 for (i=0;i<WRAP_TRIM;i++) {
1012 if (!(isspace(next_line[0]) && !BUFFER_OOB(next_line)))
1013 break;
1014 next_line++;
1020 if (prefs.line_mode == EXPAND)
1021 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1022 if (next_line[0] == 0)
1023 if (next_line != cur_line)
1024 return (unsigned char*) next_line;
1026 /* If next_line is pointing to a zero, increment it; i.e.,
1027 leave the terminator at the end of cur_line. If pointing
1028 to a hyphen, increment only if there is room to display
1029 the hyphen on current line (won't apply in WIDE mode,
1030 since it's guarenteed there won't be room). */
1031 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1032 if (next_line[0] == 0)/* ||
1033 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
1034 next_line++;
1036 if (BUFFER_OOB(next_line))
1038 if (BUFFER_EOF() && next_line != cur_line)
1039 return (unsigned char*) next_line;
1040 return NULL;
1043 if (is_short)
1044 *is_short = false;
1046 return (unsigned char*) next_line;
1049 static unsigned char* find_prev_line(const unsigned char* cur_line)
1051 const unsigned char *prev_line = NULL;
1052 const unsigned char *p;
1054 if BUFFER_OOB(cur_line)
1055 return NULL;
1057 /* To wrap consistently at the same places, we must
1058 start with a known hard return, then work downwards.
1059 We can either search backwards for a hard return,
1060 or simply start wrapping downwards from top of buffer.
1061 If current line is not near top of buffer, this is
1062 a file with long lines (paragraphs). We would need to
1063 read earlier sectors before we could decide how to
1064 properly wrap the lines above the current line, but
1065 it probably is not worth the disk access. Instead,
1066 start with top of buffer and wrap down from there.
1067 This may result in some lines wrapping at different
1068 points from where they wrap when scrolling down.
1069 If buffer is at top of file, start at top of buffer. */
1071 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
1072 prev_line = p = NULL;
1073 else
1074 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
1075 /* Null means no line feeds in buffer above current line. */
1077 if (prev_line == NULL)
1078 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
1079 prev_line = p = buffer;
1080 /* (else return NULL and read previous block) */
1082 /* Wrap downwards until too far, then use the one before. */
1083 while (p != NULL && p < cur_line) {
1084 prev_line = p;
1085 p = find_next_line(prev_line, NULL);
1088 if (BUFFER_OOB(prev_line))
1089 return NULL;
1091 return (unsigned char*) prev_line;
1094 static void check_bom(void)
1096 unsigned char bom[BOM_SIZE];
1097 off_t orig = rb->lseek(fd, 0, SEEK_CUR);
1099 is_bom = false;
1101 rb->lseek(fd, 0, SEEK_SET);
1103 if (rb->read(fd, bom, BOM_SIZE) == BOM_SIZE)
1104 is_bom = !memcmp(bom, BOM, BOM_SIZE);
1106 rb->lseek(fd, orig, SEEK_SET);
1109 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
1111 /* Read from file and preprocess the data */
1112 /* To minimize disk access, always read on sector boundaries */
1113 unsigned numread, i;
1114 bool found_CR = false;
1115 off_t offset = rb->lseek(fd, pos, SEEK_SET);
1117 if (offset == 0 && prefs.encoding == UTF_8 && is_bom)
1118 rb->lseek(fd, BOM_SIZE, SEEK_SET);
1120 numread = rb->read(fd, buf, size - 1);
1121 buf[numread] = 0;
1122 rb->button_clear_queue(); /* clear button queue */
1124 for(i = 0; i < numread; i++) {
1125 switch(buf[i]) {
1126 case '\r':
1127 if (mac_text) {
1128 buf[i] = 0;
1130 else {
1131 buf[i] = ' ';
1132 found_CR = true;
1134 break;
1136 case '\n':
1137 buf[i] = 0;
1138 found_CR = false;
1139 break;
1141 case 0: /* No break between case 0 and default, intentionally */
1142 buf[i] = ' ';
1143 default:
1144 if (found_CR) {
1145 buf[i - 1] = 0;
1146 found_CR = false;
1147 mac_text = true;
1149 break;
1154 static int viewer_find_bookmark(int page, int line)
1156 int i;
1158 for (i = 0; i < bookmark_count; i++)
1160 if (bookmarks[i].page == page && bookmarks[i].line == line)
1161 return i;
1163 return -1;
1166 static int read_and_synch(int direction)
1168 /* Read next (or prev) block, and reposition global pointers. */
1169 /* direction: 1 for down (i.e., further into file), -1 for up */
1170 int move_size, move_vector, offset;
1171 unsigned char *fill_buf;
1173 if (direction == -1) /* up */ {
1174 move_size = SMALL_BLOCK_SIZE;
1175 offset = 0;
1176 fill_buf = TOP_SECTOR;
1177 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1178 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
1180 else /* down */ {
1181 if (prefs.view_mode == WIDE) {
1182 /* WIDE mode needs more buffer so we have to read smaller blocks */
1183 move_size = SMALL_BLOCK_SIZE;
1184 offset = LARGE_BLOCK_SIZE;
1185 fill_buf = BOTTOM_SECTOR;
1186 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1187 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1189 else {
1190 move_size = LARGE_BLOCK_SIZE;
1191 offset = SMALL_BLOCK_SIZE;
1192 fill_buf = MID_SECTOR;
1193 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1196 move_vector = direction * move_size;
1197 screen_top_ptr -= move_vector;
1198 file_pos += move_vector;
1199 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1200 fill_buffer(file_pos + offset, fill_buf, move_size);
1201 return move_vector;
1204 static void get_next_line_position(unsigned char **line_begin,
1205 unsigned char **line_end,
1206 bool *is_short)
1208 int resynch_move;
1210 *line_begin = *line_end;
1211 *line_end = find_next_line(*line_begin, is_short);
1213 if (*line_end == NULL && !BUFFER_EOF())
1215 resynch_move = read_and_synch(1); /* Read block & move ptrs */
1216 *line_begin -= resynch_move;
1217 if (next_line_ptr > buffer)
1218 next_line_ptr -= resynch_move;
1220 *line_end = find_next_line(*line_begin, is_short);
1224 static void increment_current_line(void)
1226 if (cline < display_lines)
1227 cline++;
1228 else if (cpage < MAX_PAGE)
1230 cpage++;
1231 cline = 1;
1235 static void decrement_current_line(void)
1237 if (cline > 1)
1238 cline--;
1239 else if (cpage > 1)
1241 cpage--;
1242 cline = display_lines;
1246 static void viewer_scroll_up(void)
1248 unsigned char *p;
1250 p = find_prev_line(screen_top_ptr);
1251 if (p == NULL && !BUFFER_BOF()) {
1252 read_and_synch(-1);
1253 p = find_prev_line(screen_top_ptr);
1255 if (p != NULL)
1256 screen_top_ptr = p;
1258 decrement_current_line();
1261 static void viewer_scroll_down(bool autoscroll)
1263 if (cpage == lpage)
1264 return;
1266 if (next_line_ptr != NULL)
1267 screen_top_ptr = next_line_ptr;
1269 if (prefs.scroll_mode == LINE || autoscroll)
1270 increment_current_line();
1271 cline++;
1272 if (cline > display_lines) {
1273 cline = 1;
1274 cpage++;
1278 static void viewer_scroll_to_top_line(void)
1280 int line;
1282 for (line = cline; line > 1; line--)
1283 viewer_scroll_up();
1286 #ifdef HAVE_LCD_BITMAP
1287 static void viewer_scrollbar(void) {
1288 int items, min_shown, max_shown, sb_begin_y, sb_height;
1290 items = (int) file_size; /* (SH1 int is same as long) */
1291 min_shown = (int) file_pos + (screen_top_ptr - buffer);
1293 if (next_screen_ptr == NULL)
1294 max_shown = items;
1295 else
1296 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
1298 sb_begin_y = header_height;
1299 sb_height = LCD_HEIGHT - header_height - footer_height;
1301 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, sb_begin_y,
1302 SCROLLBAR_WIDTH-1, sb_height,
1303 items, min_shown, max_shown, VERTICAL);
1305 #endif
1307 #ifdef HAVE_LCD_BITMAP
1308 static void viewer_show_header(void)
1310 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1311 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1313 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1314 rb->lcd_putsxy(0, header_height - pf->height, file_name);
1317 static void viewer_show_footer(void)
1319 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1320 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1322 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1324 unsigned char buf[12];
1326 if (cline == 1)
1327 rb->snprintf(buf, sizeof(buf), "%d", cpage);
1328 else
1329 rb->snprintf(buf, sizeof(buf), "%d - %d", cpage, cpage+1);
1331 rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf);
1334 #endif
1336 static void viewer_draw(int col)
1338 int i, j, k, line_len, line_width, spaces, left_col=0;
1339 int width, extra_spaces, indent_spaces, spaces_per_word, spaces_width, disp_width = 0;
1340 bool multiple_spacing, line_is_short;
1341 unsigned short ch;
1342 unsigned char *str, *oldstr;
1343 unsigned char *line_begin;
1344 unsigned char *line_end;
1345 unsigned char c;
1346 int max_chars = max_columns * DIACRITIC_FACTOR;
1347 unsigned char scratch_buffer[max_chars * 4 + 1];
1348 unsigned char utf8_buffer[max_chars * 4 + 1];
1350 /* If col==-1 do all calculations but don't display */
1351 if (col != -1) {
1352 #ifdef HAVE_LCD_BITMAP
1353 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
1354 #else
1355 left_col = 0;
1356 #endif
1357 rb->lcd_clear_display();
1359 line_begin = line_end = screen_top_ptr;
1361 for (i = 0; i < display_lines; i++) {
1362 if (BUFFER_OOB(line_end))
1364 if (lpage == cpage)
1365 break; /* Happens after display last line at BUFFER_EOF() */
1367 if (lpage == 0 && cline == 1)
1369 lpage = cpage;
1370 last_screen_top_ptr = screen_top_ptr;
1371 last_file_pos = file_pos;
1375 get_next_line_position(&line_begin, &line_end, &line_is_short);
1376 if (line_end == NULL)
1378 if (BUFFER_OOB(line_begin))
1379 break;
1380 line_end = buffer_end + 1;
1383 line_len = line_end - line_begin;
1385 /* calculate line_len */
1386 str = oldstr = line_begin;
1387 j = -1;
1388 while (str < line_end) {
1389 oldstr = str;
1390 str = crop_at_width(str);
1391 j++;
1392 if (oldstr == str)
1394 oldstr = line_end;
1395 break;
1398 /* width of un-displayed part of the line */
1399 line_width = j*draw_columns;
1400 spaces_width = 0;
1401 while (oldstr < line_end) {
1402 oldstr = get_ucs(oldstr, &ch);
1403 /* add width of displayed part of the line */
1404 if (ch)
1406 int dw = glyph_width(ch);
1408 /* avoid counting spaces at the end of the line */
1409 if (ch == ' ')
1411 spaces_width += dw;
1413 else
1415 line_width += dw + spaces_width;
1416 spaces_width = 0;
1421 if (prefs.line_mode == JOIN) {
1422 if (line_begin[0] == 0) {
1423 line_begin++;
1424 if (prefs.word_mode == CHOP)
1425 line_end++;
1426 else
1427 line_len--;
1429 for (j=k=spaces=0; j < line_len; j++) {
1430 if (k == max_chars)
1431 break;
1433 c = line_begin[j];
1434 switch (c) {
1435 case ' ':
1436 spaces++;
1437 break;
1438 case 0:
1439 spaces = 0;
1440 scratch_buffer[k++] = ' ';
1441 break;
1442 default:
1443 while (spaces) {
1444 spaces--;
1445 scratch_buffer[k++] = ' ';
1446 if (k == max_chars - 1)
1447 break;
1449 scratch_buffer[k++] = c;
1450 break;
1453 scratch_buffer[k] = 0;
1455 else if (prefs.line_mode == REFLOW) {
1456 if (line_begin[0] == 0) {
1457 line_begin++;
1458 if (prefs.word_mode == CHOP)
1459 line_end++;
1460 else
1461 line_len--;
1464 indent_spaces = 0;
1465 if (!line_is_short) {
1466 multiple_spacing = false;
1467 width=spaces=0;
1468 for (str = line_begin; str < line_end; ) {
1469 str = get_ucs(str, &ch);
1470 switch (ch) {
1471 case 0:
1472 case ' ':
1473 if (str == line_begin)
1475 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1477 /* special case: indent the paragraph,
1478 * don't count spaces */
1479 indent_spaces = par_indent_spaces;
1482 else if (!multiple_spacing)
1483 spaces++;
1484 multiple_spacing = true;
1485 break;
1486 default:
1487 multiple_spacing = false;
1488 width += glyph_width(ch);
1489 break;
1492 if (multiple_spacing) spaces--;
1494 if (spaces) {
1495 /* total number of spaces to insert between words */
1496 extra_spaces = (max_width-width)/glyph_width(' ')
1497 - indent_spaces;
1498 /* number of spaces between each word*/
1499 spaces_per_word = extra_spaces / spaces;
1500 /* number of words with n+1 spaces (to fill up) */
1501 extra_spaces = extra_spaces % spaces;
1502 if (spaces_per_word > 2) { /* too much spacing is awful */
1503 spaces_per_word = 3;
1504 extra_spaces = 0;
1506 } else { /* this doesn't matter much... no spaces anyway */
1507 spaces_per_word = extra_spaces = 0;
1509 } else { /* end of a paragraph: don't fill line */
1510 spaces_per_word = 1;
1511 extra_spaces = 0;
1513 multiple_spacing = false;
1514 for (j=k=spaces=0; j < line_len; j++) {
1515 if (k == max_chars)
1516 break;
1518 c = line_begin[j];
1519 switch (c) {
1520 case 0:
1521 if (j == line_len - 1)
1522 break;
1523 case ' ':
1524 if (j==0) {
1525 /* indent paragraph */
1526 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1528 for (j=0; j<par_indent_spaces; j++)
1529 scratch_buffer[k++] = ' ';
1530 j=0;
1533 else if (!multiple_spacing) {
1534 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1535 scratch_buffer[k++] = ' ';
1536 spaces++;
1538 multiple_spacing = true;
1539 break;
1540 default:
1541 scratch_buffer[k++] = c;
1542 multiple_spacing = false;
1543 break;
1546 while (scratch_buffer[k-1] == ' ')
1547 k--;
1548 scratch_buffer[k] = 0;
1550 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1551 if (col != -1)
1553 rb->memcpy(scratch_buffer, line_begin, line_len);
1554 scratch_buffer[line_len] = '\0';
1558 /* create displayed line */
1559 if (col != -1)
1561 decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
1562 rb->lcd_getstringsize(utf8_buffer, &disp_width, NULL);
1565 /* display on screen the displayed part of the line */
1566 if (col != -1)
1568 bool in_page = (cline+i <= display_lines);
1569 int dpage = cpage + (in_page ? 0 : 1);
1570 int dline = cline + i - (in_page ? 0 : display_lines);
1571 bool bflag = (viewer_find_bookmark(dpage, dline) >= 0);
1572 #ifdef HAVE_LCD_BITMAP
1573 int dy = i * pf->height + header_height;
1574 int dx = (prefs.alignment == LEFT)? left_col : LCD_WIDTH - disp_width;
1575 #endif
1576 if (bflag)
1577 #ifdef HAVE_LCD_BITMAP
1579 rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG);
1580 rb->lcd_fillrect(left_col, dy, LCD_WIDTH - left_col, pf->height);
1581 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1583 rb->lcd_putsxy(dx, dy, utf8_buffer);
1584 rb->lcd_set_drawmode(DRMODE_SOLID);
1585 #else
1587 rb->lcd_puts(left_col, i, BOOKMARK_ICON);
1589 rb->lcd_puts(left_col+1, i, utf8_buffer);
1590 #endif
1593 if (i == 0)
1594 next_line_ptr = line_end;
1596 next_screen_ptr = line_end;
1597 if (BUFFER_OOB(next_screen_ptr))
1598 next_screen_ptr = NULL;
1600 #ifdef HAVE_LCD_BITMAP
1601 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1603 if (prefs.need_scrollbar)
1604 viewer_scrollbar();
1605 #else
1606 next_screen_to_draw_ptr = next_screen_ptr;
1607 #endif
1609 #ifdef HAVE_LCD_BITMAP
1610 /* show header */
1611 viewer_show_header();
1613 /* show footer */
1614 viewer_show_footer();
1615 #endif
1617 if (col != -1)
1618 rb->lcd_update();
1621 static void viewer_top(void)
1623 /* Read top of file into buffer
1624 and point screen pointer to top */
1625 if (file_pos != 0)
1627 rb->splash(0, "Loading...");
1629 file_pos = 0;
1630 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1631 fill_buffer(0, buffer, buffer_size);
1634 screen_top_ptr = buffer;
1635 cpage = 1;
1636 cline = 1;
1639 static void viewer_bottom(void)
1641 unsigned char *line_begin;
1642 unsigned char *line_end;
1644 rb->splash(0, "Loading...");
1646 if (last_screen_top_ptr)
1648 cpage = lpage;
1649 cline = 1;
1650 screen_top_ptr = last_screen_top_ptr;
1651 file_pos = last_file_pos;
1652 fill_buffer(file_pos, buffer, buffer_size);
1653 buffer_end = BUFFER_END();
1654 return;
1657 line_end = screen_top_ptr;
1659 while (!BUFFER_EOF() || !BUFFER_OOB(line_end))
1661 get_next_line_position(&line_begin, &line_end, NULL);
1662 if (line_end == NULL)
1663 break;
1665 increment_current_line();
1666 if (cline == 1)
1667 screen_top_ptr = line_end;
1669 lpage = cpage;
1670 cline = 1;
1671 last_screen_top_ptr = screen_top_ptr;
1672 last_file_pos = file_pos;
1673 buffer_end = BUFFER_END();
1676 #ifdef HAVE_LCD_BITMAP
1677 static void init_need_scrollbar(void) {
1678 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1679 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1680 viewer_draw(-1);
1681 prefs.need_scrollbar = NEED_SCROLLBAR();
1682 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1683 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1684 calc_max_width();
1687 static void init_header_and_footer(void)
1689 header_height = 0;
1690 footer_height = 0;
1691 if (rb->global_settings->statusbar == STATUSBAR_TOP)
1693 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1694 header_height = STATUSBAR_HEIGHT;
1696 if (prefs.footer_mode == FT_SBAR)
1697 prefs.footer_mode = FT_NONE;
1698 else if (prefs.footer_mode == FT_BOTH)
1699 prefs.footer_mode = FT_PAGE;
1701 else if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
1703 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1704 footer_height = STATUSBAR_HEIGHT;
1706 if (prefs.header_mode == HD_SBAR)
1707 prefs.header_mode = HD_NONE;
1708 else if (prefs.header_mode == HD_BOTH)
1709 prefs.header_mode = HD_PATH;
1711 else /* STATUSBAR_OFF || STATUSBAR_CUSTOM */
1713 if (prefs.header_mode == HD_SBAR)
1714 prefs.header_mode = HD_NONE;
1715 else if (prefs.header_mode == HD_BOTH)
1716 prefs.header_mode = HD_PATH;
1718 if (prefs.footer_mode == FT_SBAR)
1719 prefs.footer_mode = FT_NONE;
1720 else if (prefs.footer_mode == FT_BOTH)
1721 prefs.footer_mode = FT_PAGE;
1724 if (prefs.header_mode == HD_NONE || prefs.header_mode == HD_PATH ||
1725 prefs.footer_mode == FT_NONE || prefs.footer_mode == FT_PAGE)
1726 rb->gui_syncstatusbar_draw(rb->statusbars, false);
1728 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1729 header_height += pf->height;
1730 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1731 footer_height += pf->height;
1733 display_lines = (LCD_HEIGHT - header_height - footer_height) / pf->height;
1735 lpage = 0;
1736 last_file_pos = 0;
1737 last_screen_top_ptr = NULL;
1740 static bool change_font(unsigned char *font)
1742 unsigned char buf[MAX_PATH];
1744 if (font == NULL || *font == '\0')
1745 return false;
1747 rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font);
1748 if (rb->font_load(NULL, buf) < 0) {
1749 rb->splash(HZ/2, "Font load failed.");
1750 return false;
1753 return true;
1755 #endif
1757 static bool viewer_init(void)
1759 #ifdef HAVE_LCD_BITMAP
1760 /* initialize fonts */
1761 pf = rb->font_get(FONT_UI);
1762 if (pf == NULL)
1763 return false;
1765 draw_columns = display_columns = LCD_WIDTH;
1766 #else
1767 /* REAL fixed pitch :) all chars use up 1 cell */
1768 display_lines = 2;
1769 draw_columns = display_columns = 11;
1770 par_indent_spaces = 2;
1771 #endif
1773 fd = rb->open(file_name, O_RDONLY);
1774 if (fd < 0)
1775 return false;
1777 /* Init mac_text value used in processing buffer */
1778 mac_text = false;
1780 return true;
1783 /* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8,
1784 * then file size decreases only BOM_SIZE.
1786 static void get_filesize(void)
1788 file_size = rb->filesize(fd);
1789 if (file_size == -1)
1790 return;
1792 if (prefs.encoding == UTF_8 && is_bom)
1793 file_size -= BOM_SIZE;
1796 static int bm_comp(const void *a, const void *b)
1798 struct bookmark_info *pa;
1799 struct bookmark_info *pb;
1801 pa = (struct bookmark_info*)a;
1802 pb = (struct bookmark_info*)b;
1804 if (pa->page != pb->page)
1805 return pa->page - pb->page;
1807 return pa->line - pb->line;
1810 static void viewer_add_bookmark(void)
1812 if (bookmark_count >= MAX_BOOKMARKS-1)
1813 return;
1815 bookmarks[bookmark_count].file_position
1816 = file_pos + screen_top_ptr - buffer;
1817 bookmarks[bookmark_count].page = cpage;
1818 bookmarks[bookmark_count].line = cline;
1819 bookmarks[bookmark_count].flag = BOOKMARK_USER;
1820 bookmark_count++;
1823 static int viewer_add_last_read_bookmark(void)
1825 int i;
1827 i = viewer_find_bookmark(cpage, cline);
1828 if (i >= 0)
1829 bookmarks[i].flag |= BOOKMARK_LAST;
1830 else
1832 viewer_add_bookmark();
1833 i = bookmark_count-1;
1834 bookmarks[i].flag = BOOKMARK_LAST;
1836 return i;
1839 static void viewer_remove_bookmark(int i)
1841 int j;
1843 if (i < 0 || i >= bookmark_count)
1844 return;
1846 for (j = i+1; j < bookmark_count; j++)
1847 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1848 sizeof(struct bookmark_info));
1850 bookmark_count--;
1853 static void viewer_remove_last_read_bookmark(void)
1855 int i, j;
1857 for (i = 0; i < bookmark_count; i++)
1859 if (bookmarks[i].flag & BOOKMARK_LAST)
1861 if (bookmarks[i].flag == BOOKMARK_LAST)
1863 for (j = i+1; j < bookmark_count; j++)
1864 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1865 sizeof(struct bookmark_info));
1867 bookmark_count--;
1869 else
1870 bookmarks[i].flag = BOOKMARK_USER;
1871 break;
1876 static int viewer_get_last_read_bookmark(void)
1878 int i;
1880 for (i = 0; i < bookmark_count; i++)
1882 if (bookmarks[i].flag & BOOKMARK_LAST)
1883 return i;
1885 return -1;
1888 static void viewer_select_bookmark(int initval)
1890 int i;
1891 int ipage = 0;
1892 int iline = 0;
1893 int screen_pos;
1894 int screen_top;
1895 int selected = -1;
1897 struct opt_items items[bookmark_count];
1898 unsigned char names[bookmark_count][38];
1900 if (initval >= 0 && initval < bookmark_count)
1902 ipage = bookmarks[initval].page;
1903 iline = bookmarks[initval].line;
1906 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
1907 bm_comp);
1909 for (i = 0; i < bookmark_count; i++)
1911 rb->snprintf(names[i], sizeof(names[0]),
1912 #if CONFIG_KEYPAD != PLAYER_PAD
1913 "%sPage: %d Line: %d",
1914 #else
1915 "%sP:%d L:%d",
1916 #endif
1917 (bookmarks[i].flag&BOOKMARK_LAST)? "*":" ",
1918 bookmarks[i].page,
1919 bookmarks[i].line);
1920 items[i].string = names[i];
1921 items[i].voice_id = -1;
1922 if (selected < 0 && bookmarks[i].page == ipage && bookmarks[i].line == iline)
1923 selected = i;
1926 rb->set_option("Select bookmark", &selected, INT, items,
1927 sizeof(items) / sizeof(items[0]), NULL);
1929 if (selected < 0 || selected >= bookmark_count)
1931 if (initval < 0 || (selected = viewer_get_last_read_bookmark()) < 0)
1933 if (initval < 0)
1934 rb->splash(HZ, "Start the first page.");
1935 file_pos = 0;
1936 screen_top_ptr = buffer;
1937 cpage = 1;
1938 cline = 1;
1939 buffer_end = BUFFER_END();
1940 return;
1944 screen_pos = bookmarks[selected].file_position;
1945 screen_top = screen_pos % buffer_size;
1946 file_pos = screen_pos - screen_top;
1947 screen_top_ptr = buffer + screen_top;
1948 cpage = bookmarks[selected].page;
1949 cline = bookmarks[selected].line;
1950 buffer_end = BUFFER_END();
1953 static void viewer_default_preferences(void)
1955 prefs.word_mode = WRAP;
1956 prefs.line_mode = NORMAL;
1957 prefs.view_mode = NARROW;
1958 prefs.alignment = LEFT;
1959 prefs.scroll_mode = PAGE;
1960 prefs.page_mode = NO_OVERLAP;
1961 prefs.scrollbar_mode = SB_OFF;
1962 rb->memset(prefs.font, 0, MAX_PATH);
1963 #ifdef HAVE_LCD_BITMAP
1964 prefs.header_mode = HD_BOTH;
1965 prefs.footer_mode = FT_BOTH;
1966 rb->snprintf(prefs.font, MAX_PATH, "%s", rb->global_settings->font_file);
1967 #else
1968 prefs.header_mode = HD_NONE;
1969 prefs.footer_mode = FT_NONE;
1970 #endif
1971 prefs.autoscroll_speed = 1;
1972 /* Set codepage to system default */
1973 prefs.encoding = rb->global_settings->default_codepage;
1976 static bool viewer_read_preferences(int pfd, int version, struct preferences *prf)
1978 unsigned char buf[PREFERENCES_SIZE];
1979 unsigned char *p = buf;
1980 int read_size = PREFERENCES_SIZE;
1982 if (version == 0)
1983 read_size--;
1985 if (rb->read(pfd, buf, read_size) != read_size)
1986 return false;
1988 prf->word_mode = *p++;
1989 prf->line_mode = *p++;
1990 prf->view_mode = *p++;
1991 if (version > 0)
1992 prf->alignment = *p++;
1993 else
1994 prf->alignment = LEFT;
1995 prf->encoding = *p++;
1996 prf->scrollbar_mode = *p++;
1997 prf->need_scrollbar = *p++;
1998 prf->page_mode = *p++;
1999 prf->header_mode = *p++;
2000 prf->footer_mode = *p++;
2001 prf->scroll_mode = *p++;
2002 prf->autoscroll_speed = *p++;
2003 rb->memcpy(prf->font, p, MAX_PATH);
2004 return true;
2007 static bool viewer_write_preferences(int pfd, const struct preferences *prf)
2009 unsigned char buf[PREFERENCES_SIZE];
2010 unsigned char *p = buf;
2012 *p++ = prf->word_mode;
2013 *p++ = prf->line_mode;
2014 *p++ = prf->view_mode;
2015 *p++ = prf->alignment;
2016 *p++ = prf->encoding;
2017 *p++ = prf->scrollbar_mode;
2018 *p++ = prf->need_scrollbar;
2019 *p++ = prf->page_mode;
2020 *p++ = prf->header_mode;
2021 *p++ = prf->footer_mode;
2022 *p++ = prf->scroll_mode;
2023 *p++ = prf->autoscroll_speed;
2024 rb->memcpy(p, prf->font, MAX_PATH);
2026 return (rb->write(pfd, buf, sizeof(buf)) == sizeof(buf));
2029 static bool viewer_read_bookmark_info(int bfd, struct bookmark_info *b)
2031 unsigned char buf[BOOKMARK_SIZE];
2033 if (rb->read(bfd, buf, sizeof(buf)) != sizeof(buf))
2034 return false;
2036 b->file_position = (buf[0] << 24)|(buf[1] << 16)|(buf[2] << 8)|buf[3];
2037 b->page = (buf[4] << 8)|buf[5];
2038 b->line = buf[6];
2039 b->flag = buf[7];
2041 return true;
2044 static bool viewer_read_bookmark_infos(int bfd)
2046 unsigned char c;
2047 int i;
2049 if (rb->read(bfd, &c, 1) != 1)
2051 bookmark_count = 0;
2052 return false;
2055 bookmark_count = c;
2056 if (bookmark_count > MAX_BOOKMARKS)
2057 bookmark_count = MAX_BOOKMARKS;
2059 for (i = 0; i < bookmark_count; i++)
2061 if (!viewer_read_bookmark_info(bfd, &bookmarks[i]))
2063 bookmark_count = i;
2064 return false;
2067 return true;
2070 static bool viewer_write_bookmark_info(int bfd, struct bookmark_info *b)
2072 unsigned char buf[BOOKMARK_SIZE];
2073 unsigned char *p = buf;
2074 unsigned long ul;
2076 ul = b->file_position;
2077 *p++ = ul >> 24;
2078 *p++ = ul >> 16;
2079 *p++ = ul >> 8;
2080 *p++ = ul;
2082 ul = b->page;
2083 *p++ = ul >> 8;
2084 *p++ = ul;
2086 *p++ = b->line;
2087 *p = b->flag;
2089 return (rb->write(bfd, buf, sizeof(buf)) == sizeof(buf));
2092 static bool viewer_write_bookmark_infos(int bfd)
2094 unsigned char c = bookmark_count;
2095 int i;
2097 if (rb->write(bfd, &c, 1) != 1)
2098 return false;
2100 for (i = 0; i < bookmark_count; i++)
2102 if (!viewer_write_bookmark_info(bfd, &bookmarks[i]))
2103 return false;
2106 return true;
2109 static bool viewer_load_global_settings(void)
2111 unsigned buf[GLOBAL_SETTINGS_H_SIZE];
2112 int sfd = rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY);
2113 int version;
2114 bool res = false;
2116 if (sfd < 0)
2117 return false;
2119 if ((rb->read(sfd, buf, GLOBAL_SETTINGS_H_SIZE) == GLOBAL_SETTINGS_H_SIZE) ||
2120 (rb->memcmp(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1) == 0))
2122 version = buf[GLOBAL_SETTINGS_H_SIZE - 1] - GLOBAL_SETTINGS_FIRST_VERSION;
2123 res = viewer_read_preferences(sfd, version, &prefs);
2125 rb->close(sfd);
2126 return res;
2129 static bool viewer_save_global_settings(void)
2131 int sfd = rb->open(GLOBAL_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC);
2132 unsigned char buf[GLOBAL_SETTINGS_H_SIZE];
2134 if (sfd < 0)
2135 return false;
2137 rb->memcpy(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1);
2138 buf[GLOBAL_SETTINGS_H_SIZE - 1] = GLOBAL_SETTINGS_VERSION;
2140 if (rb->write(sfd, buf, GLOBAL_SETTINGS_H_SIZE) != GLOBAL_SETTINGS_H_SIZE ||
2141 !viewer_write_preferences(sfd, &prefs))
2143 rb->close(sfd);
2144 rb->remove(GLOBAL_SETTINGS_TMP_FILE);
2145 return false;
2147 rb->close(sfd);
2148 rb->remove(GLOBAL_SETTINGS_FILE);
2149 rb->rename(GLOBAL_SETTINGS_TMP_FILE, GLOBAL_SETTINGS_FILE);
2150 return true;
2153 static bool viewer_convert_settings(int sfd, int dfd, int old_ver)
2155 struct preferences new_prefs;
2156 off_t old_pos;
2157 off_t new_pos;
2158 unsigned char buf[MAX_PATH + 2];
2159 int settings_size;
2161 rb->read(sfd, buf, MAX_PATH + 2);
2162 rb->write(dfd, buf, MAX_PATH + 2);
2164 settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
2166 old_pos = rb->lseek(sfd, 0, SEEK_CUR);
2167 new_pos = rb->lseek(dfd, 0, SEEK_CUR);
2170 * when the settings size != preferences size + bookmarks size,
2171 * settings data are considered to be old version.
2173 if (old_ver > 0 && ((settings_size - PREFERENCES_SIZE) % 8) == 0)
2174 old_ver = 0;
2176 if (!viewer_read_preferences(sfd, old_ver, &new_prefs))
2177 return false;
2179 if (!viewer_write_preferences(dfd, &new_prefs))
2180 return false;
2182 settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos);
2184 if (settings_size > 0)
2186 rb->read(sfd, buf, settings_size);
2187 rb->write(dfd, buf, settings_size);
2190 settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos;
2191 buf[0] = settings_size >> 8;
2192 buf[1] = settings_size;
2193 rb->lseek(dfd, new_pos - 2, SEEK_SET);
2194 rb->write(dfd, buf, 2);
2195 rb->lseek(dfd, settings_size, SEEK_CUR);
2196 return true;
2199 static bool viewer_convert_settings_file(void)
2201 unsigned char buf[SETTINGS_H_SIZE+2];
2202 int sfd;
2203 int tfd;
2204 int i;
2205 int fcount;
2206 int version;
2207 bool res = true;
2209 if ((sfd = rb->open(SETTINGS_FILE, O_RDONLY)) < 0)
2210 return false;
2212 if ((tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
2214 rb->close(sfd);
2215 return false;
2218 rb->read(sfd, buf, SETTINGS_H_SIZE + 2);
2220 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2221 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE + 1];
2222 buf[SETTINGS_H_SIZE - 1] = SETTINGS_VERSION;
2224 rb->write(tfd, buf, SETTINGS_H_SIZE + 2);
2226 for (i = 0; i < fcount; i++)
2228 if (!viewer_convert_settings(sfd, tfd, version))
2230 res = false;
2231 break;
2235 rb->close(sfd);
2236 rb->close(tfd);
2238 if (!res)
2239 rb->remove(SETTINGS_TMP_FILE);
2240 else
2242 rb->remove(SETTINGS_FILE);
2243 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2245 return res;
2248 static bool viewer_load_settings(void)
2250 unsigned char buf[MAX_PATH+2];
2251 unsigned int fcount;
2252 unsigned int i;
2253 bool res = false;
2254 int sfd;
2255 unsigned int size;
2256 int version;
2258 sfd = rb->open(SETTINGS_FILE, O_RDONLY);
2259 if (sfd < 0)
2260 goto read_end;
2262 if ((rb->read(sfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2263 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2265 /* illegal setting file */
2266 rb->close(sfd);
2268 if (rb->file_exists(SETTINGS_FILE))
2269 rb->remove(SETTINGS_FILE);
2271 goto read_end;
2274 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2276 rb->close(sfd);
2277 if (!viewer_convert_settings_file())
2278 goto read_end;
2280 return viewer_load_settings();
2283 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2284 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2285 for (i = 0; i < fcount; i++)
2287 if (rb->read(sfd, buf, MAX_PATH+2) != MAX_PATH+2)
2288 break;
2290 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2293 * when the settings size != preferences size + bookmarks size,
2294 * the settings file converts to the newer.
2296 if (version > 0 && ((size - PREFERENCES_SIZE) % 8) == 0)
2298 rb->close(sfd);
2299 if (!viewer_convert_settings_file())
2300 break;
2302 return viewer_load_settings();
2305 if (rb->strcmp(buf, file_name))
2307 if (rb->lseek(sfd, size, SEEK_CUR) < 0)
2308 break;
2309 continue;
2311 if (!viewer_read_preferences(sfd, version, &prefs))
2312 break;
2314 res = viewer_read_bookmark_infos(sfd);
2315 break;
2318 rb->close(sfd);
2320 read_end:
2321 if (!res)
2323 /* load global settings */
2324 if (!viewer_load_global_settings())
2325 viewer_default_preferences();
2327 file_pos = 0;
2328 screen_top_ptr = buffer;
2329 cpage = 1;
2330 cline = 1;
2331 bookmark_count = 0;
2334 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
2335 calc_max_width();
2337 if (bookmark_count > 1)
2338 viewer_select_bookmark(-1);
2339 else if (bookmark_count == 1)
2341 int screen_pos;
2342 int screen_top;
2344 screen_pos = bookmarks[0].file_position;
2345 screen_top = screen_pos % buffer_size;
2346 file_pos = screen_pos - screen_top;
2347 screen_top_ptr = buffer + screen_top;
2348 cpage = bookmarks[0].page;
2349 cline = bookmarks[0].line;
2352 viewer_remove_last_read_bookmark();
2354 check_bom();
2355 get_filesize();
2357 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
2359 if (BUFFER_OOB(screen_top_ptr))
2360 screen_top_ptr = buffer;
2362 fill_buffer(file_pos, buffer, buffer_size);
2363 if (prefs.scroll_mode == PAGE && cline > 1)
2364 viewer_scroll_to_top_line();
2366 /* remember the current position */
2367 start_position = file_pos + screen_top_ptr - buffer;
2369 #ifdef HAVE_LCD_BITMAP
2370 /* load prefs font if it is different than the global settings font */
2371 if (rb->strcmp(prefs.font, rb->global_settings->font_file)) {
2372 if (!change_font(prefs.font)) {
2373 /* fallback by resetting prefs font to the global settings font */
2374 rb->memset(prefs.font, 0, MAX_PATH);
2375 rb->snprintf(prefs.font, MAX_PATH, "%s",
2376 rb->global_settings->font_file);
2378 if (!change_font(prefs.font))
2379 return false;
2383 init_need_scrollbar();
2384 init_header_and_footer();
2385 #endif
2387 return true;
2390 static bool copy_bookmark_file(int sfd, int dfd, off_t start, off_t size)
2392 off_t rsize;
2394 if (rb->lseek(sfd, start, SEEK_SET) < 0)
2395 return false;
2397 while (size > 0)
2399 if (size > buffer_size)
2400 rsize = buffer_size;
2401 else
2402 rsize = size;
2403 size -= rsize;
2405 if (rb->read(sfd, buffer, rsize) != rsize ||
2406 rb->write(dfd, buffer, rsize) != rsize)
2407 return false;
2409 return true;
2412 static bool viewer_save_settings(void)
2414 unsigned char buf[MAX_PATH+2];
2415 unsigned int fcount = 0;
2416 unsigned int i;
2417 int idx;
2418 int ofd;
2419 int tfd;
2420 off_t first_copy_size = 0;
2421 off_t second_copy_start_pos = 0;
2422 off_t size;
2424 /* add reading page to bookmarks */
2425 idx = viewer_find_bookmark(cpage, cline);
2426 if (idx >= 0)
2427 bookmarks[idx].flag |= BOOKMARK_LAST;
2428 else
2430 viewer_add_bookmark();
2431 bookmarks[bookmark_count-1].flag = BOOKMARK_LAST;
2434 tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC);
2435 if (tfd < 0)
2436 return false;
2438 ofd = rb->open(SETTINGS_FILE, O_RDWR);
2439 if (ofd >= 0)
2441 if ((rb->read(ofd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2442 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2444 rb->close(ofd);
2445 goto save_err;
2448 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2450 rb->close(ofd);
2451 if (!viewer_convert_settings_file())
2452 goto save_err;
2454 viewer_save_settings();
2457 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2459 for (i = 0; i < fcount; i++)
2461 if (rb->read(ofd, buf, MAX_PATH+2) != MAX_PATH+2)
2463 rb->close(ofd);
2464 goto save_err;
2466 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2467 if (rb->strcmp(buf, file_name))
2469 if (rb->lseek(ofd, size, SEEK_CUR) < 0)
2471 rb->close(ofd);
2472 goto save_err;
2475 else
2477 first_copy_size = rb->lseek(ofd, 0, SEEK_CUR);
2478 if (first_copy_size < 0)
2480 rb->close(ofd);
2481 goto save_err;
2483 second_copy_start_pos = first_copy_size + size;
2484 first_copy_size -= MAX_PATH+2;
2485 fcount--;
2486 break;
2489 if (first_copy_size == 0)
2490 first_copy_size = rb->filesize(ofd);
2492 if (!copy_bookmark_file(ofd, tfd, 0, first_copy_size))
2494 rb->close(ofd);
2495 goto save_err;
2497 if (second_copy_start_pos > 0)
2499 if (!copy_bookmark_file(ofd, tfd, second_copy_start_pos,
2500 rb->filesize(ofd) - second_copy_start_pos))
2502 rb->close(ofd);
2503 goto save_err;
2506 rb->close(ofd);
2508 else
2510 rb->memcpy(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1);
2511 buf[SETTINGS_H_SIZE-1] = SETTINGS_VERSION;
2512 buf[SETTINGS_H_SIZE ] = 0;
2513 buf[SETTINGS_H_SIZE+1] = 0;
2514 if (rb->write(tfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2)
2515 goto save_err;
2518 /* copy to current read file's bookmarks */
2519 rb->memset(buf, 0, MAX_PATH);
2520 rb->snprintf(buf, MAX_PATH, "%s", file_name);
2522 size = PREFERENCES_SIZE + bookmark_count * BOOKMARK_SIZE + 1;
2523 buf[MAX_PATH] = size >> 8;
2524 buf[MAX_PATH+1] = size;
2526 if (rb->write(tfd, buf, MAX_PATH+2) != MAX_PATH+2)
2527 goto save_err;
2529 if (!viewer_write_preferences(tfd, &prefs))
2530 goto save_err;
2532 if (!viewer_write_bookmark_infos(tfd))
2533 goto save_err;
2535 if (rb->lseek(tfd, SETTINGS_H_SIZE, SEEK_SET) < 0)
2536 goto save_err;
2538 fcount++;
2539 buf[0] = fcount >> 8;
2540 buf[1] = fcount;
2542 if (rb->write(tfd, buf, 2) != 2)
2543 goto save_err;
2545 rb->close(tfd);
2547 rb->remove(SETTINGS_FILE);
2548 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2550 return true;
2552 save_err:
2553 rb->close(tfd);
2554 rb->remove(SETTINGS_TMP_FILE);
2555 return false;
2558 static void viewer_exit(void *parameter)
2560 (void)parameter;
2562 /* save preference and bookmarks */
2563 if (!viewer_save_settings())
2564 rb->splash(HZ, "Can't save preference and bookmarks.");
2566 rb->close(fd);
2567 #ifdef HAVE_LCD_BITMAP
2568 if (rb->strcmp(prefs.font, rb->global_settings->font_file))
2569 change_font(rb->global_settings->font_file);
2570 #endif
2573 static void calc_page(void)
2575 int i;
2576 unsigned char *line_begin;
2577 unsigned char *line_end;
2578 off_t sfp;
2579 unsigned char *sstp;
2581 rb->splash(0, "Calculating page/line number...");
2583 /* add reading page to bookmarks */
2584 viewer_add_last_read_bookmark();
2586 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
2587 bm_comp);
2589 cpage = 1;
2590 cline = 1;
2591 file_pos = 0;
2592 screen_top_ptr = buffer;
2593 buffer_end = BUFFER_END();
2595 fill_buffer(file_pos, buffer, buffer_size);
2596 line_end = line_begin = buffer;
2598 /* update page and line of all bookmark */
2599 for (i = 0; i < bookmark_count; i++)
2601 sfp = bookmarks[i].file_position;
2602 sstp = buffer;
2604 while ((line_begin > sstp || sstp >= line_end) ||
2605 (file_pos > sfp || sfp >= file_pos + BUFFER_END() - buffer))
2607 get_next_line_position(&line_begin, &line_end, NULL);
2608 if (line_end == NULL)
2609 break;
2611 next_line_ptr = line_end;
2613 if (sstp == buffer &&
2614 file_pos <= sfp && sfp < file_pos + BUFFER_END() - buffer)
2615 sstp = sfp - file_pos + buffer;
2617 increment_current_line();
2620 decrement_current_line();
2621 bookmarks[i].page = cpage;
2622 bookmarks[i].line = cline;
2623 bookmarks[i].file_position = file_pos + (line_begin - buffer);
2624 increment_current_line();
2627 /* remove reading page's bookmark */
2628 for (i = 0; i < bookmark_count; i++)
2630 if (bookmarks[i].flag & BOOKMARK_LAST)
2632 int screen_pos;
2633 int screen_top;
2635 screen_pos = bookmarks[i].file_position;
2636 screen_top = screen_pos % buffer_size;
2637 file_pos = screen_pos - screen_top;
2638 screen_top_ptr = buffer + screen_top;
2640 cpage = bookmarks[i].page;
2641 cline = bookmarks[i].line;
2642 bookmarks[i].flag ^= BOOKMARK_LAST;
2643 buffer_end = BUFFER_END();
2645 fill_buffer(file_pos, buffer, buffer_size);
2647 if (bookmarks[i].flag == 0)
2648 viewer_remove_bookmark(i);
2650 if (prefs.scroll_mode == PAGE && cline > 1)
2651 viewer_scroll_to_top_line();
2652 break;
2657 static int col_limit(int col)
2659 if (col < 0)
2660 col = 0;
2661 else
2662 if (col >= max_width - draw_columns)
2663 col = max_width - draw_columns;
2665 return col;
2668 /* settings helper functions */
2670 static bool encoding_setting(void)
2672 static struct opt_items names[NUM_CODEPAGES];
2673 int idx;
2674 bool res;
2675 enum codepages oldenc = prefs.encoding;
2677 for (idx = 0; idx < NUM_CODEPAGES; idx++)
2679 names[idx].string = rb->get_codepage_name(idx);
2680 names[idx].voice_id = -1;
2683 res = rb->set_option("Encoding", &prefs.encoding, INT, names,
2684 sizeof(names) / sizeof(names[0]), NULL);
2686 /* When prefs.encoding changes into UTF-8 or changes from UTF-8,
2687 * filesize (file_size) might change.
2688 * In addition, if prefs.encoding is UTF-8, then BOM does not read.
2690 if (oldenc != prefs.encoding && (oldenc == UTF_8 || prefs.encoding == UTF_8))
2692 check_bom();
2693 get_filesize();
2694 fill_buffer(file_pos, buffer, buffer_size);
2697 return res;
2700 static bool word_wrap_setting(void)
2702 static const struct opt_items names[] = {
2703 {"On", -1},
2704 {"Off (Chop Words)", -1},
2707 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
2708 names, 2, NULL);
2711 static bool line_mode_setting(void)
2713 static const struct opt_items names[] = {
2714 {"Normal", -1},
2715 {"Join Lines", -1},
2716 {"Expand Lines", -1},
2717 #ifdef HAVE_LCD_BITMAP
2718 {"Reflow Lines", -1},
2719 #endif
2722 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
2723 sizeof(names) / sizeof(names[0]), NULL);
2726 static bool view_mode_setting(void)
2728 static const struct opt_items names[] = {
2729 {"No (Narrow)", -1},
2730 {"Yes", -1},
2732 bool ret;
2733 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
2734 names , 2, NULL);
2735 if (prefs.view_mode == NARROW)
2736 col = 0;
2737 calc_max_width();
2738 return ret;
2741 static bool scroll_mode_setting(void)
2743 static const struct opt_items names[] = {
2744 {"Scroll by Page", -1},
2745 {"Scroll by Line", -1},
2748 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
2749 names, 2, NULL);
2752 #ifdef HAVE_LCD_BITMAP
2753 static bool page_mode_setting(void)
2755 static const struct opt_items names[] = {
2756 {"No", -1},
2757 {"Yes", -1},
2760 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
2761 names, 2, NULL);
2764 static bool scrollbar_setting(void)
2766 static const struct opt_items names[] = {
2767 {"Off", -1},
2768 {"On", -1}
2771 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
2772 names, 2, NULL);
2775 static bool header_setting(void)
2777 int len = (rb->global_settings->statusbar == STATUSBAR_TOP)? 4 : 2;
2778 struct opt_items names[len];
2780 names[0].string = "None";
2781 names[0].voice_id = -1;
2782 names[1].string = "File path";
2783 names[1].voice_id = -1;
2785 if (rb->global_settings->statusbar == STATUSBAR_TOP)
2787 names[2].string = "Status bar";
2788 names[2].voice_id = -1;
2789 names[3].string = "Both";
2790 names[3].voice_id = -1;
2793 return rb->set_option("Show Header", &prefs.header_mode, INT,
2794 names, len, NULL);
2797 static bool footer_setting(void)
2799 int len = (rb->global_settings->statusbar == STATUSBAR_BOTTOM)? 4 : 2;
2800 struct opt_items names[len];
2802 names[0].string = "None";
2803 names[0].voice_id = -1;
2804 names[1].string = "Page Num";
2805 names[1].voice_id = -1;
2807 if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
2809 names[2].string = "Status bar";
2810 names[2].voice_id = -1;
2811 names[3].string = "Both";
2812 names[3].voice_id = -1;
2815 return rb->set_option("Show Footer", &prefs.footer_mode, INT,
2816 names, len, NULL);
2819 static int font_comp(const void *a, const void *b)
2821 struct opt_items *pa;
2822 struct opt_items *pb;
2824 pa = (struct opt_items *)a;
2825 pb = (struct opt_items *)b;
2827 return rb->strcmp(pa->string, pb->string);
2830 static bool font_setting(void)
2832 int count = 0;
2833 DIR *dir;
2834 struct dirent *entry;
2835 int i = 0;
2836 int len;
2837 int new_font = 0;
2838 int old_font;
2839 bool res;
2840 int size = 0;
2842 dir = rb->opendir(FONT_DIR);
2843 if (!dir)
2845 rb->splash(HZ/2, "Font dir is not accessible");
2846 return false;
2849 while (1)
2851 entry = rb->readdir(dir);
2853 if (entry == NULL)
2854 break;
2856 len = rb->strlen(entry->d_name);
2857 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2858 continue;
2859 size += len-3;
2860 count++;
2862 rb->closedir(dir);
2864 struct opt_items names[count];
2865 unsigned char font_names[size];
2866 unsigned char *p = font_names;
2868 dir = rb->opendir(FONT_DIR);
2869 if (!dir)
2871 rb->splash(HZ/2, "Font dir is not accessible");
2872 return false;
2875 while (1)
2877 entry = rb->readdir(dir);
2879 if (entry == NULL)
2880 break;
2882 len = rb->strlen(entry->d_name);
2883 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2884 continue;
2886 rb->snprintf(p, len-3, "%s", entry->d_name);
2887 names[i].string = p;
2888 names[i].voice_id = -1;
2889 p += len-3;
2890 i++;
2891 if (i >= count)
2892 break;
2894 rb->closedir(dir);
2896 rb->qsort(names, count, sizeof(struct opt_items), font_comp);
2898 for (i = 0; i < count; i++)
2900 if (!rb->strcmp(names[i].string, prefs.font))
2902 new_font = i;
2903 break;
2906 old_font = new_font;
2908 res = rb->set_option("Select Font", &new_font, INT,
2909 names, count, NULL);
2911 if (new_font != old_font)
2913 /* load selected font */
2914 if (!change_font((unsigned char *)names[new_font].string)) {
2915 /* revert by re-loading the preferences font */
2916 change_font(prefs.font);
2917 return false;
2919 rb->memset(prefs.font, 0, MAX_PATH);
2920 rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string);
2923 return res;
2926 static bool alignment_setting(void)
2928 static const struct opt_items names[] = {
2929 {"Left", -1},
2930 {"Right", -1},
2933 return rb->set_option("Alignment", &prefs.alignment, INT,
2934 names , 2, NULL);
2936 #endif
2938 static bool autoscroll_speed_setting(void)
2940 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
2941 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
2944 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
2945 NULL, NULL, Icon_NOICON);
2946 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
2947 NULL, NULL, Icon_NOICON);
2948 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
2949 NULL, NULL, Icon_NOICON);
2950 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
2951 NULL, NULL, Icon_NOICON);
2952 #ifdef HAVE_LCD_BITMAP
2953 MENUITEM_FUNCTION(alignment_item, 0, "Alignment", alignment_setting,
2954 NULL, NULL, Icon_NOICON);
2955 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
2956 NULL, NULL, Icon_NOICON);
2957 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
2958 NULL, NULL, Icon_NOICON);
2959 MENUITEM_FUNCTION(header_item, 0, "Show Header", header_setting,
2960 NULL, NULL, Icon_NOICON);
2961 MENUITEM_FUNCTION(footer_item, 0, "Show Footer", footer_setting,
2962 NULL, NULL, Icon_NOICON);
2963 MENUITEM_FUNCTION(font_item, 0, "Font", font_setting,
2964 NULL, NULL, Icon_NOICON);
2965 #endif
2966 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
2967 NULL, NULL, Icon_NOICON);
2968 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
2969 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
2970 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
2971 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
2972 #ifdef HAVE_LCD_BITMAP
2973 &alignment_item, &scrollbar_item, &page_mode_item, &header_item,
2974 &footer_item, &font_item,
2975 #endif
2976 &scroll_mode_item, &autoscroll_speed_item);
2978 static bool viewer_options_menu(bool is_global)
2980 bool result;
2981 struct preferences tmp_prefs;
2983 rb->memcpy(&tmp_prefs, &prefs, sizeof(struct preferences));
2985 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
2987 if (!is_global && rb->memcmp(&tmp_prefs, &prefs, sizeof(struct preferences)))
2989 /* Show-scrollbar mode for current view-width mode */
2990 #ifdef HAVE_LCD_BITMAP
2991 init_need_scrollbar();
2992 init_header_and_footer();
2993 #endif
2994 calc_page();
2996 return result;
2999 static void viewer_menu(void)
3001 int result;
3003 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
3004 "Return", "Viewer Options",
3005 "Show Playback Menu", "Select Bookmark",
3006 "Global Settings", "Quit");
3008 result = rb->do_menu(&menu, NULL, NULL, false);
3009 switch (result)
3011 case 0: /* return */
3012 break;
3013 case 1: /* change settings */
3014 done = viewer_options_menu(false);
3015 break;
3016 case 2: /* playback control */
3017 playback_control(NULL);
3018 break;
3019 case 3: /* select bookmark */
3020 viewer_select_bookmark(viewer_add_last_read_bookmark());
3021 viewer_remove_last_read_bookmark();
3022 fill_buffer(file_pos, buffer, buffer_size);
3023 if (prefs.scroll_mode == PAGE && cline > 1)
3024 viewer_scroll_to_top_line();
3025 break;
3026 case 4: /* change global settings */
3028 struct preferences orig_prefs;
3030 rb->memcpy(&orig_prefs, &prefs, sizeof(struct preferences));
3031 if (!viewer_load_global_settings())
3032 viewer_default_preferences();
3033 done = viewer_options_menu(true);
3034 viewer_save_global_settings();
3035 rb->memcpy(&prefs, &orig_prefs, sizeof(struct preferences));
3037 break;
3038 case 5: /* quit */
3039 viewer_exit(NULL);
3040 done = true;
3041 break;
3043 viewer_draw(col);
3046 enum plugin_status plugin_start(const void* file)
3048 int button, i, ok;
3049 int lastbutton = BUTTON_NONE;
3050 bool autoscroll = false;
3051 long old_tick;
3053 old_tick = *rb->current_tick;
3055 /* get the plugin buffer */
3056 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
3057 if (buffer_size == 0)
3059 rb->splash(HZ, "buffer does not allocate !!");
3060 return PLUGIN_ERROR;
3062 block_size = buffer_size / 3;
3063 buffer_size = 3 * block_size;
3065 if (!file)
3066 return PLUGIN_ERROR;
3068 file_name = file;
3069 ok = viewer_init();
3070 if (!ok) {
3071 rb->splash(HZ, "Error opening file.");
3072 return PLUGIN_ERROR;
3075 if (!viewer_load_settings()) /* load the preferences and bookmark */
3076 return PLUGIN_ERROR;
3078 #if LCD_DEPTH > 1
3079 rb->lcd_set_backdrop(NULL);
3080 #endif
3082 viewer_draw(col);
3084 while (!done) {
3086 if(autoscroll)
3088 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
3090 viewer_scroll_down(true);
3091 viewer_draw(col);
3092 old_tick = *rb->current_tick;
3096 button = rb->button_get_w_tmo(HZ/10);
3098 if (prefs.view_mode != WIDE) {
3099 /* when not in wide view mode, the SCREEN_LEFT and SCREEN_RIGHT
3100 buttons jump to the beginning and end of the file. To stop
3101 users doing this by accident, replace non-held occurrences
3102 with page up/down instead. */
3103 if (button == VIEWER_SCREEN_LEFT)
3104 button = VIEWER_PAGE_UP;
3105 else if (button == VIEWER_SCREEN_RIGHT)
3106 button = VIEWER_PAGE_DOWN;
3109 switch (button) {
3110 case VIEWER_MENU:
3111 #ifdef VIEWER_MENU2
3112 case VIEWER_MENU2:
3113 #endif
3114 viewer_menu();
3115 break;
3117 case VIEWER_AUTOSCROLL:
3118 #ifdef VIEWER_AUTOSCROLL_PRE
3119 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
3120 break;
3121 #endif
3122 autoscroll = !autoscroll;
3123 break;
3125 case VIEWER_PAGE_UP:
3126 case VIEWER_PAGE_UP | BUTTON_REPEAT:
3127 #ifdef VIEWER_PAGE_UP2
3128 case VIEWER_PAGE_UP2:
3129 case VIEWER_PAGE_UP2 | BUTTON_REPEAT:
3130 #endif
3131 if (prefs.scroll_mode == PAGE)
3133 /* Page up */
3134 #ifdef HAVE_LCD_BITMAP
3135 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
3136 #else
3137 for (i = 0; i < display_lines; i++)
3138 #endif
3139 viewer_scroll_up();
3141 else
3142 viewer_scroll_up();
3143 old_tick = *rb->current_tick;
3144 viewer_draw(col);
3145 break;
3147 case VIEWER_PAGE_DOWN:
3148 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
3149 #ifdef VIEWER_PAGE_DOWN2
3150 case VIEWER_PAGE_DOWN2:
3151 case VIEWER_PAGE_DOWN2 | BUTTON_REPEAT:
3152 #endif
3153 if (prefs.scroll_mode == PAGE)
3155 /* Page down */
3156 if (next_screen_ptr != NULL)
3158 screen_top_ptr = next_screen_to_draw_ptr;
3159 if (cpage < MAX_PAGE)
3160 cpage++;
3163 else
3164 viewer_scroll_down(autoscroll);
3165 old_tick = *rb->current_tick;
3166 viewer_draw(col);
3167 break;
3169 case VIEWER_SCREEN_LEFT:
3170 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
3171 if (prefs.view_mode == WIDE) {
3172 /* Screen left */
3173 col = col_limit(col - draw_columns);
3175 else { /* prefs.view_mode == NARROW */
3176 /* Top of file */
3177 viewer_top();
3180 viewer_draw(col);
3181 break;
3183 case VIEWER_SCREEN_RIGHT:
3184 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
3185 if (prefs.view_mode == WIDE) {
3186 /* Screen right */
3187 col = col_limit(col + draw_columns);
3189 else { /* prefs.view_mode == NARROW */
3190 /* Bottom of file */
3191 viewer_bottom();
3194 viewer_draw(col);
3195 break;
3197 #ifdef VIEWER_LINE_UP
3198 case VIEWER_LINE_UP:
3199 case VIEWER_LINE_UP | BUTTON_REPEAT:
3200 /* Scroll up one line */
3201 viewer_scroll_up();
3202 old_tick = *rb->current_tick;
3203 viewer_draw(col);
3204 break;
3206 case VIEWER_LINE_DOWN:
3207 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
3208 /* Scroll down one line */
3209 viewer_scroll_down(autoscroll);
3210 increment_current_line();
3211 old_tick = *rb->current_tick;
3212 viewer_draw(col);
3213 break;
3214 #endif
3215 #ifdef VIEWER_COLUMN_LEFT
3216 case VIEWER_COLUMN_LEFT:
3217 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
3218 if (prefs.view_mode == WIDE) {
3219 /* Scroll left one column */
3220 col = col_limit(col - glyph_width('o'));
3221 viewer_draw(col);
3223 break;
3225 case VIEWER_COLUMN_RIGHT:
3226 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
3227 if (prefs.view_mode == WIDE) {
3228 /* Scroll right one column */
3229 col = col_limit(col + glyph_width('o'));
3230 viewer_draw(col);
3232 break;
3233 #endif
3235 #ifdef VIEWER_RC_QUIT
3236 case VIEWER_RC_QUIT:
3237 #endif
3238 case VIEWER_QUIT:
3239 #ifdef VIEWER_QUIT2
3240 case VIEWER_QUIT2:
3241 #endif
3242 viewer_exit(NULL);
3243 done = true;
3244 break;
3246 case VIEWER_BOOKMARK:
3248 int idx = viewer_find_bookmark(cpage, cline);
3250 if (idx < 0)
3252 if (bookmark_count >= MAX_BOOKMARKS-1)
3253 rb->splash(HZ/2, "No more bookmarks");
3254 else
3256 viewer_add_bookmark();
3257 rb->splash(HZ/2, "Bookmark added");
3260 else
3262 viewer_remove_bookmark(idx);
3263 rb->splash(HZ/2, "Bookmark removed");
3265 viewer_draw(col);
3267 break;
3269 default:
3270 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
3271 == SYS_USB_CONNECTED)
3272 return PLUGIN_USB_CONNECTED;
3273 break;
3275 if (button != BUTTON_NONE)
3277 lastbutton = button;
3278 rb->yield();
3281 return PLUGIN_OK;