viewer plugin: when the setting file loads or saves, converts to new version settings...
[kugel-rb.git] / apps / plugins / viewer.c
blob5678c5ef7dc4486472dfe6878793e79f8b88bf0a
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
21 #include "plugin.h"
22 #include <ctype.h>
23 #include "lib/playback_control.h"
25 PLUGIN_HEADER
27 /* global settings file
28 * binary file, so dont use .cfg
30 * setting file format
32 * part byte count
33 * --------------------------------
34 * 'TVGS' 4
35 * version 1
36 * word_mode 1
37 * line_mode 1
38 * view_mode 1
39 * alignment 1
40 * encoding 1
41 * scrollbar_mode 1
42 * need_scrollbar 1
43 * page_mode 1
44 * page_number_mode 1
45 * title_mode 1
46 * scroll_mode 1
47 * autoscroll_speed 1
48 * font name MAX_PATH
50 #define GLOBAL_SETTINGS_FILE VIEWERS_DIR "/viewer.dat"
52 /* temporary file */
53 #define GLOBAL_SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp"
55 #define GLOBAL_SETTINGS_HEADER "\x54\x56\x47\x53" /* header="TVGS" */
56 #define GLOBAL_SETTINGS_H_SIZE 5
57 #define GLOBAL_SETTINGS_VERSION 0x32 /* version=2 */
58 #define GLOBAL_SETTINGS_FIRST_VERSION 0x31
60 /* preferences and bookmarks at each file
61 * binary file, so dont use .cfg
63 * setting file format
65 * part byte count
66 * --------------------------------
67 * 'TVS' 3
68 * version 1
69 * file count 2
70 * [1st file]
71 * file path MAX_PATH
72 * next file pos 2
73 * [preferences]
74 * word_mode 1
75 * line_mode 1
76 * view_mode 1
77 * alignment 1
78 * encoding 1
79 * scrollbar_mode 1
80 * need_scrollbar 1
81 * page_mode 1
82 * header_mode 1
83 * footer_mode 1
84 * scroll_mode 1
85 * autoscroll_speed 1
86 * font name MAX_PATH
87 * bookmark count 1
88 * [1st bookmark]
89 * file_position 4
90 * page 2
91 * line 1
92 * flag 1
93 * [2nd bookmark]
94 * ...
95 * [last bookmark]
96 * [2nd file]
97 * ...
98 * [last file]
100 #define SETTINGS_FILE VIEWERS_DIR "/viewer_file.dat"
102 /* temporary file */
103 #define SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp"
105 #define SETTINGS_HEADER "\x54\x56\x53" /* header="TVS" */
106 #define SETTINGS_H_SIZE 4
107 #define SETTINGS_VERSION 0x33 /* version=3 */
108 #define SETTINGS_FIRST_VERSION 0x32
110 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
111 #define NARROW_MAX_COLUMNS 64 /* Max displayable string len [narrow] (over-estimate) */
112 #define WIDE_MAX_COLUMNS 128 /* Max displayable string len [wide] (over-estimate) */
113 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
114 #define READ_PREV_ZONE (block_size*9/10) /* Arbitrary number less than SMALL_BLOCK_SIZE */
115 #define SMALL_BLOCK_SIZE block_size /* Smallest file chunk we will read */
116 #define LARGE_BLOCK_SIZE (block_size << 1) /* Preferable size of file chunk to read */
117 #define TOP_SECTOR buffer
118 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
119 #define BOTTOM_SECTOR (buffer + (SMALL_BLOCK_SIZE << 1))
120 #undef SCROLLBAR_WIDTH
121 #define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width
122 #define MAX_PAGE 9999
124 #define BOOKMARK_SIZE 8
125 #define MAX_BOOKMARKS 10 /* user setting bookmarks + last read page */
127 #define BOOKMARK_LAST 1
128 #define BOOKMARK_USER 2
130 #ifndef HAVE_LCD_BITMAP
131 #define BOOKMARK_ICON "\xee\x84\x81\x00"
132 #endif
134 #define PREFERENCES_SIZE (12 + MAX_PATH)
136 /* Out-Of-Bounds test for any pointer to data in the buffer */
137 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
139 /* Does the buffer contain the beginning of the file? */
140 #define BUFFER_BOF() (file_pos==0)
142 /* Does the buffer contain the end of the file? */
143 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
145 /* Formula for the endpoint address outside of buffer data */
146 #define BUFFER_END() \
147 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
149 /* Is the entire file being shown in one screen? */
150 #define ONE_SCREEN_FITS_ALL() \
151 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
153 /* Is a scrollbar called for on the current screen? */
154 #define NEED_SCROLLBAR() \
155 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
157 /* variable button definitions */
159 /* Recorder keys */
160 #if CONFIG_KEYPAD == RECORDER_PAD
161 #define VIEWER_QUIT BUTTON_OFF
162 #define VIEWER_PAGE_UP BUTTON_UP
163 #define VIEWER_PAGE_DOWN BUTTON_DOWN
164 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
165 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
166 #define VIEWER_MENU BUTTON_F1
167 #define VIEWER_AUTOSCROLL BUTTON_PLAY
168 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
169 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
170 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
171 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
172 #define VIEWER_BOOKMARK BUTTON_F2
174 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
175 #define VIEWER_QUIT BUTTON_OFF
176 #define VIEWER_PAGE_UP BUTTON_UP
177 #define VIEWER_PAGE_DOWN BUTTON_DOWN
178 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
179 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
180 #define VIEWER_MENU BUTTON_F1
181 #define VIEWER_AUTOSCROLL BUTTON_SELECT
182 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
183 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
184 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
185 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
186 #define VIEWER_BOOKMARK BUTTON_F2
188 /* Ondio keys */
189 #elif CONFIG_KEYPAD == ONDIO_PAD
190 #define VIEWER_QUIT BUTTON_OFF
191 #define VIEWER_PAGE_UP BUTTON_UP
192 #define VIEWER_PAGE_DOWN BUTTON_DOWN
193 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
194 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
195 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
196 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
197 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
198 #define VIEWER_BOOKMARK (BUTTON_MENU|BUTTON_OFF)
200 /* Player keys */
201 #elif CONFIG_KEYPAD == PLAYER_PAD
202 #define VIEWER_QUIT BUTTON_STOP
203 #define VIEWER_PAGE_UP BUTTON_LEFT
204 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
205 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
206 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
207 #define VIEWER_MENU BUTTON_MENU
208 #define VIEWER_AUTOSCROLL BUTTON_PLAY
209 #define VIEWER_BOOKMARK BUTTON_ON
211 /* iRiver H1x0 && H3x0 keys */
212 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
213 (CONFIG_KEYPAD == IRIVER_H300_PAD)
214 #define VIEWER_QUIT BUTTON_OFF
215 #define VIEWER_PAGE_UP BUTTON_UP
216 #define VIEWER_PAGE_DOWN BUTTON_DOWN
217 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
218 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
219 #define VIEWER_MENU BUTTON_MODE
220 #define VIEWER_AUTOSCROLL BUTTON_SELECT
221 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
222 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
223 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
224 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
225 #define VIEWER_BOOKMARK (BUTTON_ON | BUTTON_SELECT)
227 #define VIEWER_RC_QUIT BUTTON_RC_STOP
229 /* iPods */
230 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
231 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
232 (CONFIG_KEYPAD == IPOD_1G2G_PAD)
233 #define VIEWER_QUIT_PRE BUTTON_SELECT
234 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
235 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
236 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
237 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
238 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
239 #define VIEWER_MENU BUTTON_MENU
240 #define VIEWER_AUTOSCROLL BUTTON_PLAY
241 #define VIEWER_BOOKMARK BUTTON_SELECT
243 /* iFP7xx keys */
244 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
245 #define VIEWER_QUIT BUTTON_PLAY
246 #define VIEWER_PAGE_UP BUTTON_UP
247 #define VIEWER_PAGE_DOWN BUTTON_DOWN
248 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
249 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
250 #define VIEWER_MENU BUTTON_MODE
251 #define VIEWER_AUTOSCROLL BUTTON_SELECT
252 #define VIEWER_BOOKMARK (BUTTON_LEFT|BUTTON_SELECT)
254 /* iAudio X5 keys */
255 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
256 #define VIEWER_QUIT BUTTON_POWER
257 #define VIEWER_PAGE_UP BUTTON_UP
258 #define VIEWER_PAGE_DOWN BUTTON_DOWN
259 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
260 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
261 #define VIEWER_MENU BUTTON_SELECT
262 #define VIEWER_AUTOSCROLL BUTTON_PLAY
263 #define VIEWER_BOOKMARK BUTTON_REC
265 /* GIGABEAT keys */
266 #elif CONFIG_KEYPAD == GIGABEAT_PAD
267 #define VIEWER_QUIT BUTTON_POWER
268 #define VIEWER_PAGE_UP BUTTON_UP
269 #define VIEWER_PAGE_DOWN BUTTON_DOWN
270 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
271 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
272 #define VIEWER_MENU BUTTON_MENU
273 #define VIEWER_AUTOSCROLL BUTTON_A
274 #define VIEWER_BOOKMARK BUTTON_SELECT
276 /* Sansa E200 keys */
277 #elif CONFIG_KEYPAD == SANSA_E200_PAD
278 #define VIEWER_QUIT BUTTON_POWER
279 #define VIEWER_PAGE_UP BUTTON_UP
280 #define VIEWER_PAGE_DOWN BUTTON_DOWN
281 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
282 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
283 #define VIEWER_MENU BUTTON_SELECT
284 #define VIEWER_AUTOSCROLL BUTTON_REC
285 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
286 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
287 #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
289 /* Sansa Fuze keys */
290 #elif CONFIG_KEYPAD == SANSA_FUZE_PAD
291 #define VIEWER_QUIT (BUTTON_HOME|BUTTON_REPEAT)
292 #define VIEWER_PAGE_UP BUTTON_UP
293 #define VIEWER_PAGE_DOWN BUTTON_DOWN
294 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
295 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
296 #define VIEWER_MENU BUTTON_SELECT|BUTTON_REPEAT
297 #define VIEWER_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
298 #define VIEWER_LINE_UP BUTTON_SCROLL_BACK
299 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
300 #define VIEWER_BOOKMARK BUTTON_SELECT
302 /* Sansa C200 keys */
303 #elif CONFIG_KEYPAD == SANSA_C200_PAD
304 #define VIEWER_QUIT BUTTON_POWER
305 #define VIEWER_PAGE_UP BUTTON_VOL_UP
306 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
307 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
308 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
309 #define VIEWER_MENU BUTTON_SELECT
310 #define VIEWER_AUTOSCROLL BUTTON_REC
311 #define VIEWER_LINE_UP BUTTON_UP
312 #define VIEWER_LINE_DOWN BUTTON_DOWN
313 #define VIEWER_BOOKMARK (BUTTON_DOWN | BUTTON_SELECT)
315 /* Sansa Clip keys */
316 #elif CONFIG_KEYPAD == SANSA_CLIP_PAD
317 #define VIEWER_QUIT BUTTON_POWER
318 #define VIEWER_PAGE_UP BUTTON_VOL_UP
319 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
320 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
321 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
322 #define VIEWER_MENU BUTTON_SELECT
323 #define VIEWER_AUTOSCROLL BUTTON_HOME
324 #define VIEWER_LINE_UP BUTTON_UP
325 #define VIEWER_LINE_DOWN BUTTON_DOWN
326 #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
328 /* Sansa M200 keys */
329 #elif CONFIG_KEYPAD == SANSA_M200_PAD
330 #define VIEWER_QUIT BUTTON_POWER
331 #define VIEWER_PAGE_UP BUTTON_VOL_UP
332 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
333 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
334 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
335 #define VIEWER_MENU (BUTTON_SELECT | BUTTON_UP)
336 #define VIEWER_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
337 #define VIEWER_LINE_UP BUTTON_UP
338 #define VIEWER_LINE_DOWN BUTTON_DOWN
339 #define VIEWER_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
341 /* iriver H10 keys */
342 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
343 #define VIEWER_QUIT BUTTON_POWER
344 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
345 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
346 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
347 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
348 #define VIEWER_MENU BUTTON_REW
349 #define VIEWER_AUTOSCROLL BUTTON_PLAY
350 #define VIEWER_BOOKMARK BUTTON_FF
352 /*M-Robe 500 keys */
353 #elif CONFIG_KEYPAD == MROBE500_PAD
354 #define VIEWER_QUIT BUTTON_POWER
355 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
356 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
357 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
358 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
359 #define VIEWER_MENU BUTTON_RC_HEART
360 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
361 #define VIEWER_BOOKMARK BUTTON_CENTER
363 /*Gigabeat S keys */
364 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
365 #define VIEWER_QUIT BUTTON_BACK
366 #define VIEWER_PAGE_UP BUTTON_PREV
367 #define VIEWER_PAGE_DOWN BUTTON_NEXT
368 #define VIEWER_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
369 #define VIEWER_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
370 #define VIEWER_MENU BUTTON_MENU
371 #define VIEWER_AUTOSCROLL_PRE BUTTON_PLAY
372 #define VIEWER_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
373 #define VIEWER_LINE_UP BUTTON_UP
374 #define VIEWER_LINE_DOWN BUTTON_DOWN
375 #define VIEWER_COLUMN_LEFT BUTTON_LEFT
376 #define VIEWER_COLUMN_RIGHT BUTTON_RIGHT
377 #define VIEWER_BOOKMARK BUTTON_SELECT
379 /*M-Robe 100 keys */
380 #elif CONFIG_KEYPAD == MROBE100_PAD
381 #define VIEWER_QUIT BUTTON_POWER
382 #define VIEWER_PAGE_UP BUTTON_UP
383 #define VIEWER_PAGE_DOWN BUTTON_DOWN
384 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
385 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
386 #define VIEWER_MENU BUTTON_MENU
387 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
388 #define VIEWER_BOOKMARK BUTTON_SELECT
390 /* iAUdio M3 keys */
391 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
392 #define VIEWER_QUIT BUTTON_REC
393 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
394 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
395 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
396 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
397 #define VIEWER_MENU BUTTON_RC_MENU
398 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
399 #define VIEWER_RC_QUIT BUTTON_RC_REC
400 #define VIEWER_BOOKMARK BUTTON_RC_PLAY
402 /* Cowon D2 keys */
403 #elif CONFIG_KEYPAD == COWON_D2_PAD
404 #define VIEWER_QUIT BUTTON_POWER
405 #define VIEWER_MENU BUTTON_MENU
406 #define VIEWER_PAGE_UP BUTTON_MINUS
407 #define VIEWER_PAGE_DOWN BUTTON_PLUS
408 #define VIEWER_BOOKMARK (BUTTON_MENU|BUTTON_PLUS)
410 #elif CONFIG_KEYPAD == IAUDIO67_PAD
411 #define VIEWER_QUIT BUTTON_POWER
412 #define VIEWER_PAGE_UP BUTTON_VOLUP
413 #define VIEWER_PAGE_DOWN BUTTON_VOLDOWN
414 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
415 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
416 #define VIEWER_MENU BUTTON_MENU
417 #define VIEWER_AUTOSCROLL BUTTON_PLAY
418 #define VIEWER_RC_QUIT BUTTON_STOP
419 #define VIEWER_BOOKMARK (BUTTON_LEFT|BUTTON_PLAY)
421 /* Creative Zen Vision:M keys */
422 #elif CONFIG_KEYPAD == CREATIVEZVM_PAD
423 #define VIEWER_QUIT BUTTON_BACK
424 #define VIEWER_PAGE_UP BUTTON_UP
425 #define VIEWER_PAGE_DOWN BUTTON_DOWN
426 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
427 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
428 #define VIEWER_MENU BUTTON_MENU
429 #define VIEWER_AUTOSCROLL BUTTON_SELECT
430 #define VIEWER_BOOKMARK BUTTON_PLAY
432 /* Philips HDD1630 keys */
433 #elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
434 #define VIEWER_QUIT BUTTON_POWER
435 #define VIEWER_PAGE_UP BUTTON_UP
436 #define VIEWER_PAGE_DOWN BUTTON_DOWN
437 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
438 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
439 #define VIEWER_MENU BUTTON_MENU
440 #define VIEWER_AUTOSCROLL BUTTON_VIEW
441 #define VIEWER_BOOKMARK BUTTON_SELECT
443 /* Philips SA9200 keys */
444 #elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
445 #define VIEWER_QUIT BUTTON_POWER
446 #define VIEWER_PAGE_UP BUTTON_UP
447 #define VIEWER_PAGE_DOWN BUTTON_DOWN
448 #define VIEWER_SCREEN_LEFT BUTTON_PREV
449 #define VIEWER_SCREEN_RIGHT BUTTON_NEXT
450 #define VIEWER_MENU BUTTON_MENU
451 #define VIEWER_AUTOSCROLL BUTTON_PLAY
452 #define VIEWER_BOOKMARK BUTTON_RIGHT
454 /* Onda VX747 keys */
455 #elif CONFIG_KEYPAD == ONDAVX747_PAD
456 #define VIEWER_QUIT BUTTON_POWER
457 #define VIEWER_MENU BUTTON_MENU
458 #define VIEWER_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER)
460 /* Onda VX777 keys */
461 #elif CONFIG_KEYPAD == ONDAVX777_PAD
462 #define VIEWER_QUIT BUTTON_POWER
463 #define VIEWER_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER)
465 /* SAMSUNG YH-820 / YH-920 / YH-925 keys */
466 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
467 #define VIEWER_QUIT BUTTON_REC
468 #define VIEWER_PAGE_UP BUTTON_UP
469 #define VIEWER_PAGE_DOWN BUTTON_DOWN
470 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
471 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
472 #define VIEWER_MENU BUTTON_PLAY
473 #define VIEWER_AUTOSCROLL BUTTON_REW
474 #define VIEWER_BOOKMARK BUTTON_FFWD
476 /* Packard Bell Vibe 500 keys */
477 #elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
478 #define VIEWER_QUIT BUTTON_REC
479 #define VIEWER_PAGE_UP BUTTON_OK
480 #define VIEWER_PAGE_DOWN BUTTON_CANCEL
481 #define VIEWER_LINE_UP BUTTON_UP
482 #define VIEWER_LINE_DOWN BUTTON_DOWN
483 #define VIEWER_SCREEN_LEFT BUTTON_PREV
484 #define VIEWER_SCREEN_RIGHT BUTTON_NEXT
485 #define VIEWER_MENU BUTTON_MENU
486 #define VIEWER_AUTOSCROLL BUTTON_PLAY
487 #define VIEWER_BOOKMARK BUTTON_POWER
489 #else
490 #error No keymap defined!
491 #endif
493 #ifdef HAVE_TOUCHSCREEN
494 #ifdef VIEWER_QUIT
495 #define VIEWER_QUIT2 BUTTON_TOPLEFT
496 #else
497 #define VIEWER_QUIT BUTTON_TOPLEFT
498 #endif
499 #ifdef VIEWER_PAGE_UP
500 #define VIEWER_PAGE_UP2 BUTTON_TOPMIDDLE
501 #else
502 #define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
503 #endif
504 #ifdef VIEWER_PAGE_DOWN
505 #define VIEWER_PAGE_DOWN2 BUTTON_BOTTOMMIDDLE
506 #else
507 #define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
508 #endif
509 #ifndef VIEWER_SCREEN_LEFT
510 #define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
511 #endif
512 #ifndef VIEWER_SCREEN_RIGHT
513 #define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
514 #endif
515 #ifdef VIEWER_MENU
516 #define VIEWER_MENU2 BUTTON_TOPRIGHT
517 #else
518 #define VIEWER_MENU BUTTON_TOPRIGHT
519 #endif
520 #ifndef VIEWER_AUTOSCROLL
521 #define VIEWER_AUTOSCROLL BUTTON_CENTER
522 #endif
523 #endif
525 /* stuff for the bookmarking */
526 struct bookmark_info {
527 long file_position;
528 int page;
529 int line;
530 unsigned char flag;
533 struct preferences {
534 enum {
535 WRAP=0,
536 CHOP,
537 } word_mode;
539 enum {
540 NORMAL=0,
541 JOIN,
542 EXPAND,
543 REFLOW, /* won't be set on charcell LCD, must be last */
544 } line_mode;
546 enum {
547 NARROW=0,
548 WIDE,
549 } view_mode;
551 enum {
552 LEFT=0,
553 RIGHT,
554 } alignment;
556 enum codepages encoding;
558 enum {
559 SB_OFF=0,
560 SB_ON,
561 } scrollbar_mode;
562 bool need_scrollbar;
564 enum {
565 NO_OVERLAP=0,
566 OVERLAP,
567 } page_mode;
569 enum {
570 HD_NONE = 0,
571 HD_PATH,
572 HD_SBAR,
573 HD_BOTH,
574 } header_mode;
576 enum {
577 FT_NONE = 0,
578 FT_PAGE,
579 FT_SBAR,
580 FT_BOTH,
581 } footer_mode;
583 enum {
584 PAGE=0,
585 LINE,
586 } scroll_mode;
588 int autoscroll_speed;
590 unsigned char font[MAX_PATH];
593 enum {
594 VIEWER_FONT_MENU = 0,
595 VIEWER_FONT_TEXT,
598 struct preferences prefs;
599 struct preferences old_prefs;
601 static unsigned char *buffer;
602 static long buffer_size;
603 static long block_size = 0x1000;
604 static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
605 static int display_columns; /* number of (pixel) columns on the display */
606 static int display_lines; /* number of lines on the display */
607 static int draw_columns; /* number of (pixel) columns available for text */
608 static int par_indent_spaces; /* number of spaces to indent first paragraph */
609 static int fd;
610 static const char *file_name;
611 static long file_size;
612 static long start_position; /* position in the file after the viewer is started */
613 static bool mac_text;
614 static long file_pos; /* Position of the top of the buffer in the file */
615 static long last_file_pos;
616 static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
617 static int max_line_len;
618 static int max_width;
619 static int max_columns;
620 static int cline = 1;
621 static int cpage = 1;
622 static int lpage = 0;
623 static unsigned char *screen_top_ptr;
624 static unsigned char *next_screen_ptr;
625 static unsigned char *next_screen_to_draw_ptr;
626 static unsigned char *next_line_ptr;
627 static unsigned char *last_screen_top_ptr = NULL;
628 #ifdef HAVE_LCD_BITMAP
629 static struct font *pf;
630 static int header_height = 0;
631 static int footer_height = 0;
632 #endif
633 struct bookmark_info bookmarks[MAX_BOOKMARKS];
634 static int bookmark_count;
636 /* UTF-8 BOM */
637 #define BOM "\xef\xbb\xbf"
638 #define BOM_SIZE 3
640 static bool is_bom = false;
642 /* calculate the width of a UCS character (zero width for diacritics) */
643 static int glyph_width(unsigned short ch)
645 if (ch == 0)
646 ch = ' ';
648 #ifdef HAVE_LCD_BITMAP
649 if (rb->is_diacritic(ch, NULL))
650 return 0;
652 return rb->font_get_width(pf, ch);
653 #else
654 return 1;
655 #endif
658 /* get UCS character from string */
659 static unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
661 unsigned char utf8_tmp[6];
662 int count;
664 if (prefs.encoding == UTF_8)
665 return (unsigned char*)rb->utf8decode(str, ch);
667 count = BUFFER_OOB(str+2)? 1:2;
668 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
669 rb->utf8decode(utf8_tmp, ch);
671 /* return a pointer after the parsed section of the string */
672 #ifdef HAVE_LCD_BITMAP
673 if (prefs.encoding >= SJIS && *str >= 0x80
674 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
675 return (unsigned char*)str+2;
676 else
677 #endif
678 return (unsigned char*)str+1;
681 /* decode UCS string into UTF-8 string */
682 static unsigned char *decode2utf8(const unsigned char *src, unsigned char *dst,
683 int skip_width, int disp_width)
685 unsigned short ch;
686 const unsigned char *oldstr;
687 const unsigned char *str = src;
688 unsigned char *utf8 = dst;
689 int width = 0;
691 /* skip the skip_width */
692 while (*str != '\0')
694 oldstr = str;
695 str = get_ucs(oldstr, &ch);
696 width += glyph_width(ch);
697 if (width > skip_width)
699 str = oldstr;
700 break;
704 /* decode until string end or disp_width reached */
705 width = 0;
706 while(*str != '\0')
708 str = get_ucs(str, &ch);
709 width += glyph_width(ch);
710 if (width > disp_width)
711 break;
713 utf8 = rb->utf8encode(ch, utf8);
716 /* return a pointer after the dst string ends */
717 return utf8;
720 /* set max_columns and max_width */
721 static void calc_max_width(void)
723 if (prefs.view_mode == NARROW)
725 max_columns = NARROW_MAX_COLUMNS;
726 max_width = draw_columns;
728 else
730 max_columns = WIDE_MAX_COLUMNS;
731 max_width = 2 * draw_columns;
735 static bool done = false;
736 static int col = 0;
738 static inline void advance_conters(unsigned short ch, int* k, int* width)
740 #ifdef HAVE_LCD_BITMAP
741 /* diacritics do not count */
742 if (rb->is_diacritic(ch, NULL))
743 return;
744 #endif
746 *width += glyph_width(ch);
747 (*k)++;
750 static inline bool line_is_full(int k, int width)
752 return ((k >= max_columns - 1) || (width >= max_width));
755 static unsigned char* crop_at_width(const unsigned char* p)
757 int k,width;
758 unsigned short ch;
759 const unsigned char *oldp = p;
761 k=width=0;
763 while (!line_is_full(k, width)) {
764 oldp = p;
765 if (BUFFER_OOB(p))
766 break;
767 p = get_ucs(p, &ch);
768 advance_conters(ch, &k, &width);
771 return (unsigned char*)oldp;
774 static unsigned char* find_first_feed(const unsigned char* p, int size)
776 int s = 0;
777 unsigned short ch;
778 const unsigned char *oldp = p;
780 while(s <= size)
782 if (*p == 0)
783 return (unsigned char*)p;
784 oldp = p;
785 p = get_ucs(p, &ch);
786 s += (p - oldp);
789 return NULL;
792 static unsigned char* find_last_feed(const unsigned char* p, int size)
794 int i;
796 for (i=size-1; i>=0; i--)
797 if (p[i] == 0)
798 return (unsigned char*) p+i;
800 return NULL;
803 static unsigned char* find_last_space(const unsigned char* p, int size)
805 int i, j, k;
807 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
809 i = size;
810 if (!BUFFER_OOB(&p[i]))
811 for (j=k; j < ((int) sizeof(line_break)) - 1; j++) {
812 if (p[i] == line_break[j])
813 return (unsigned char*) p+i;
816 if (prefs.word_mode == WRAP) {
817 for (i=size-1; i>=0; i--) {
818 for (j=k; j < (int) sizeof(line_break) - 1; j++) {
819 if (p[i] == line_break[j])
820 return (unsigned char*) p+i;
825 return NULL;
828 static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
830 const unsigned char *next_line = NULL;
831 int size, i, j, j_next, j_prev, k, width, search_len, spaces, newlines;
832 bool first_chars;
833 unsigned short ch;
835 if (is_short != NULL)
836 *is_short = true;
838 if BUFFER_OOB(cur_line)
839 return NULL;
841 if (prefs.view_mode == WIDE) {
842 search_len = MAX_WIDTH;
844 else { /* prefs.view_mode == NARROW */
845 search_len = crop_at_width(cur_line) - cur_line;
848 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
850 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
851 /* Need to scan ahead and possibly increase search_len and size,
852 or possibly set next_line at second hard return in a row. */
853 next_line = NULL;
854 first_chars=true;
855 j_next=j=k=width=spaces=newlines=0;
856 while (1) {
857 const unsigned char *p, *oldp;
859 j_prev = j;
860 j = j_next;
862 if (BUFFER_OOB(cur_line+j))
863 return NULL;
864 if (line_is_full(k, width)) {
865 size = search_len = j_prev;
866 break;
869 oldp = p = &cur_line[j];
870 p = get_ucs(p, &ch);
871 j_next = j + (p - oldp);
873 switch (ch) {
874 case ' ':
875 if (prefs.line_mode == REFLOW) {
876 if (newlines > 0) {
877 size = j;
878 next_line = cur_line + size;
879 return (unsigned char*) next_line;
881 if (j==0) /* i=1 is intentional */
882 for (i=0; i<par_indent_spaces; i++)
883 advance_conters(' ', &k, &width);
885 if (!first_chars) spaces++;
886 break;
888 case 0:
889 if (newlines > 0) {
890 size = j;
891 next_line = cur_line + size - spaces;
892 if (next_line != cur_line)
893 return (unsigned char*) next_line;
894 break;
897 newlines++;
898 size += spaces -1;
899 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
900 return NULL;
901 search_len = size;
902 spaces = first_chars? 0:1;
903 break;
905 default:
906 if (prefs.line_mode==JOIN || newlines>0) {
907 while (spaces) {
908 spaces--;
909 advance_conters(' ', &k, &width);
910 if (line_is_full(k, width)) {
911 size = search_len = j;
912 break;
915 newlines=0;
916 } else if (spaces) {
917 /* REFLOW, multiple spaces between words: count only
918 * one. If more are needed, they will be added
919 * while drawing. */
920 search_len = size;
921 spaces=0;
922 advance_conters(' ', &k, &width);
923 if (line_is_full(k, width)) {
924 size = search_len = j;
925 break;
928 first_chars = false;
929 advance_conters(ch, &k, &width);
930 break;
934 else {
935 /* find first hard return */
936 next_line = find_first_feed(cur_line, size);
939 if (next_line == NULL)
940 if (size == search_len) {
941 if (prefs.word_mode == WRAP) /* Find last space */
942 next_line = find_last_space(cur_line, size);
944 if (next_line == NULL) {
945 next_line = crop_at_width(cur_line);
947 else {
948 if (prefs.word_mode == WRAP) {
949 for (i=0;i<WRAP_TRIM;i++) {
950 if (!(isspace(next_line[0]) && !BUFFER_OOB(next_line)))
951 break;
952 next_line++;
958 if (prefs.line_mode == EXPAND)
959 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
960 if (next_line[0] == 0)
961 if (next_line != cur_line)
962 return (unsigned char*) next_line;
964 /* If next_line is pointing to a zero, increment it; i.e.,
965 leave the terminator at the end of cur_line. If pointing
966 to a hyphen, increment only if there is room to display
967 the hyphen on current line (won't apply in WIDE mode,
968 since it's guarenteed there won't be room). */
969 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
970 if (next_line[0] == 0)/* ||
971 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
972 next_line++;
974 if (BUFFER_OOB(next_line))
976 if (BUFFER_EOF() && next_line != cur_line)
977 return (unsigned char*) next_line;
978 return NULL;
981 if (is_short)
982 *is_short = false;
984 return (unsigned char*) next_line;
987 static unsigned char* find_prev_line(const unsigned char* cur_line)
989 const unsigned char *prev_line = NULL;
990 const unsigned char *p;
992 if BUFFER_OOB(cur_line)
993 return NULL;
995 /* To wrap consistently at the same places, we must
996 start with a known hard return, then work downwards.
997 We can either search backwards for a hard return,
998 or simply start wrapping downwards from top of buffer.
999 If current line is not near top of buffer, this is
1000 a file with long lines (paragraphs). We would need to
1001 read earlier sectors before we could decide how to
1002 properly wrap the lines above the current line, but
1003 it probably is not worth the disk access. Instead,
1004 start with top of buffer and wrap down from there.
1005 This may result in some lines wrapping at different
1006 points from where they wrap when scrolling down.
1007 If buffer is at top of file, start at top of buffer. */
1009 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
1010 prev_line = p = NULL;
1011 else
1012 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
1013 /* Null means no line feeds in buffer above current line. */
1015 if (prev_line == NULL)
1016 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
1017 prev_line = p = buffer;
1018 /* (else return NULL and read previous block) */
1020 /* Wrap downwards until too far, then use the one before. */
1021 while (p != NULL && p < cur_line) {
1022 prev_line = p;
1023 p = find_next_line(prev_line, NULL);
1026 if (BUFFER_OOB(prev_line))
1027 return NULL;
1029 return (unsigned char*) prev_line;
1032 static void check_bom(void)
1034 unsigned char bom[BOM_SIZE];
1035 off_t orig = rb->lseek(fd, 0, SEEK_CUR);
1037 is_bom = false;
1039 rb->lseek(fd, 0, SEEK_SET);
1041 if (rb->read(fd, bom, BOM_SIZE) == BOM_SIZE)
1042 is_bom = !memcmp(bom, BOM, BOM_SIZE);
1044 rb->lseek(fd, orig, SEEK_SET);
1047 static void fill_buffer(long pos, unsigned char* buf, unsigned size)
1049 /* Read from file and preprocess the data */
1050 /* To minimize disk access, always read on sector boundaries */
1051 unsigned numread, i;
1052 bool found_CR = false;
1053 off_t offset = rb->lseek(fd, pos, SEEK_SET);
1055 if (offset == 0 && prefs.encoding == UTF_8 && is_bom)
1056 rb->lseek(fd, BOM_SIZE, SEEK_SET);
1058 numread = rb->read(fd, buf, size - 1);
1059 buf[numread] = 0;
1060 rb->button_clear_queue(); /* clear button queue */
1062 for(i = 0; i < numread; i++) {
1063 switch(buf[i]) {
1064 case '\r':
1065 if (mac_text) {
1066 buf[i] = 0;
1068 else {
1069 buf[i] = ' ';
1070 found_CR = true;
1072 break;
1074 case '\n':
1075 buf[i] = 0;
1076 found_CR = false;
1077 break;
1079 case 0: /* No break between case 0 and default, intentionally */
1080 buf[i] = ' ';
1081 default:
1082 if (found_CR) {
1083 buf[i - 1] = 0;
1084 found_CR = false;
1085 mac_text = true;
1087 break;
1092 static int viewer_find_bookmark(int page, int line)
1094 int i;
1096 for (i = 0; i < bookmark_count; i++)
1098 if (bookmarks[i].page == page && bookmarks[i].line == line)
1099 return i;
1101 return -1;
1104 static int read_and_synch(int direction)
1106 /* Read next (or prev) block, and reposition global pointers. */
1107 /* direction: 1 for down (i.e., further into file), -1 for up */
1108 int move_size, move_vector, offset;
1109 unsigned char *fill_buf;
1111 if (direction == -1) /* up */ {
1112 move_size = SMALL_BLOCK_SIZE;
1113 offset = 0;
1114 fill_buf = TOP_SECTOR;
1115 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1116 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
1118 else /* down */ {
1119 if (prefs.view_mode == WIDE) {
1120 /* WIDE mode needs more buffer so we have to read smaller blocks */
1121 move_size = SMALL_BLOCK_SIZE;
1122 offset = LARGE_BLOCK_SIZE;
1123 fill_buf = BOTTOM_SECTOR;
1124 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1125 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1127 else {
1128 move_size = LARGE_BLOCK_SIZE;
1129 offset = SMALL_BLOCK_SIZE;
1130 fill_buf = MID_SECTOR;
1131 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1134 move_vector = direction * move_size;
1135 screen_top_ptr -= move_vector;
1136 file_pos += move_vector;
1137 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1138 fill_buffer(file_pos + offset, fill_buf, move_size);
1139 return move_vector;
1142 static void get_next_line_position(unsigned char **line_begin,
1143 unsigned char **line_end,
1144 bool *is_short)
1146 int resynch_move;
1148 *line_begin = *line_end;
1149 *line_end = find_next_line(*line_begin, is_short);
1151 if (*line_end == NULL && !BUFFER_EOF())
1153 resynch_move = read_and_synch(1); /* Read block & move ptrs */
1154 *line_begin -= resynch_move;
1155 if (next_line_ptr > buffer)
1156 next_line_ptr -= resynch_move;
1158 *line_end = find_next_line(*line_begin, is_short);
1162 static void increment_current_line(void)
1164 if (cline < display_lines)
1165 cline++;
1166 else if (cpage < MAX_PAGE)
1168 cpage++;
1169 cline = 1;
1173 static void decrement_current_line(void)
1175 if (cline > 1)
1176 cline--;
1177 else if (cpage > 1)
1179 cpage--;
1180 cline = display_lines;
1184 static void viewer_scroll_up(void)
1186 unsigned char *p;
1188 p = find_prev_line(screen_top_ptr);
1189 if (p == NULL && !BUFFER_BOF()) {
1190 read_and_synch(-1);
1191 p = find_prev_line(screen_top_ptr);
1193 if (p != NULL)
1194 screen_top_ptr = p;
1196 decrement_current_line();
1199 static void viewer_scroll_down(bool autoscroll)
1201 if (cpage == lpage)
1202 return;
1204 if (next_line_ptr != NULL)
1205 screen_top_ptr = next_line_ptr;
1207 if (prefs.scroll_mode == LINE || autoscroll)
1208 increment_current_line();
1211 static void viewer_scroll_to_top_line(void)
1213 int line;
1215 for (line = cline; line > 1; line--)
1216 viewer_scroll_up();
1219 #ifdef HAVE_LCD_BITMAP
1220 static void viewer_scrollbar(void) {
1221 int items, min_shown, max_shown, sb_begin_y, sb_height;
1223 items = (int) file_size; /* (SH1 int is same as long) */
1224 min_shown = (int) file_pos + (screen_top_ptr - buffer);
1226 if (next_screen_ptr == NULL)
1227 max_shown = items;
1228 else
1229 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
1231 sb_begin_y = header_height;
1232 sb_height = LCD_HEIGHT - header_height - footer_height;
1234 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, sb_begin_y,
1235 SCROLLBAR_WIDTH-1, sb_height,
1236 items, min_shown, max_shown, VERTICAL);
1238 #endif
1240 #ifdef HAVE_LCD_BITMAP
1241 static void viewer_show_header(void)
1243 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1244 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1246 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1247 rb->lcd_putsxy(0, header_height - pf->height, file_name);
1250 static void viewer_show_footer(void)
1252 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1253 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1255 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1257 unsigned char buf[12];
1259 if (cline == 1)
1260 rb->snprintf(buf, sizeof(buf), "%d", cpage);
1261 else
1262 rb->snprintf(buf, sizeof(buf), "%d - %d", cpage, cpage+1);
1264 rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf);
1267 #endif
1269 /* We draw a diacritic char over a non-diacritic one. Therefore, such chars are
1270 * not considered to occupy space, therefore buffers might have more than
1271 * max_columns characters. The DIACRITIC_FACTOR is the max ratio between all
1272 * characters and non-diacritic characters in the buffer
1274 #define DIACRITIC_FACTOR 2
1276 static void viewer_draw(int col)
1278 int i, j, k, line_len, line_width, spaces, left_col=0;
1279 int width, extra_spaces, indent_spaces, spaces_per_word, spaces_width;
1280 bool multiple_spacing, line_is_short;
1281 unsigned short ch;
1282 unsigned char *str, *oldstr;
1283 unsigned char *line_begin;
1284 unsigned char *line_end;
1285 unsigned char c;
1286 int max_chars = max_columns * DIACRITIC_FACTOR;
1287 unsigned char scratch_buffer[max_chars + 1];
1288 unsigned char utf8_buffer[max_chars * 4 + 1];
1289 unsigned char *endptr;
1291 /* If col==-1 do all calculations but don't display */
1292 if (col != -1) {
1293 #ifdef HAVE_LCD_BITMAP
1294 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
1295 #else
1296 left_col = 0;
1297 #endif
1298 rb->lcd_clear_display();
1300 max_line_len = 0;
1301 line_begin = line_end = screen_top_ptr;
1303 for (i = 0; i < display_lines; i++) {
1304 if (BUFFER_OOB(line_end))
1306 if (lpage == cpage)
1307 break; /* Happens after display last line at BUFFER_EOF() */
1309 if (lpage == 0 && cline == 1)
1311 lpage = cpage;
1312 last_screen_top_ptr = screen_top_ptr;
1313 last_file_pos = file_pos;
1317 get_next_line_position(&line_begin, &line_end, &line_is_short);
1318 if (line_end == NULL)
1320 if (BUFFER_OOB(line_begin))
1321 break;
1322 line_end = buffer_end + 1;
1325 line_len = line_end - line_begin;
1327 /* calculate line_len */
1328 str = oldstr = line_begin;
1329 j = -1;
1330 while (str < line_end) {
1331 oldstr = str;
1332 str = crop_at_width(str);
1333 j++;
1334 if (oldstr == str)
1336 oldstr = line_end;
1337 break;
1340 /* width of un-displayed part of the line */
1341 line_width = j*draw_columns;
1342 spaces_width = 0;
1343 while (oldstr < line_end) {
1344 oldstr = get_ucs(oldstr, &ch);
1345 /* add width of displayed part of the line */
1346 if (ch)
1348 int dw = glyph_width(ch);
1350 /* avoid counting spaces at the end of the line */
1351 if (ch == ' ')
1353 spaces_width += dw;
1355 else
1357 line_width += dw + spaces_width;
1358 spaces_width = 0;
1363 if (prefs.line_mode == JOIN) {
1364 if (line_begin[0] == 0) {
1365 line_begin++;
1366 if (prefs.word_mode == CHOP)
1367 line_end++;
1368 else
1369 line_len--;
1371 for (j=k=spaces=0; j < line_len; j++) {
1372 if (k == max_chars)
1373 break;
1375 c = line_begin[j];
1376 switch (c) {
1377 case ' ':
1378 spaces++;
1379 break;
1380 case 0:
1381 spaces = 0;
1382 scratch_buffer[k++] = ' ';
1383 break;
1384 default:
1385 while (spaces) {
1386 spaces--;
1387 scratch_buffer[k++] = ' ';
1388 if (k == max_chars - 1)
1389 break;
1391 scratch_buffer[k++] = c;
1392 break;
1395 if (col != -1) {
1396 scratch_buffer[k] = 0;
1397 endptr = decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
1398 *endptr = 0;
1401 else if (prefs.line_mode == REFLOW) {
1402 if (line_begin[0] == 0) {
1403 line_begin++;
1404 if (prefs.word_mode == CHOP)
1405 line_end++;
1406 else
1407 line_len--;
1410 indent_spaces = 0;
1411 if (!line_is_short) {
1412 multiple_spacing = false;
1413 width=spaces=0;
1414 for (str = line_begin; str < line_end; ) {
1415 str = get_ucs(str, &ch);
1416 switch (ch) {
1417 case ' ':
1418 case 0:
1419 if ((str == line_begin) && (prefs.word_mode==WRAP))
1420 /* special case: indent the paragraph,
1421 * don't count spaces */
1422 indent_spaces = par_indent_spaces;
1423 else if (!multiple_spacing)
1424 spaces++;
1425 multiple_spacing = true;
1426 break;
1427 default:
1428 multiple_spacing = false;
1429 width += glyph_width(ch);
1430 break;
1433 if (multiple_spacing) spaces--;
1435 if (spaces) {
1436 /* total number of spaces to insert between words */
1437 extra_spaces = (max_width-width)/glyph_width(' ')
1438 - indent_spaces;
1439 /* number of spaces between each word*/
1440 spaces_per_word = extra_spaces / spaces;
1441 /* number of words with n+1 spaces (to fill up) */
1442 extra_spaces = extra_spaces % spaces;
1443 if (spaces_per_word > 2) { /* too much spacing is awful */
1444 spaces_per_word = 3;
1445 extra_spaces = 0;
1447 } else { /* this doesn't matter much... no spaces anyway */
1448 spaces_per_word = extra_spaces = 0;
1450 } else { /* end of a paragraph: don't fill line */
1451 spaces_per_word = 1;
1452 extra_spaces = 0;
1455 multiple_spacing = false;
1456 for (j=k=spaces=0; j < line_len; j++) {
1457 if (k == max_chars)
1458 break;
1460 c = line_begin[j];
1461 switch (c) {
1462 case ' ':
1463 case 0:
1464 if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
1465 for (j=0; j<par_indent_spaces; j++)
1466 scratch_buffer[k++] = ' ';
1467 j=0;
1469 else if (!multiple_spacing) {
1470 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1471 scratch_buffer[k++] = ' ';
1472 spaces++;
1474 multiple_spacing = true;
1475 break;
1476 default:
1477 scratch_buffer[k++] = c;
1478 multiple_spacing = false;
1479 break;
1482 if (col != -1) {
1483 scratch_buffer[k] = 0;
1484 endptr = decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
1485 *endptr = 0;
1488 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1489 if ((col != -1) && (line_width > col)) {
1490 str = oldstr = line_begin;
1491 k = col;
1492 width = 0;
1493 while( (width<draw_columns) && (oldstr<line_end) )
1495 oldstr = get_ucs(oldstr, &ch);
1496 if (k > 0) {
1497 k -= glyph_width(ch);
1498 line_begin = oldstr;
1499 } else {
1500 width += glyph_width(ch);
1504 if(prefs.view_mode==WIDE)
1505 endptr = rb->iso_decode(line_begin, utf8_buffer,
1506 prefs.encoding, oldstr-line_begin);
1507 else
1508 endptr = rb->iso_decode(line_begin, utf8_buffer,
1509 prefs.encoding, line_end-line_begin);
1510 *endptr = 0;
1514 /* display on screen the displayed part of the line */
1515 if (col != -1 && line_width > col)
1517 int dpage = (cline+i <= display_lines)?cpage:cpage+1;
1518 int dline = cline+i - ((cline+i <= display_lines)?0:display_lines);
1519 bool bflag = (viewer_find_bookmark(dpage, dline) >= 0);
1520 #ifdef HAVE_LCD_BITMAP
1521 int dy = i * pf->height + header_height;
1522 int dx = (prefs.alignment == LEFT) ? left_col : LCD_WIDTH - line_width;
1523 #endif
1524 if (bflag)
1525 #ifdef HAVE_LCD_BITMAP
1527 rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG);
1528 rb->lcd_fillrect(left_col, dy, LCD_WIDTH - left_col, pf->height);
1529 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1531 rb->lcd_putsxy(dx, dy, utf8_buffer);
1532 rb->lcd_set_drawmode(DRMODE_SOLID);
1533 #else
1535 rb->lcd_puts(left_col, i, BOOKMARK_ICON);
1537 rb->lcd_puts(left_col+1, i, utf8_buffer);
1538 #endif
1540 if (line_width > max_line_len)
1541 max_line_len = line_width;
1543 if (i == 0)
1544 next_line_ptr = line_end;
1546 next_screen_ptr = line_end;
1547 if (BUFFER_OOB(next_screen_ptr))
1548 next_screen_ptr = NULL;
1550 #ifdef HAVE_LCD_BITMAP
1551 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1553 if (prefs.need_scrollbar)
1554 viewer_scrollbar();
1555 #else
1556 next_screen_to_draw_ptr = next_screen_ptr;
1557 #endif
1559 #ifdef HAVE_LCD_BITMAP
1560 /* show header */
1561 viewer_show_header();
1563 /* show footer */
1564 viewer_show_footer();
1565 #endif
1567 if (col != -1)
1568 rb->lcd_update();
1571 static void viewer_top(void)
1573 /* Read top of file into buffer
1574 and point screen pointer to top */
1575 if (file_pos != 0)
1577 rb->splash(0, "Loading...");
1579 file_pos = 0;
1580 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1581 fill_buffer(0, buffer, buffer_size);
1584 screen_top_ptr = buffer;
1585 cpage = 1;
1586 cline = 1;
1589 static void viewer_bottom(void)
1591 unsigned char *line_begin;
1592 unsigned char *line_end;
1594 rb->splash(0, "Loading...");
1596 if (last_screen_top_ptr)
1598 cpage = lpage;
1599 cline = 1;
1600 screen_top_ptr = last_screen_top_ptr;
1601 file_pos = last_file_pos;
1602 fill_buffer(file_pos, buffer, buffer_size);
1603 buffer_end = BUFFER_END();
1604 return;
1607 line_end = screen_top_ptr;
1609 while (!BUFFER_EOF() || !BUFFER_OOB(line_end))
1611 get_next_line_position(&line_begin, &line_end, NULL);
1612 if (line_end == NULL)
1613 break;
1615 increment_current_line();
1616 if (cline == 1)
1617 screen_top_ptr = line_end;
1619 lpage = cpage;
1620 cline = 1;
1621 last_screen_top_ptr = screen_top_ptr;
1622 last_file_pos = file_pos;
1623 buffer_end = BUFFER_END();
1626 #ifdef HAVE_LCD_BITMAP
1627 static void init_need_scrollbar(void) {
1628 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1629 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1630 viewer_draw(-1);
1631 prefs.need_scrollbar = NEED_SCROLLBAR();
1632 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1633 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1634 calc_max_width();
1637 static void init_header_and_footer(void)
1639 header_height = 0;
1640 footer_height = 0;
1641 if (rb->global_settings->statusbar == STATUSBAR_TOP)
1643 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1644 header_height = STATUSBAR_HEIGHT;
1646 if (prefs.footer_mode == FT_SBAR)
1647 prefs.footer_mode = FT_NONE;
1648 else if (prefs.footer_mode == FT_BOTH)
1649 prefs.footer_mode = FT_PAGE;
1651 else if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
1653 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1654 footer_height = STATUSBAR_HEIGHT;
1656 if (prefs.header_mode == HD_SBAR)
1657 prefs.header_mode = HD_NONE;
1658 else if (prefs.header_mode == HD_BOTH)
1659 prefs.header_mode = HD_PATH;
1661 else /* STATUSBAR_OFF || STATUSBAR_CUSTOM */
1663 if (prefs.header_mode == HD_SBAR)
1664 prefs.header_mode = HD_NONE;
1665 else if (prefs.header_mode == HD_BOTH)
1666 prefs.header_mode = HD_PATH;
1668 if (prefs.footer_mode == FT_SBAR)
1669 prefs.footer_mode = FT_NONE;
1670 else if (prefs.footer_mode == FT_BOTH)
1671 prefs.footer_mode = FT_PAGE;
1674 if (prefs.header_mode == HD_NONE || prefs.header_mode == HD_PATH ||
1675 prefs.footer_mode == FT_NONE || prefs.footer_mode == FT_PAGE)
1676 rb->gui_syncstatusbar_draw(rb->statusbars, false);
1678 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1679 header_height += pf->height;
1680 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1681 footer_height += pf->height;
1683 display_lines = (LCD_HEIGHT - header_height - footer_height) / pf->height;
1685 lpage = 0;
1686 last_file_pos = 0;
1687 last_screen_top_ptr = NULL;
1690 static bool change_font(unsigned char *font)
1692 unsigned char buf[MAX_PATH];
1694 if (font == NULL || *font == '\0')
1695 return false;
1697 rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font);
1698 if (rb->font_load(NULL, buf) < 0) {
1699 rb->splash(HZ/2, "Font load failed.");
1700 return false;
1703 return true;
1705 #endif
1707 static bool viewer_init(void)
1709 #ifdef HAVE_LCD_BITMAP
1710 /* initialize fonts */
1711 pf = rb->font_get(FONT_UI);
1712 if (pf == NULL)
1713 return false;
1715 draw_columns = display_columns = LCD_WIDTH;
1716 #else
1717 /* REAL fixed pitch :) all chars use up 1 cell */
1718 display_lines = 2;
1719 draw_columns = display_columns = 11;
1720 par_indent_spaces = 2;
1721 #endif
1723 fd = rb->open(file_name, O_RDONLY);
1724 if (fd < 0)
1725 return false;
1727 /* Init mac_text value used in processing buffer */
1728 mac_text = false;
1730 return true;
1733 /* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8,
1734 * then file size decreases only BOM_SIZE.
1736 static void get_filesize(void)
1738 file_size = rb->filesize(fd);
1739 if (file_size == -1)
1740 return;
1742 if (prefs.encoding == UTF_8 && is_bom)
1743 file_size -= BOM_SIZE;
1746 static int bm_comp(const void *a, const void *b)
1748 struct bookmark_info *pa;
1749 struct bookmark_info *pb;
1751 pa = (struct bookmark_info*)a;
1752 pb = (struct bookmark_info*)b;
1754 if (pa->page != pb->page)
1755 return pa->page - pb->page;
1757 return pa->line - pb->line;
1760 static void viewer_add_bookmark(void)
1762 if (bookmark_count >= MAX_BOOKMARKS-1)
1763 return;
1765 bookmarks[bookmark_count].file_position
1766 = file_pos + screen_top_ptr - buffer;
1767 bookmarks[bookmark_count].page = cpage;
1768 bookmarks[bookmark_count].line = cline;
1769 bookmarks[bookmark_count].flag = BOOKMARK_USER;
1770 bookmark_count++;
1773 static int viewer_add_last_read_bookmark(void)
1775 int i;
1777 i = viewer_find_bookmark(cpage, cline);
1778 if (i >= 0)
1779 bookmarks[i].flag |= BOOKMARK_LAST;
1780 else
1782 viewer_add_bookmark();
1783 i = bookmark_count-1;
1784 bookmarks[i].flag = BOOKMARK_LAST;
1786 return i;
1789 static void viewer_remove_bookmark(int i)
1791 int j;
1793 if (i < 0 || i >= bookmark_count)
1794 return;
1796 for (j = i+1; j < bookmark_count; j++)
1797 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1798 sizeof(struct bookmark_info));
1800 bookmark_count--;
1803 static void viewer_remove_last_read_bookmark(void)
1805 int i, j;
1807 for (i = 0; i < bookmark_count; i++)
1809 if (bookmarks[i].flag & BOOKMARK_LAST)
1811 if (bookmarks[i].flag == BOOKMARK_LAST)
1813 for (j = i+1; j < bookmark_count; j++)
1814 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1815 sizeof(struct bookmark_info));
1817 bookmark_count--;
1819 else
1820 bookmarks[i].flag = BOOKMARK_USER;
1821 break;
1826 static int viewer_get_last_read_bookmark(void)
1828 int i;
1830 for (i = 0; i < bookmark_count; i++)
1832 if (bookmarks[i].flag & BOOKMARK_LAST)
1833 return i;
1835 return -1;
1838 static void viewer_select_bookmark(int initval)
1840 int i;
1841 int ipage = 0;
1842 int iline = 0;
1843 int screen_pos;
1844 int screen_top;
1845 int selected = -1;
1847 struct opt_items items[bookmark_count];
1848 unsigned char names[bookmark_count][38];
1850 if (initval >= 0 && initval < bookmark_count)
1852 ipage = bookmarks[initval].page;
1853 iline = bookmarks[initval].line;
1856 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
1857 bm_comp);
1859 for (i = 0; i < bookmark_count; i++)
1861 rb->snprintf(names[i], sizeof(names[0]),
1862 #if CONFIG_KEYPAD != PLAYER_PAD
1863 "%sPage: %d Line: %d",
1864 #else
1865 "%sP:%d L:%d",
1866 #endif
1867 (bookmarks[i].flag&BOOKMARK_LAST)? "*":" ",
1868 bookmarks[i].page,
1869 bookmarks[i].line);
1870 items[i].string = names[i];
1871 items[i].voice_id = -1;
1872 if (selected < 0 && bookmarks[i].page == ipage && bookmarks[i].line == iline)
1873 selected = i;
1876 rb->set_option("Select bookmark", &selected, INT, items,
1877 sizeof(items) / sizeof(items[0]), NULL);
1879 if (selected < 0 || selected >= bookmark_count)
1881 if (initval < 0 || (selected = viewer_get_last_read_bookmark()) < 0)
1883 if (initval < 0)
1884 rb->splash(HZ, "Start the first page.");
1885 file_pos = 0;
1886 screen_top_ptr = buffer;
1887 cpage = 1;
1888 cline = 1;
1889 buffer_end = BUFFER_END();
1890 return;
1894 screen_pos = bookmarks[selected].file_position;
1895 screen_top = screen_pos % buffer_size;
1896 file_pos = screen_pos - screen_top;
1897 screen_top_ptr = buffer + screen_top;
1898 cpage = bookmarks[selected].page;
1899 cline = bookmarks[selected].line;
1900 buffer_end = BUFFER_END();
1903 static void viewer_default_preferences(void)
1905 prefs.word_mode = WRAP;
1906 prefs.line_mode = NORMAL;
1907 prefs.view_mode = NARROW;
1908 prefs.alignment = LEFT;
1909 prefs.scroll_mode = PAGE;
1910 prefs.page_mode = NO_OVERLAP;
1911 prefs.scrollbar_mode = SB_OFF;
1912 rb->memset(prefs.font, 0, MAX_PATH);
1913 #ifdef HAVE_LCD_BITMAP
1914 prefs.header_mode = HD_BOTH;
1915 prefs.footer_mode = FT_BOTH;
1916 rb->snprintf(prefs.font, MAX_PATH, "%s", rb->global_settings->font_file);
1917 #else
1918 prefs.header_mode = HD_NONE;
1919 prefs.footer_mode = FT_NONE;
1920 #endif
1921 prefs.autoscroll_speed = 1;
1922 /* Set codepage to system default */
1923 prefs.encoding = rb->global_settings->default_codepage;
1926 static bool viewer_read_preferences(int pfd, int version, struct preferences *prf)
1928 unsigned char buf[PREFERENCES_SIZE];
1929 unsigned char *p = buf;
1930 int read_size = PREFERENCES_SIZE;
1932 if (version == 0)
1933 read_size--;
1935 if (rb->read(pfd, buf, read_size) != read_size)
1936 return false;
1938 prf->word_mode = *p++;
1939 prf->line_mode = *p++;
1940 prf->view_mode = *p++;
1941 if (version > 0)
1942 prf->alignment = *p++;
1943 else
1944 prf->alignment = LEFT;
1945 prf->encoding = *p++;
1946 prf->scrollbar_mode = *p++;
1947 prf->need_scrollbar = *p++;
1948 prf->page_mode = *p++;
1949 prf->header_mode = *p++;
1950 prf->footer_mode = *p++;
1951 prf->scroll_mode = *p++;
1952 prf->autoscroll_speed = *p++;
1953 rb->memcpy(prf->font, p, MAX_PATH);
1954 return true;
1957 static bool viewer_write_preferences(int pfd, const struct preferences *prf)
1959 unsigned char buf[PREFERENCES_SIZE];
1960 unsigned char *p = buf;
1962 *p++ = prf->word_mode;
1963 *p++ = prf->line_mode;
1964 *p++ = prf->view_mode;
1965 *p++ = prf->alignment;
1966 *p++ = prf->encoding;
1967 *p++ = prf->scrollbar_mode;
1968 *p++ = prf->need_scrollbar;
1969 *p++ = prf->page_mode;
1970 *p++ = prf->header_mode;
1971 *p++ = prf->footer_mode;
1972 *p++ = prf->scroll_mode;
1973 *p++ = prf->autoscroll_speed;
1974 rb->memcpy(p, prf->font, MAX_PATH);
1976 return (rb->write(pfd, buf, sizeof(buf)) == sizeof(buf));
1979 static bool viewer_read_bookmark_info(int bfd, struct bookmark_info *b)
1981 unsigned char buf[BOOKMARK_SIZE];
1983 if (rb->read(bfd, buf, sizeof(buf)) != sizeof(buf))
1984 return false;
1986 b->file_position = (buf[0] << 24)|(buf[1] << 16)|(buf[2] << 8)|buf[3];
1987 b->page = (buf[4] << 8)|buf[5];
1988 b->line = buf[6];
1989 b->flag = buf[7];
1991 return true;
1994 static bool viewer_read_bookmark_infos(int bfd)
1996 unsigned char c;
1997 int i;
1999 if (rb->read(bfd, &c, 1) != 1)
2001 bookmark_count = 0;
2002 return false;
2005 bookmark_count = c;
2006 if (bookmark_count > MAX_BOOKMARKS)
2007 bookmark_count = MAX_BOOKMARKS;
2009 for (i = 0; i < bookmark_count; i++)
2011 if (!viewer_read_bookmark_info(bfd, &bookmarks[i]))
2013 bookmark_count = i;
2014 return false;
2017 return true;
2020 static bool viewer_write_bookmark_info(int bfd, struct bookmark_info *b)
2022 unsigned char buf[BOOKMARK_SIZE];
2023 unsigned char *p = buf;
2024 unsigned long ul;
2026 ul = b->file_position;
2027 *p++ = ul >> 24;
2028 *p++ = ul >> 16;
2029 *p++ = ul >> 8;
2030 *p++ = ul;
2032 ul = b->page;
2033 *p++ = ul >> 8;
2034 *p++ = ul;
2036 *p++ = b->line;
2037 *p = b->flag;
2039 return (rb->write(bfd, buf, sizeof(buf)) == sizeof(buf));
2042 static bool viewer_write_bookmark_infos(int bfd)
2044 unsigned char c = bookmark_count;
2045 int i;
2047 if (rb->write(bfd, &c, 1) != 1)
2048 return false;
2050 for (i = 0; i < bookmark_count; i++)
2052 if (!viewer_write_bookmark_info(bfd, &bookmarks[i]))
2053 return false;
2056 return true;
2059 static bool viewer_load_global_settings(void)
2061 unsigned buf[GLOBAL_SETTINGS_H_SIZE];
2062 int sfd = rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY);
2063 int version;
2064 bool res = false;
2066 if (sfd < 0)
2067 return false;
2069 if ((rb->read(sfd, buf, GLOBAL_SETTINGS_H_SIZE) == GLOBAL_SETTINGS_H_SIZE) ||
2070 (rb->memcmp(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1) == 0))
2072 version = buf[GLOBAL_SETTINGS_H_SIZE - 1] - GLOBAL_SETTINGS_FIRST_VERSION;
2073 res = viewer_read_preferences(sfd, version, &prefs);
2075 rb->close(sfd);
2076 return res;
2079 static bool viewer_save_global_settings(void)
2081 int sfd = rb->open(GLOBAL_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC);
2082 unsigned char buf[GLOBAL_SETTINGS_H_SIZE];
2084 if (sfd < 0)
2085 return false;
2087 rb->memcpy(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1);
2088 buf[GLOBAL_SETTINGS_H_SIZE - 1] = GLOBAL_SETTINGS_VERSION;
2090 if (rb->write(sfd, buf, GLOBAL_SETTINGS_H_SIZE) != GLOBAL_SETTINGS_H_SIZE ||
2091 !viewer_write_preferences(sfd, &prefs))
2093 rb->close(sfd);
2094 rb->remove(GLOBAL_SETTINGS_TMP_FILE);
2095 return false;
2097 rb->close(sfd);
2098 rb->remove(GLOBAL_SETTINGS_FILE);
2099 rb->rename(GLOBAL_SETTINGS_TMP_FILE, GLOBAL_SETTINGS_FILE);
2100 return true;
2103 static bool viewer_convert_settings(int sfd, int dfd, int old_ver)
2105 struct preferences new_prefs;
2106 off_t old_pos;
2107 off_t new_pos;
2108 unsigned char buf[MAX_PATH + 2];
2109 int settings_size;
2111 rb->read(sfd, buf, MAX_PATH + 2);
2112 rb->write(dfd, buf, MAX_PATH + 2);
2114 settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
2116 old_pos = rb->lseek(sfd, 0, SEEK_CUR);
2117 new_pos = rb->lseek(dfd, 0, SEEK_CUR);
2120 * when the settings size != preferences size + bookmarks size,
2121 * settings data are considered to be old version.
2123 if (old_ver > 0 && ((settings_size - PREFERENCES_SIZE) % 8) == 0)
2124 old_ver = 0;
2126 if (!viewer_read_preferences(sfd, old_ver, &new_prefs))
2127 return false;
2129 if (!viewer_write_preferences(dfd, &new_prefs))
2130 return false;
2132 settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos);
2134 if (settings_size > 0)
2136 rb->read(sfd, buf, settings_size);
2137 rb->write(dfd, buf, settings_size);
2140 settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos;
2141 buf[0] = settings_size >> 8;
2142 buf[1] = settings_size;
2143 rb->lseek(dfd, new_pos - 2, SEEK_SET);
2144 rb->write(dfd, buf, 2);
2145 rb->lseek(dfd, settings_size, SEEK_CUR);
2146 return true;
2149 static bool viewer_convert_settings_file(void)
2151 unsigned char buf[SETTINGS_H_SIZE+2];
2152 int sfd;
2153 int tfd;
2154 int i;
2155 int fcount;
2156 int version;
2157 bool res = true;
2159 if ((sfd = rb->open(SETTINGS_FILE, O_RDONLY)) < 0)
2160 return false;
2162 if ((tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
2164 rb->close(sfd);
2165 return false;
2168 rb->read(sfd, buf, SETTINGS_H_SIZE + 2);
2170 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2171 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE + 1];
2172 buf[SETTINGS_H_SIZE - 1] = SETTINGS_VERSION;
2174 rb->write(tfd, buf, SETTINGS_H_SIZE + 2);
2176 for (i = 0; i < fcount; i++)
2178 if (!viewer_convert_settings(sfd, tfd, version))
2180 res = false;
2181 break;
2185 rb->close(sfd);
2186 rb->close(tfd);
2188 if (!res)
2189 rb->remove(SETTINGS_TMP_FILE);
2190 else
2192 rb->remove(SETTINGS_FILE);
2193 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2195 return res;
2198 static bool viewer_load_settings(void)
2200 unsigned char buf[MAX_PATH+2];
2201 unsigned int fcount;
2202 unsigned int i;
2203 bool res = false;
2204 int sfd;
2205 unsigned int size;
2206 int version;
2208 sfd = rb->open(SETTINGS_FILE, O_RDONLY);
2209 if (sfd < 0)
2210 goto read_end;
2212 if ((rb->read(sfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2213 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2215 /* illegal setting file */
2216 rb->close(sfd);
2218 if (rb->file_exists(SETTINGS_FILE))
2219 rb->remove(SETTINGS_FILE);
2221 goto read_end;
2224 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2226 rb->close(sfd);
2227 if (!viewer_convert_settings_file())
2228 goto read_end;
2230 return viewer_load_settings();
2233 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2234 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2235 for (i = 0; i < fcount; i++)
2237 if (rb->read(sfd, buf, MAX_PATH+2) != MAX_PATH+2)
2238 break;
2240 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2243 * when the settings size != preferences size + bookmarks size,
2244 * the settings file converts to the newer.
2246 if (version > 0 && ((size - PREFERENCES_SIZE) % 8) == 0)
2248 rb->close(sfd);
2249 if (!viewer_convert_settings_file())
2250 break;
2252 return viewer_load_settings();
2255 if (rb->strcmp(buf, file_name))
2257 if (rb->lseek(sfd, size, SEEK_CUR) < 0)
2258 break;
2259 continue;
2261 if (!viewer_read_preferences(sfd, version, &prefs))
2262 break;
2264 res = viewer_read_bookmark_infos(sfd);
2265 break;
2268 rb->close(sfd);
2270 read_end:
2271 if (!res)
2273 /* load global settings */
2274 if (!viewer_load_global_settings())
2275 viewer_default_preferences();
2277 file_pos = 0;
2278 screen_top_ptr = buffer;
2279 cpage = 1;
2280 cline = 1;
2281 bookmark_count = 0;
2284 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
2285 calc_max_width();
2287 if (bookmark_count > 1)
2288 viewer_select_bookmark(-1);
2289 else if (bookmark_count == 1)
2291 int screen_pos;
2292 int screen_top;
2294 screen_pos = bookmarks[0].file_position;
2295 screen_top = screen_pos % buffer_size;
2296 file_pos = screen_pos - screen_top;
2297 screen_top_ptr = buffer + screen_top;
2298 cpage = bookmarks[0].page;
2299 cline = bookmarks[0].line;
2302 viewer_remove_last_read_bookmark();
2304 check_bom();
2305 get_filesize();
2307 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
2309 if (BUFFER_OOB(screen_top_ptr))
2310 screen_top_ptr = buffer;
2312 fill_buffer(file_pos, buffer, buffer_size);
2313 if (prefs.scroll_mode == PAGE && cline > 1)
2314 viewer_scroll_to_top_line();
2316 /* remember the current position */
2317 start_position = file_pos + screen_top_ptr - buffer;
2319 #ifdef HAVE_LCD_BITMAP
2320 /* load prefs font if it is different than the global settings font */
2321 if (rb->strcmp(prefs.font, rb->global_settings->font_file)) {
2322 if (!change_font(prefs.font)) {
2323 /* fallback by resetting prefs font to the global settings font */
2324 rb->memset(prefs.font, 0, MAX_PATH);
2325 rb->snprintf(prefs.font, MAX_PATH, "%s",
2326 rb->global_settings->font_file);
2328 if (!change_font(prefs.font))
2329 return false;
2333 init_need_scrollbar();
2334 init_header_and_footer();
2335 #endif
2337 return true;
2340 static bool copy_bookmark_file(int sfd, int dfd, off_t start, off_t size)
2342 off_t rsize;
2344 if (rb->lseek(sfd, start, SEEK_SET) < 0)
2345 return false;
2347 while (size > 0)
2349 if (size > buffer_size)
2350 rsize = buffer_size;
2351 else
2352 rsize = size;
2353 size -= rsize;
2355 if (rb->read(sfd, buffer, rsize) != rsize ||
2356 rb->write(dfd, buffer, rsize) != rsize)
2357 return false;
2359 return true;
2362 static bool viewer_save_settings(void)
2364 unsigned char buf[MAX_PATH+2];
2365 unsigned int fcount = 0;
2366 unsigned int i;
2367 int idx;
2368 int ofd;
2369 int tfd;
2370 off_t first_copy_size = 0;
2371 off_t second_copy_start_pos = 0;
2372 off_t size;
2374 /* add reading page to bookmarks */
2375 idx = viewer_find_bookmark(cpage, cline);
2376 if (idx >= 0)
2377 bookmarks[idx].flag |= BOOKMARK_LAST;
2378 else
2380 viewer_add_bookmark();
2381 bookmarks[bookmark_count-1].flag = BOOKMARK_LAST;
2384 tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC);
2385 if (tfd < 0)
2386 return false;
2388 ofd = rb->open(SETTINGS_FILE, O_RDWR);
2389 if (ofd >= 0)
2391 if ((rb->read(ofd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2392 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2394 rb->close(ofd);
2395 goto save_err;
2398 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2400 rb->close(ofd);
2401 if (!viewer_convert_settings_file())
2402 goto save_err;
2404 viewer_save_settings();
2407 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2409 for (i = 0; i < fcount; i++)
2411 if (rb->read(ofd, buf, MAX_PATH+2) != MAX_PATH+2)
2413 rb->close(ofd);
2414 goto save_err;
2416 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2417 if (rb->strcmp(buf, file_name))
2419 if (rb->lseek(ofd, size, SEEK_CUR) < 0)
2421 rb->close(ofd);
2422 goto save_err;
2425 else
2427 first_copy_size = rb->lseek(ofd, 0, SEEK_CUR);
2428 if (first_copy_size < 0)
2430 rb->close(ofd);
2431 goto save_err;
2433 second_copy_start_pos = first_copy_size + size;
2434 first_copy_size -= MAX_PATH+2;
2435 fcount--;
2436 break;
2439 if (first_copy_size == 0)
2440 first_copy_size = rb->filesize(ofd);
2442 if (!copy_bookmark_file(ofd, tfd, 0, first_copy_size))
2444 rb->close(ofd);
2445 goto save_err;
2447 if (second_copy_start_pos > 0)
2449 if (!copy_bookmark_file(ofd, tfd, second_copy_start_pos,
2450 rb->filesize(ofd) - second_copy_start_pos))
2452 rb->close(ofd);
2453 goto save_err;
2456 rb->close(ofd);
2458 else
2460 rb->memcpy(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1);
2461 buf[SETTINGS_H_SIZE-1] = SETTINGS_VERSION;
2462 buf[SETTINGS_H_SIZE ] = 0;
2463 buf[SETTINGS_H_SIZE+1] = 0;
2464 if (rb->write(tfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2)
2465 goto save_err;
2468 /* copy to current read file's bookmarks */
2469 rb->memset(buf, 0, MAX_PATH);
2470 rb->snprintf(buf, MAX_PATH, "%s", file_name);
2472 size = PREFERENCES_SIZE + bookmark_count * BOOKMARK_SIZE + 1;
2473 buf[MAX_PATH] = size >> 8;
2474 buf[MAX_PATH+1] = size;
2476 if (rb->write(tfd, buf, MAX_PATH+2) != MAX_PATH+2)
2477 goto save_err;
2479 if (!viewer_write_preferences(tfd, &prefs))
2480 goto save_err;
2482 if (!viewer_write_bookmark_infos(tfd))
2483 goto save_err;
2485 if (rb->lseek(tfd, SETTINGS_H_SIZE, SEEK_SET) < 0)
2486 goto save_err;
2488 fcount++;
2489 buf[0] = fcount >> 8;
2490 buf[1] = fcount;
2492 if (rb->write(tfd, buf, 2) != 2)
2493 goto save_err;
2495 rb->close(tfd);
2497 rb->remove(SETTINGS_FILE);
2498 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2500 return true;
2502 save_err:
2503 rb->close(tfd);
2504 rb->remove(SETTINGS_TMP_FILE);
2505 return false;
2508 static void viewer_exit(void *parameter)
2510 (void)parameter;
2512 /* save preference and bookmarks */
2513 if (!viewer_save_settings())
2514 rb->splash(HZ, "Can't save preference and bookmarks.");
2516 rb->close(fd);
2517 #ifdef HAVE_LCD_BITMAP
2518 if (rb->strcmp(prefs.font, rb->global_settings->font_file))
2519 change_font(rb->global_settings->font_file);
2520 #endif
2523 static void calc_page(void)
2525 int i;
2526 unsigned char *line_begin;
2527 unsigned char *line_end;
2528 off_t sfp;
2529 unsigned char *sstp;
2531 rb->splash(0, "Calculating page/line number...");
2533 /* add reading page to bookmarks */
2534 viewer_add_last_read_bookmark();
2536 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
2537 bm_comp);
2539 cpage = 1;
2540 cline = 1;
2541 file_pos = 0;
2542 screen_top_ptr = buffer;
2543 buffer_end = BUFFER_END();
2545 fill_buffer(file_pos, buffer, buffer_size);
2546 line_end = line_begin = buffer;
2548 for (i = 0; i < bookmark_count; i++)
2550 sfp = bookmarks[i].file_position;
2551 sstp = buffer;
2553 while ((line_begin > sstp || sstp >= line_end) ||
2554 (file_pos > sfp || sfp >= file_pos + BUFFER_END() - buffer))
2556 get_next_line_position(&line_begin, &line_end, NULL);
2557 if (line_end == NULL)
2558 break;
2560 next_line_ptr = line_end;
2562 if (sstp == buffer &&
2563 file_pos <= sfp && sfp < file_pos + BUFFER_END() - buffer)
2564 sstp = sfp - file_pos + buffer;
2566 increment_current_line();
2569 decrement_current_line();
2570 bookmarks[i].page = cpage;
2571 bookmarks[i].line = cline;
2572 bookmarks[i].file_position = file_pos + (line_begin - buffer);
2573 increment_current_line();
2576 /* remove reading page's bookmark */
2577 for (i = 0; i < bookmark_count; i++)
2579 if (bookmarks[i].flag & BOOKMARK_LAST)
2581 int screen_pos;
2582 int screen_top;
2584 screen_pos = bookmarks[i].file_position;
2585 screen_top = screen_pos % buffer_size;
2586 file_pos = screen_pos - screen_top;
2587 screen_top_ptr = buffer + screen_top;
2589 cpage = bookmarks[i].page;
2590 cline = bookmarks[i].line;
2591 bookmarks[i].flag ^= BOOKMARK_LAST;
2592 buffer_end = BUFFER_END();
2594 fill_buffer(file_pos, buffer, buffer_size);
2596 if (bookmarks[i].flag == 0)
2597 viewer_remove_bookmark(i);
2599 if (prefs.scroll_mode == PAGE && cline > 1)
2600 viewer_scroll_to_top_line();
2601 break;
2606 static int col_limit(int col)
2608 if (col < 0)
2609 col = 0;
2610 else
2611 if (col >= max_width - draw_columns)
2612 col = max_width - draw_columns;
2614 return col;
2617 /* settings helper functions */
2619 static bool encoding_setting(void)
2621 static struct opt_items names[NUM_CODEPAGES];
2622 int idx;
2623 bool res;
2624 enum codepages oldenc = prefs.encoding;
2626 for (idx = 0; idx < NUM_CODEPAGES; idx++)
2628 names[idx].string = rb->get_codepage_name(idx);
2629 names[idx].voice_id = -1;
2632 res = rb->set_option("Encoding", &prefs.encoding, INT, names,
2633 sizeof(names) / sizeof(names[0]), NULL);
2635 /* When prefs.encoding changes into UTF-8 or changes from UTF-8,
2636 * filesize (file_size) might change.
2637 * In addition, if prefs.encoding is UTF-8, then BOM does not read.
2639 if (oldenc != prefs.encoding && (oldenc == UTF_8 || prefs.encoding == UTF_8))
2641 check_bom();
2642 get_filesize();
2643 fill_buffer(file_pos, buffer, buffer_size);
2646 return res;
2649 static bool word_wrap_setting(void)
2651 static const struct opt_items names[] = {
2652 {"On", -1},
2653 {"Off (Chop Words)", -1},
2656 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
2657 names, 2, NULL);
2660 static bool line_mode_setting(void)
2662 static const struct opt_items names[] = {
2663 {"Normal", -1},
2664 {"Join Lines", -1},
2665 {"Expand Lines", -1},
2666 #ifdef HAVE_LCD_BITMAP
2667 {"Reflow Lines", -1},
2668 #endif
2671 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
2672 sizeof(names) / sizeof(names[0]), NULL);
2675 static bool view_mode_setting(void)
2677 static const struct opt_items names[] = {
2678 {"No (Narrow)", -1},
2679 {"Yes", -1},
2681 bool ret;
2682 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
2683 names , 2, NULL);
2684 if (prefs.view_mode == NARROW)
2685 col = 0;
2686 calc_max_width();
2687 return ret;
2690 static bool scroll_mode_setting(void)
2692 static const struct opt_items names[] = {
2693 {"Scroll by Page", -1},
2694 {"Scroll by Line", -1},
2697 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
2698 names, 2, NULL);
2701 #ifdef HAVE_LCD_BITMAP
2702 static bool page_mode_setting(void)
2704 static const struct opt_items names[] = {
2705 {"No", -1},
2706 {"Yes", -1},
2709 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
2710 names, 2, NULL);
2713 static bool scrollbar_setting(void)
2715 static const struct opt_items names[] = {
2716 {"Off", -1},
2717 {"On", -1}
2720 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
2721 names, 2, NULL);
2724 static bool header_setting(void)
2726 int len = (rb->global_settings->statusbar == STATUSBAR_TOP)? 4 : 2;
2727 struct opt_items names[len];
2729 names[0].string = "None";
2730 names[0].voice_id = -1;
2731 names[1].string = "File path";
2732 names[1].voice_id = -1;
2734 if (rb->global_settings->statusbar == STATUSBAR_TOP)
2736 names[2].string = "Status bar";
2737 names[2].voice_id = -1;
2738 names[3].string = "Both";
2739 names[3].voice_id = -1;
2742 return rb->set_option("Show Header", &prefs.header_mode, INT,
2743 names, len, NULL);
2746 static bool footer_setting(void)
2748 int len = (rb->global_settings->statusbar == STATUSBAR_BOTTOM)? 4 : 2;
2749 struct opt_items names[len];
2751 names[0].string = "None";
2752 names[0].voice_id = -1;
2753 names[1].string = "Page Num";
2754 names[1].voice_id = -1;
2756 if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
2758 names[2].string = "Status bar";
2759 names[2].voice_id = -1;
2760 names[3].string = "Both";
2761 names[3].voice_id = -1;
2764 return rb->set_option("Show Footer", &prefs.footer_mode, INT,
2765 names, len, NULL);
2768 static int font_comp(const void *a, const void *b)
2770 struct opt_items *pa;
2771 struct opt_items *pb;
2773 pa = (struct opt_items *)a;
2774 pb = (struct opt_items *)b;
2776 return rb->strcmp(pa->string, pb->string);
2779 static bool font_setting(void)
2781 int count = 0;
2782 DIR *dir;
2783 struct dirent *entry;
2784 int i = 0;
2785 int len;
2786 int new_font = 0;
2787 int old_font;
2788 bool res;
2789 int size = 0;
2791 dir = rb->opendir(FONT_DIR);
2792 if (!dir)
2794 rb->splash(HZ/2, "Font dir is not accessible");
2795 return false;
2798 while (1)
2800 entry = rb->readdir(dir);
2802 if (entry == NULL)
2803 break;
2805 len = rb->strlen(entry->d_name);
2806 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2807 continue;
2808 size += len-3;
2809 count++;
2811 rb->closedir(dir);
2813 struct opt_items names[count];
2814 unsigned char font_names[size];
2815 unsigned char *p = font_names;
2817 dir = rb->opendir(FONT_DIR);
2818 if (!dir)
2820 rb->splash(HZ/2, "Font dir is not accessible");
2821 return false;
2824 while (1)
2826 entry = rb->readdir(dir);
2828 if (entry == NULL)
2829 break;
2831 len = rb->strlen(entry->d_name);
2832 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2833 continue;
2835 rb->snprintf(p, len-3, "%s", entry->d_name);
2836 names[i].string = p;
2837 names[i].voice_id = -1;
2838 p += len-3;
2839 i++;
2840 if (i >= count)
2841 break;
2843 rb->closedir(dir);
2845 rb->qsort(names, count, sizeof(struct opt_items), font_comp);
2847 for (i = 0; i < count; i++)
2849 if (!rb->strcmp(names[i].string, prefs.font))
2851 new_font = i;
2852 break;
2855 old_font = new_font;
2857 res = rb->set_option("Select Font", &new_font, INT,
2858 names, count, NULL);
2860 if (new_font != old_font)
2862 /* load selected font */
2863 if (!change_font((unsigned char *)names[new_font].string)) {
2864 /* revert by re-loading the preferences font */
2865 change_font(prefs.font);
2866 return false;
2868 rb->memset(prefs.font, 0, MAX_PATH);
2869 rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string);
2872 return res;
2875 static bool alignment_setting(void)
2877 static const struct opt_items names[] = {
2878 {"Left", -1},
2879 {"Right", -1},
2882 return rb->set_option("Alignment", &prefs.alignment, INT,
2883 names , 2, NULL);
2885 #endif
2887 static bool autoscroll_speed_setting(void)
2889 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
2890 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
2893 MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
2894 NULL, NULL, Icon_NOICON);
2895 MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
2896 NULL, NULL, Icon_NOICON);
2897 MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
2898 NULL, NULL, Icon_NOICON);
2899 MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
2900 NULL, NULL, Icon_NOICON);
2901 #ifdef HAVE_LCD_BITMAP
2902 MENUITEM_FUNCTION(alignment_item, 0, "Alignment", alignment_setting,
2903 NULL, NULL, Icon_NOICON);
2904 MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
2905 NULL, NULL, Icon_NOICON);
2906 MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
2907 NULL, NULL, Icon_NOICON);
2908 MENUITEM_FUNCTION(header_item, 0, "Show Header", header_setting,
2909 NULL, NULL, Icon_NOICON);
2910 MENUITEM_FUNCTION(footer_item, 0, "Show Footer", footer_setting,
2911 NULL, NULL, Icon_NOICON);
2912 MENUITEM_FUNCTION(font_item, 0, "Font", font_setting,
2913 NULL, NULL, Icon_NOICON);
2914 #endif
2915 MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
2916 NULL, NULL, Icon_NOICON);
2917 MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
2918 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
2919 MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
2920 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
2921 #ifdef HAVE_LCD_BITMAP
2922 &alignment_item, &scrollbar_item, &page_mode_item, &header_item,
2923 &footer_item, &font_item,
2924 #endif
2925 &scroll_mode_item, &autoscroll_speed_item);
2927 static bool viewer_options_menu(bool is_global)
2929 bool result;
2930 struct preferences tmp_prefs;
2932 rb->memcpy(&tmp_prefs, &prefs, sizeof(struct preferences));
2934 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
2936 if (!is_global && rb->memcmp(&tmp_prefs, &prefs, sizeof(struct preferences)))
2938 /* Show-scrollbar mode for current view-width mode */
2939 #ifdef HAVE_LCD_BITMAP
2940 init_need_scrollbar();
2941 init_header_and_footer();
2942 #endif
2943 calc_page();
2945 return result;
2948 static void viewer_menu(void)
2950 int result;
2952 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
2953 "Return", "Viewer Options",
2954 "Show Playback Menu", "Select Bookmark",
2955 "Global Settings", "Quit");
2957 result = rb->do_menu(&menu, NULL, NULL, false);
2958 switch (result)
2960 case 0: /* return */
2961 break;
2962 case 1: /* change settings */
2963 done = viewer_options_menu(false);
2964 break;
2965 case 2: /* playback control */
2966 playback_control(NULL);
2967 break;
2968 case 3: /* select bookmark */
2969 viewer_select_bookmark(viewer_add_last_read_bookmark());
2970 viewer_remove_last_read_bookmark();
2971 fill_buffer(file_pos, buffer, buffer_size);
2972 if (prefs.scroll_mode == PAGE && cline > 1)
2973 viewer_scroll_to_top_line();
2974 break;
2975 case 4: /* change global settings */
2977 struct preferences orig_prefs;
2979 rb->memcpy(&orig_prefs, &prefs, sizeof(struct preferences));
2980 if (!viewer_load_global_settings())
2981 viewer_default_preferences();
2982 done = viewer_options_menu(true);
2983 viewer_save_global_settings();
2984 rb->memcpy(&prefs, &orig_prefs, sizeof(struct preferences));
2986 break;
2987 case 5: /* quit */
2988 viewer_exit(NULL);
2989 done = true;
2990 break;
2992 viewer_draw(col);
2995 enum plugin_status plugin_start(const void* file)
2997 int button, i, ok;
2998 int lastbutton = BUTTON_NONE;
2999 bool autoscroll = false;
3000 long old_tick;
3002 old_tick = *rb->current_tick;
3004 /* get the plugin buffer */
3005 buffer = rb->plugin_get_buffer((size_t *)&buffer_size);
3006 if (buffer_size == 0)
3008 rb->splash(HZ, "buffer does not allocate !!");
3009 return PLUGIN_ERROR;
3011 block_size = buffer_size / 3;
3012 buffer_size = 3 * block_size;
3014 if (!file)
3015 return PLUGIN_ERROR;
3017 file_name = file;
3018 ok = viewer_init();
3019 if (!ok) {
3020 rb->splash(HZ, "Error opening file.");
3021 return PLUGIN_ERROR;
3024 if (!viewer_load_settings()) /* load the preferences and bookmark */
3025 return PLUGIN_ERROR;
3027 #if LCD_DEPTH > 1
3028 rb->lcd_set_backdrop(NULL);
3029 #endif
3031 viewer_draw(col);
3033 while (!done) {
3035 if(autoscroll)
3037 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
3039 viewer_scroll_down(true);
3040 viewer_draw(col);
3041 old_tick = *rb->current_tick;
3045 button = rb->button_get_w_tmo(HZ/10);
3047 if (prefs.view_mode != WIDE) {
3048 /* when not in wide view mode, the SCREEN_LEFT and SCREEN_RIGHT
3049 buttons jump to the beginning and end of the file. To stop
3050 users doing this by accident, replace non-held occurrences
3051 with page up/down instead. */
3052 if (button == VIEWER_SCREEN_LEFT)
3053 button = VIEWER_PAGE_UP;
3054 else if (button == VIEWER_SCREEN_RIGHT)
3055 button = VIEWER_PAGE_DOWN;
3058 switch (button) {
3059 case VIEWER_MENU:
3060 #ifdef VIEWER_MENU2
3061 case VIEWER_MENU2:
3062 #endif
3063 viewer_menu();
3064 break;
3066 case VIEWER_AUTOSCROLL:
3067 #ifdef VIEWER_AUTOSCROLL_PRE
3068 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
3069 break;
3070 #endif
3071 autoscroll = !autoscroll;
3072 break;
3074 case VIEWER_PAGE_UP:
3075 case VIEWER_PAGE_UP | BUTTON_REPEAT:
3076 #ifdef VIEWER_PAGE_UP2
3077 case VIEWER_PAGE_UP2:
3078 case VIEWER_PAGE_UP2 | BUTTON_REPEAT:
3079 #endif
3080 if (prefs.scroll_mode == PAGE)
3082 /* Page up */
3083 #ifdef HAVE_LCD_BITMAP
3084 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
3085 #else
3086 for (i = 0; i < display_lines; i++)
3087 #endif
3088 viewer_scroll_up();
3090 else
3091 viewer_scroll_up();
3092 old_tick = *rb->current_tick;
3093 viewer_draw(col);
3094 break;
3096 case VIEWER_PAGE_DOWN:
3097 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
3098 #ifdef VIEWER_PAGE_DOWN2
3099 case VIEWER_PAGE_DOWN2:
3100 case VIEWER_PAGE_DOWN2 | BUTTON_REPEAT:
3101 #endif
3102 if (prefs.scroll_mode == PAGE)
3104 /* Page down */
3105 if (next_screen_ptr != NULL)
3107 screen_top_ptr = next_screen_to_draw_ptr;
3108 if (cpage < MAX_PAGE)
3109 cpage++;
3112 else
3113 viewer_scroll_down(autoscroll);
3114 old_tick = *rb->current_tick;
3115 viewer_draw(col);
3116 break;
3118 case VIEWER_SCREEN_LEFT:
3119 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
3120 if (prefs.view_mode == WIDE) {
3121 /* Screen left */
3122 col = col_limit(col - draw_columns);
3124 else { /* prefs.view_mode == NARROW */
3125 /* Top of file */
3126 viewer_top();
3129 viewer_draw(col);
3130 break;
3132 case VIEWER_SCREEN_RIGHT:
3133 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
3134 if (prefs.view_mode == WIDE) {
3135 /* Screen right */
3136 col = col_limit(col + draw_columns);
3138 else { /* prefs.view_mode == NARROW */
3139 /* Bottom of file */
3140 viewer_bottom();
3143 viewer_draw(col);
3144 break;
3146 #ifdef VIEWER_LINE_UP
3147 case VIEWER_LINE_UP:
3148 case VIEWER_LINE_UP | BUTTON_REPEAT:
3149 /* Scroll up one line */
3150 viewer_scroll_up();
3151 old_tick = *rb->current_tick;
3152 viewer_draw(col);
3153 break;
3155 case VIEWER_LINE_DOWN:
3156 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
3157 /* Scroll down one line */
3158 viewer_scroll_down(autoscroll);
3159 increment_current_line();
3160 old_tick = *rb->current_tick;
3161 viewer_draw(col);
3162 break;
3163 #endif
3164 #ifdef VIEWER_COLUMN_LEFT
3165 case VIEWER_COLUMN_LEFT:
3166 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
3167 if (prefs.view_mode == WIDE) {
3168 /* Scroll left one column */
3169 col = col_limit(col - glyph_width('o'));
3170 viewer_draw(col);
3172 break;
3174 case VIEWER_COLUMN_RIGHT:
3175 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
3176 if (prefs.view_mode == WIDE) {
3177 /* Scroll right one column */
3178 col = col_limit(col + glyph_width('o'));
3179 viewer_draw(col);
3181 break;
3182 #endif
3184 #ifdef VIEWER_RC_QUIT
3185 case VIEWER_RC_QUIT:
3186 #endif
3187 case VIEWER_QUIT:
3188 #ifdef VIEWER_QUIT2
3189 case VIEWER_QUIT2:
3190 #endif
3191 viewer_exit(NULL);
3192 done = true;
3193 break;
3195 case VIEWER_BOOKMARK:
3197 int idx = viewer_find_bookmark(cpage, cline);
3199 if (idx < 0)
3201 if (bookmark_count >= MAX_BOOKMARKS-1)
3202 rb->splash(HZ/2, "No more add bookmark.");
3203 else
3205 viewer_add_bookmark();
3206 rb->splash(HZ/2, "Bookmark add.");
3209 else
3211 viewer_remove_bookmark(idx);
3212 rb->splash(HZ/2, "Bookmark remove.");
3214 viewer_draw(col);
3216 break;
3218 default:
3219 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
3220 == SYS_USB_CONNECTED)
3221 return PLUGIN_USB_CONNECTED;
3222 break;
3224 if (button != BUTTON_NONE)
3226 lastbutton = button;
3227 rb->yield();
3230 return PLUGIN_OK;