summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/viewer.c3294
2 files changed, 0 insertions, 3295 deletions
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 5fdee5344f..bbea275988 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -23,7 +23,6 @@ stopwatch.c
23#endif 23#endif
24theme_remove.c 24theme_remove.c
25vbrfix.c 25vbrfix.c
26viewer.c
27md5sum.c 26md5sum.c
28dice.c 27dice.c
29disktidy.c 28disktidy.c
diff --git a/apps/plugins/viewer.c b/apps/plugins/viewer.c
deleted file mode 100644
index f000628ca3..0000000000
--- a/apps/plugins/viewer.c
+++ /dev/null
@@ -1,3294 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "plugin.h"
22#include <ctype.h>
23#include "lib/playback_control.h"
24
25PLUGIN_HEADER
26
27/* global settings file
28 * binary file, so dont use .cfg
29 *
30 * setting file format
31 *
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
49 */
50#define GLOBAL_SETTINGS_FILE VIEWERS_DIR "/viewer.dat"
51
52/* temporary file */
53#define GLOBAL_SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp"
54
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
59
60/* preferences and bookmarks at each file
61 * binary file, so dont use .cfg
62 *
63 * setting file format
64 *
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]
99 */
100#define SETTINGS_FILE VIEWERS_DIR "/viewer_file.dat"
101
102/* temporary file */
103#define SETTINGS_TMP_FILE VIEWERS_DIR "/viewer_file.tmp"
104
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
109
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
123
124#define BOOKMARK_SIZE 8
125#define MAX_BOOKMARKS 10 /* user setting bookmarks + last read page */
126
127#define BOOKMARK_LAST 1
128#define BOOKMARK_USER 2
129
130#ifndef HAVE_LCD_BITMAP
131#define BOOKMARK_ICON "\xee\x84\x81\x00"
132#endif
133
134#define PREFERENCES_SIZE (12 + MAX_PATH)
135
136/* Out-Of-Bounds test for any pointer to data in the buffer */
137#define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
138
139/* Does the buffer contain the beginning of the file? */
140#define BUFFER_BOF() (file_pos==0)
141
142/* Does the buffer contain the end of the file? */
143#define BUFFER_EOF() (file_size-file_pos <= buffer_size)
144
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))
148
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())
152
153/* Is a scrollbar called for on the current screen? */
154#define NEED_SCROLLBAR() \
155 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
156
157/* variable button definitions */
158
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
173
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
187
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)
199
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
210
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)
226
227#define VIEWER_RC_QUIT BUTTON_RC_STOP
228
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
242
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)
253
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
264
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
275
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)
288
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
301
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)
314
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)
327
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)
340
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
351
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
362
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
378
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
389
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
401
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)
409
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)
420
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
431
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
442
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
453
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)
459
460/* Onda VX777 keys */
461#elif CONFIG_KEYPAD == ONDAVX777_PAD
462#define VIEWER_QUIT BUTTON_POWER
463#define VIEWER_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER)
464
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
475
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
488
489#elif CONFIG_KEYPAD == MPIO_HD200_PAD
490#define VIEWER_QUIT (BUTTON_REC | BUTTON_PLAY)
491#define VIEWER_PAGE_UP BUTTON_PREV
492#define VIEWER_PAGE_DOWN BUTTON_NEXT
493#define VIEWER_SCREEN_LEFT BUTTON_VOL_DOWN
494#define VIEWER_SCREEN_RIGHT BUTTON_VOL_UP
495#define VIEWER_MENU BUTTON_SELECT
496#define VIEWER_AUTOSCROLL BUTTON_PLAY
497#define VIEWER_BOOKMARK BUTTON_REC
498
499#else
500#error No keymap defined!
501#endif
502
503#ifdef HAVE_TOUCHSCREEN
504#ifdef VIEWER_QUIT
505#define VIEWER_QUIT2 BUTTON_TOPLEFT
506#else
507#define VIEWER_QUIT BUTTON_TOPLEFT
508#endif
509#ifdef VIEWER_PAGE_UP
510#define VIEWER_PAGE_UP2 BUTTON_TOPMIDDLE
511#else
512#define VIEWER_PAGE_UP BUTTON_TOPMIDDLE
513#endif
514#ifdef VIEWER_PAGE_DOWN
515#define VIEWER_PAGE_DOWN2 BUTTON_BOTTOMMIDDLE
516#else
517#define VIEWER_PAGE_DOWN BUTTON_BOTTOMMIDDLE
518#endif
519#ifndef VIEWER_SCREEN_LEFT
520#define VIEWER_SCREEN_LEFT BUTTON_MIDLEFT
521#endif
522#ifndef VIEWER_SCREEN_RIGHT
523#define VIEWER_SCREEN_RIGHT BUTTON_MIDRIGHT
524#endif
525#ifdef VIEWER_MENU
526#define VIEWER_MENU2 BUTTON_TOPRIGHT
527#else
528#define VIEWER_MENU BUTTON_TOPRIGHT
529#endif
530#ifndef VIEWER_AUTOSCROLL
531#define VIEWER_AUTOSCROLL BUTTON_CENTER
532#endif
533#endif
534
535/* stuff for the bookmarking */
536struct bookmark_info {
537 long file_position;
538 int page;
539 int line;
540 unsigned char flag;
541};
542
543struct preferences {
544 enum {
545 WRAP=0,
546 CHOP,
547 } word_mode;
548
549 enum {
550 NORMAL=0,
551 JOIN,
552 EXPAND,
553 REFLOW, /* won't be set on charcell LCD, must be last */
554 } line_mode;
555
556 enum {
557 NARROW=0,
558 WIDE,
559 } view_mode;
560
561 enum {
562 LEFT=0,
563 RIGHT,
564 } alignment;
565
566 enum codepages encoding;
567
568 enum {
569 SB_OFF=0,
570 SB_ON,
571 } scrollbar_mode;
572 bool need_scrollbar;
573
574 enum {
575 NO_OVERLAP=0,
576 OVERLAP,
577 } page_mode;
578
579 enum {
580 HD_NONE = 0,
581 HD_PATH,
582 HD_SBAR,
583 HD_BOTH,
584 } header_mode;
585
586 enum {
587 FT_NONE = 0,
588 FT_PAGE,
589 FT_SBAR,
590 FT_BOTH,
591 } footer_mode;
592
593 enum {
594 PAGE=0,
595 LINE,
596 } scroll_mode;
597
598 int autoscroll_speed;
599
600 unsigned char font[MAX_PATH];
601};
602
603enum {
604 VIEWER_FONT_MENU = 0,
605 VIEWER_FONT_TEXT,
606};
607
608struct preferences prefs;
609struct preferences old_prefs;
610
611static unsigned char *buffer;
612static long buffer_size;
613static long block_size = 0x1000;
614static unsigned char line_break[] = {0,0x20,9,0xB,0xC,'-'};
615static int display_columns; /* number of (pixel) columns on the display */
616static int display_lines; /* number of lines on the display */
617static int draw_columns; /* number of (pixel) columns available for text */
618static int par_indent_spaces; /* number of spaces to indent first paragraph */
619static int fd;
620static const char *file_name;
621static long file_size;
622static long start_position; /* position in the file after the viewer is started */
623static bool mac_text;
624static long file_pos; /* Position of the top of the buffer in the file */
625static long last_file_pos;
626static unsigned char *buffer_end; /*Set to BUFFER_END() when file_pos changes*/
627static int max_width;
628static int max_columns;
629static int cline = 1;
630static int cpage = 1;
631static int lpage = 0;
632static unsigned char *screen_top_ptr;
633static unsigned char *next_screen_ptr;
634static unsigned char *next_screen_to_draw_ptr;
635static unsigned char *next_line_ptr;
636static unsigned char *last_screen_top_ptr = NULL;
637#ifdef HAVE_LCD_BITMAP
638static struct font *pf;
639static int header_height = 0;
640static int footer_height = 0;
641#endif
642struct bookmark_info bookmarks[MAX_BOOKMARKS];
643static int bookmark_count;
644
645/* UTF-8 BOM */
646#define BOM "\xef\xbb\xbf"
647#define BOM_SIZE 3
648
649static bool is_bom = false;
650
651/* We draw a diacritic char over a non-diacritic one. Therefore, such chars are
652 * not considered to occupy space, therefore buffers might have more than
653 * max_columns characters. The DIACRITIC_FACTOR is the max ratio between all
654 * characters and non-diacritic characters in the buffer
655 */
656#define DIACRITIC_FACTOR 2
657
658/* calculate the width of a UCS character (zero width for diacritics) */
659static int glyph_width(unsigned short ch)
660{
661 if (ch == 0)
662 ch = ' ';
663
664#ifdef HAVE_LCD_BITMAP
665 if (rb->is_diacritic(ch, NULL))
666 return 0;
667
668 return rb->font_get_width(pf, ch);
669#else
670 return 1;
671#endif
672}
673
674/* get UCS character from string */
675static unsigned char* get_ucs(const unsigned char* str, unsigned short* ch)
676{
677 unsigned char utf8_tmp[6];
678 int count = 2;
679
680 if (prefs.encoding == UTF_8)
681 return (unsigned char*)rb->utf8decode(str, ch);
682
683 rb->iso_decode(str, utf8_tmp, prefs.encoding, count);
684 rb->utf8decode(utf8_tmp, ch);
685
686 /* return a pointer after the parsed section of the string */
687#ifdef HAVE_LCD_BITMAP
688 if (prefs.encoding >= SJIS && *str >= 0x80
689 && !(prefs.encoding == SJIS && *str > 0xA0 && *str < 0xE0))
690 return (unsigned char*)str+2;
691 else
692#endif
693 return (unsigned char*)str+1;
694}
695
696/* decode iso string into UTF-8 string */
697static unsigned char *decode2utf8(const unsigned char *src, unsigned char *dst,
698 int skip_width, int disp_width)
699{
700 unsigned short ucs[max_columns * DIACRITIC_FACTOR + 1];
701 unsigned short ch;
702 const unsigned char *oldstr = src;
703 const unsigned char *str = src;
704 unsigned char *utf8 = dst;
705 int chars = 0;
706 int idx = 0;
707 int width = max_width;
708
709 if (prefs.alignment == LEFT)
710 {
711 /* skip the skip_width */
712 if (skip_width > 0)
713 {
714 while (skip_width > 0 && *str != '\0')
715 {
716 oldstr = str;
717 str = get_ucs(oldstr, &ch);
718 skip_width -= glyph_width(ch);
719 }
720 if (skip_width < 0)
721 str = oldstr;
722 }
723
724 /* decode until string end or disp_width reached */
725 while(*str != '\0')
726 {
727 str = get_ucs(str, &ch);
728 disp_width -= glyph_width(ch);
729 if (disp_width < 0)
730 break;
731 utf8 = rb->utf8encode(ch, utf8);
732 }
733 }
734 else
735 {
736 while (width > 0 && *str != '\0')
737 {
738 str = get_ucs(str, &ch);
739 ucs[chars++] = ch;
740 }
741 ucs[chars] = 0;
742
743 skip_width = max_width - skip_width - disp_width;
744 if (skip_width > 0)
745 {
746 while (skip_width > 0 && chars-- > 0)
747 skip_width -= glyph_width(ucs[chars]);
748
749 if (skip_width < 0)
750 chars++;
751 }
752 else
753 {
754 idx = chars;
755 while (disp_width > 0 && idx-- > 0)
756 disp_width -= glyph_width(ucs[idx]);
757
758 if (disp_width < 0 || idx < 0)
759 idx++;
760 }
761
762 for ( ; idx < chars; idx++)
763 utf8 = rb->utf8encode(ucs[idx], utf8);
764 }
765
766 *utf8 = '\0';
767
768 /* return a pointer after the dst string ends */
769 return utf8;
770}
771
772/* set max_columns and max_width */
773static void calc_max_width(void)
774{
775 if (prefs.view_mode == NARROW)
776 {
777 max_columns = NARROW_MAX_COLUMNS;
778 max_width = draw_columns;
779 }
780 else
781 {
782 max_columns = WIDE_MAX_COLUMNS;
783 max_width = 2 * draw_columns;
784 }
785}
786
787static bool done = false;
788static int col = 0;
789
790static inline void advance_conters(unsigned short ch, int* k, int* width)
791{
792#ifdef HAVE_LCD_BITMAP
793 /* diacritics do not count */
794 if (rb->is_diacritic(ch, NULL))
795 return;
796#endif
797
798 *width += glyph_width(ch);
799 (*k)++;
800}
801
802static inline bool line_is_full(int k, int width)
803{
804 return ((k >= max_columns - 1) || (width >= max_width));
805}
806
807static unsigned char* crop_at_width(const unsigned char* p)
808{
809 int k,width;
810 unsigned short ch;
811 const unsigned char *oldp = p;
812
813 k=width=0;
814
815 while (!line_is_full(k, width)) {
816 oldp = p;
817 if (BUFFER_OOB(p))
818 break;
819 p = get_ucs(p, &ch);
820 advance_conters(ch, &k, &width);
821 }
822
823 return (unsigned char*)oldp;
824}
825
826static unsigned char* find_first_feed(const unsigned char* p, int size)
827{
828 int s = 0;
829 unsigned short ch;
830 const unsigned char *oldp = p;
831 const unsigned char *lbrkp = NULL;
832 int j;
833 int width = 0;
834
835 while(s <= size)
836 {
837 if (*p == 0)
838 return (unsigned char*)p;
839 oldp = p;
840 p = get_ucs(p, &ch);
841
842 if (prefs.word_mode == WRAP)
843 {
844 for (j = 0; j < ((int) sizeof(line_break)); j++)
845 {
846 if (ch == line_break[j])
847 {
848 lbrkp = p;
849 break;
850 }
851 }
852 }
853
854 width += glyph_width(ch);
855 if (width > max_width)
856 return (lbrkp == NULL)? (unsigned char*)oldp : (unsigned char*)lbrkp;
857
858 s += (p - oldp);
859 }
860
861 return NULL;
862}
863
864static unsigned char* find_last_feed(const unsigned char* p, int size)
865{
866 int i;
867
868 for (i=size-1; i>=0; i--)
869 if (p[i] == 0)
870 return (unsigned char*) p+i;
871
872 return NULL;
873}
874
875static unsigned char* find_last_space(const unsigned char* p, int size)
876{
877 int i, j, k;
878
879 k = (prefs.line_mode==JOIN) || (prefs.line_mode==REFLOW) ? 0:1;
880
881 i = size;
882 if (!BUFFER_OOB(&p[i]))
883 for (j=k; j < ((int) sizeof(line_break)) - 1; j++) {
884 if (p[i] == line_break[j])
885 return (unsigned char*) p+i;
886 }
887
888 if (prefs.word_mode == WRAP) {
889 for (i=size-1; i>=0; i--) {
890 for (j=k; j < (int) sizeof(line_break) - 1; j++) {
891 if (p[i] == line_break[j])
892 return (unsigned char*) p+i;
893 }
894 }
895 }
896
897 return NULL;
898}
899
900static unsigned char* find_next_line(const unsigned char* cur_line, bool *is_short)
901{
902 const unsigned char *next_line = NULL;
903 int size, i, j, j_next, j_prev, k, width, search_len, spaces, newlines;
904 bool first_chars;
905 unsigned short ch;
906
907 if (is_short != NULL)
908 *is_short = true;
909
910 if BUFFER_OOB(cur_line)
911 return NULL;
912
913 if (prefs.view_mode == WIDE) {
914 search_len = MAX_WIDTH;
915 }
916 else { /* prefs.view_mode == NARROW */
917 search_len = crop_at_width(cur_line) - cur_line;
918 }
919
920 size = BUFFER_OOB(cur_line+search_len) ? buffer_end-cur_line : search_len;
921
922 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW)) {
923 /* Need to scan ahead and possibly increase search_len and size,
924 or possibly set next_line at second hard return in a row. */
925 next_line = NULL;
926 first_chars=true;
927 j_next=j=k=width=spaces=newlines=0;
928 while (1) {
929 const unsigned char *p, *oldp;
930
931 j_prev = j;
932 j = j_next;
933
934 if (BUFFER_OOB(cur_line+j))
935 return NULL;
936 if (line_is_full(k, width)) {
937 size = search_len = j_prev;
938 break;
939 }
940
941 oldp = p = &cur_line[j];
942 p = get_ucs(p, &ch);
943 j_next = j + (p - oldp);
944
945 switch (ch) {
946 case ' ':
947 if (prefs.line_mode == REFLOW) {
948 if (newlines > 0) {
949 size = j;
950 next_line = cur_line + size;
951 return (unsigned char*) next_line;
952 }
953 if (j==0) /* i=1 is intentional */
954 for (i=0; i<par_indent_spaces; i++)
955 advance_conters(' ', &k, &width);
956 }
957 if (!first_chars) spaces++;
958 break;
959
960 case 0:
961 if (newlines > 0) {
962 size = j;
963 next_line = cur_line + size - spaces;
964 if (next_line != cur_line)
965 return (unsigned char*) next_line;
966 break;
967 }
968
969 newlines++;
970 size += spaces -1;
971 if (BUFFER_OOB(cur_line+size) || size > 2*search_len)
972 return NULL;
973 search_len = size;
974 spaces = first_chars? 0:1;
975 break;
976
977 default:
978 if (prefs.line_mode==JOIN || newlines>0) {
979 while (spaces) {
980 spaces--;
981 advance_conters(' ', &k, &width);
982 if (line_is_full(k, width)) {
983 size = search_len = j;
984 break;
985 }
986 }
987 newlines=0;
988 } else if (spaces) {
989 /* REFLOW, multiple spaces between words: count only
990 * one. If more are needed, they will be added
991 * while drawing. */
992 search_len = size;
993 spaces=0;
994 advance_conters(' ', &k, &width);
995 if (line_is_full(k, width)) {
996 size = search_len = j;
997 break;
998 }
999 }
1000 first_chars = false;
1001 advance_conters(ch, &k, &width);
1002 break;
1003 }
1004 }
1005 }
1006 else {
1007 /* find first hard return */
1008 next_line = find_first_feed(cur_line, size);
1009 }
1010
1011 if (next_line == NULL)
1012 if (size == search_len) {
1013 if (prefs.word_mode == WRAP) /* Find last space */
1014 next_line = find_last_space(cur_line, size);
1015
1016 if (next_line == NULL) {
1017 next_line = crop_at_width(cur_line);
1018 }
1019 else {
1020 if (prefs.word_mode == WRAP) {
1021 for (i=0;i<WRAP_TRIM;i++) {
1022 if (!(isspace(next_line[0]) && !BUFFER_OOB(next_line)))
1023 break;
1024 next_line++;
1025 }
1026 }
1027 }
1028 }
1029
1030 if (prefs.line_mode == EXPAND)
1031 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1032 if (next_line[0] == 0)
1033 if (next_line != cur_line)
1034 return (unsigned char*) next_line;
1035
1036 /* If next_line is pointing to a zero, increment it; i.e.,
1037 leave the terminator at the end of cur_line. If pointing
1038 to a hyphen, increment only if there is room to display
1039 the hyphen on current line (won't apply in WIDE mode,
1040 since it's guarenteed there won't be room). */
1041 if (!BUFFER_OOB(next_line)) /* Not Null & not out of bounds */
1042 if (next_line[0] == 0)/* ||
1043 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
1044 next_line++;
1045
1046 if (BUFFER_OOB(next_line))
1047 {
1048 if (BUFFER_EOF() && next_line != cur_line)
1049 return (unsigned char*) next_line;
1050 return NULL;
1051 }
1052
1053 if (is_short)
1054 *is_short = false;
1055
1056 return (unsigned char*) next_line;
1057}
1058
1059static unsigned char* find_prev_line(const unsigned char* cur_line)
1060{
1061 const unsigned char *prev_line = NULL;
1062 const unsigned char *p;
1063
1064 if BUFFER_OOB(cur_line)
1065 return NULL;
1066
1067 /* To wrap consistently at the same places, we must
1068 start with a known hard return, then work downwards.
1069 We can either search backwards for a hard return,
1070 or simply start wrapping downwards from top of buffer.
1071 If current line is not near top of buffer, this is
1072 a file with long lines (paragraphs). We would need to
1073 read earlier sectors before we could decide how to
1074 properly wrap the lines above the current line, but
1075 it probably is not worth the disk access. Instead,
1076 start with top of buffer and wrap down from there.
1077 This may result in some lines wrapping at different
1078 points from where they wrap when scrolling down.
1079 If buffer is at top of file, start at top of buffer. */
1080
1081 if ((prefs.line_mode == JOIN) || (prefs.line_mode == REFLOW))
1082 prev_line = p = NULL;
1083 else
1084 prev_line = p = find_last_feed(buffer, cur_line-buffer-1);
1085 /* Null means no line feeds in buffer above current line. */
1086
1087 if (prev_line == NULL)
1088 if (BUFFER_BOF() || cur_line - buffer > READ_PREV_ZONE)
1089 prev_line = p = buffer;
1090 /* (else return NULL and read previous block) */
1091
1092 /* Wrap downwards until too far, then use the one before. */
1093 while (p != NULL && p < cur_line) {
1094 prev_line = p;
1095 p = find_next_line(prev_line, NULL);
1096 }
1097
1098 if (BUFFER_OOB(prev_line))
1099 return NULL;
1100
1101 return (unsigned char*) prev_line;
1102}
1103
1104static void check_bom(void)
1105{
1106 unsigned char bom[BOM_SIZE];
1107 off_t orig = rb->lseek(fd, 0, SEEK_CUR);
1108
1109 is_bom = false;
1110
1111 rb->lseek(fd, 0, SEEK_SET);
1112
1113 if (rb->read(fd, bom, BOM_SIZE) == BOM_SIZE)
1114 is_bom = !memcmp(bom, BOM, BOM_SIZE);
1115
1116 rb->lseek(fd, orig, SEEK_SET);
1117}
1118
1119static void fill_buffer(long pos, unsigned char* buf, unsigned size)
1120{
1121 /* Read from file and preprocess the data */
1122 /* To minimize disk access, always read on sector boundaries */
1123 unsigned numread, i;
1124 bool found_CR = false;
1125 off_t offset = rb->lseek(fd, pos, SEEK_SET);
1126
1127 if (offset == 0 && prefs.encoding == UTF_8 && is_bom)
1128 rb->lseek(fd, BOM_SIZE, SEEK_SET);
1129
1130 numread = rb->read(fd, buf, size - 1);
1131 buf[numread] = 0;
1132 rb->button_clear_queue(); /* clear button queue */
1133
1134 for(i = 0; i < numread; i++) {
1135 switch(buf[i]) {
1136 case '\r':
1137 if (mac_text) {
1138 buf[i] = 0;
1139 }
1140 else {
1141 buf[i] = ' ';
1142 found_CR = true;
1143 }
1144 break;
1145
1146 case '\n':
1147 buf[i] = 0;
1148 found_CR = false;
1149 break;
1150
1151 case 0: /* No break between case 0 and default, intentionally */
1152 buf[i] = ' ';
1153 default:
1154 if (found_CR) {
1155 buf[i - 1] = 0;
1156 found_CR = false;
1157 mac_text = true;
1158 }
1159 break;
1160 }
1161 }
1162}
1163
1164static int viewer_find_bookmark(int page, int line)
1165{
1166 int i;
1167
1168 for (i = 0; i < bookmark_count; i++)
1169 {
1170 if (bookmarks[i].page == page && bookmarks[i].line == line)
1171 return i;
1172 }
1173 return -1;
1174}
1175
1176static int read_and_synch(int direction)
1177{
1178/* Read next (or prev) block, and reposition global pointers. */
1179/* direction: 1 for down (i.e., further into file), -1 for up */
1180 int move_size, move_vector, offset;
1181 unsigned char *fill_buf;
1182
1183 if (direction == -1) /* up */ {
1184 move_size = SMALL_BLOCK_SIZE;
1185 offset = 0;
1186 fill_buf = TOP_SECTOR;
1187 rb->memcpy(BOTTOM_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1188 rb->memcpy(MID_SECTOR, TOP_SECTOR, SMALL_BLOCK_SIZE);
1189 }
1190 else /* down */ {
1191 if (prefs.view_mode == WIDE) {
1192 /* WIDE mode needs more buffer so we have to read smaller blocks */
1193 move_size = SMALL_BLOCK_SIZE;
1194 offset = LARGE_BLOCK_SIZE;
1195 fill_buf = BOTTOM_SECTOR;
1196 rb->memcpy(TOP_SECTOR, MID_SECTOR, SMALL_BLOCK_SIZE);
1197 rb->memcpy(MID_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1198 }
1199 else {
1200 move_size = LARGE_BLOCK_SIZE;
1201 offset = SMALL_BLOCK_SIZE;
1202 fill_buf = MID_SECTOR;
1203 rb->memcpy(TOP_SECTOR, BOTTOM_SECTOR, SMALL_BLOCK_SIZE);
1204 }
1205 }
1206 move_vector = direction * move_size;
1207 screen_top_ptr -= move_vector;
1208 file_pos += move_vector;
1209 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1210 fill_buffer(file_pos + offset, fill_buf, move_size);
1211 return move_vector;
1212}
1213
1214static void get_next_line_position(unsigned char **line_begin,
1215 unsigned char **line_end,
1216 bool *is_short)
1217{
1218 int resynch_move;
1219
1220 *line_begin = *line_end;
1221 *line_end = find_next_line(*line_begin, is_short);
1222
1223 if (*line_end == NULL && !BUFFER_EOF())
1224 {
1225 resynch_move = read_and_synch(1); /* Read block & move ptrs */
1226 *line_begin -= resynch_move;
1227 if (next_line_ptr > buffer)
1228 next_line_ptr -= resynch_move;
1229
1230 *line_end = find_next_line(*line_begin, is_short);
1231 }
1232}
1233
1234static void increment_current_line(void)
1235{
1236 if (cline < display_lines)
1237 cline++;
1238 else if (cpage < MAX_PAGE)
1239 {
1240 cpage++;
1241 cline = 1;
1242 }
1243}
1244
1245static void decrement_current_line(void)
1246{
1247 if (cline > 1)
1248 cline--;
1249 else if (cpage > 1)
1250 {
1251 cpage--;
1252 cline = display_lines;
1253 }
1254}
1255
1256static void viewer_scroll_up(void)
1257{
1258 unsigned char *p;
1259
1260 p = find_prev_line(screen_top_ptr);
1261 if (p == NULL && !BUFFER_BOF()) {
1262 read_and_synch(-1);
1263 p = find_prev_line(screen_top_ptr);
1264 }
1265 if (p != NULL)
1266 screen_top_ptr = p;
1267
1268 decrement_current_line();
1269}
1270
1271static void viewer_scroll_down(bool autoscroll)
1272{
1273 if (cpage == lpage)
1274 return;
1275
1276 if (next_line_ptr != NULL)
1277 screen_top_ptr = next_line_ptr;
1278
1279 if (prefs.scroll_mode == LINE || autoscroll)
1280 increment_current_line();
1281 cline++;
1282 if (cline > display_lines) {
1283 cline = 1;
1284 cpage++;
1285 }
1286}
1287
1288static void viewer_scroll_to_top_line(void)
1289{
1290 int line;
1291
1292 for (line = cline; line > 1; line--)
1293 viewer_scroll_up();
1294}
1295
1296#ifdef HAVE_LCD_BITMAP
1297static void viewer_scrollbar(void) {
1298 int items, min_shown, max_shown, sb_begin_y, sb_height;
1299
1300 items = (int) file_size; /* (SH1 int is same as long) */
1301 min_shown = (int) file_pos + (screen_top_ptr - buffer);
1302
1303 if (next_screen_ptr == NULL)
1304 max_shown = items;
1305 else
1306 max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
1307
1308 sb_begin_y = header_height;
1309 sb_height = LCD_HEIGHT - header_height - footer_height;
1310
1311 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, sb_begin_y,
1312 SCROLLBAR_WIDTH-1, sb_height,
1313 items, min_shown, max_shown, VERTICAL);
1314}
1315#endif
1316
1317#ifdef HAVE_LCD_BITMAP
1318static void viewer_show_header(void)
1319{
1320 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1321 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1322
1323 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1324 rb->lcd_putsxy(0, header_height - pf->height, file_name);
1325}
1326
1327static void viewer_show_footer(void)
1328{
1329 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1330 rb->gui_syncstatusbar_draw(rb->statusbars, true);
1331
1332 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1333 {
1334 unsigned char buf[12];
1335
1336 if (cline == 1)
1337 rb->snprintf(buf, sizeof(buf), "%d", cpage);
1338 else
1339 rb->snprintf(buf, sizeof(buf), "%d - %d", cpage, cpage+1);
1340
1341 rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf);
1342 }
1343}
1344#endif
1345
1346static void viewer_draw(int col)
1347{
1348 int i, j, k, line_len, line_width, spaces, left_col=0;
1349 int width, extra_spaces, indent_spaces, spaces_per_word, spaces_width, disp_width = 0;
1350 bool multiple_spacing, line_is_short;
1351 unsigned short ch;
1352 unsigned char *str, *oldstr;
1353 unsigned char *line_begin;
1354 unsigned char *line_end;
1355 unsigned char c;
1356 int max_chars = max_columns * DIACRITIC_FACTOR;
1357 unsigned char scratch_buffer[max_chars * 4 + 1];
1358 unsigned char utf8_buffer[max_chars * 4 + 1];
1359
1360 /* If col==-1 do all calculations but don't display */
1361 if (col != -1) {
1362#ifdef HAVE_LCD_BITMAP
1363 left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
1364#else
1365 left_col = 0;
1366#endif
1367 rb->lcd_clear_display();
1368 }
1369 line_begin = line_end = screen_top_ptr;
1370
1371 for (i = 0; i < display_lines; i++) {
1372 if (BUFFER_OOB(line_end))
1373 {
1374 if (lpage == cpage)
1375 break; /* Happens after display last line at BUFFER_EOF() */
1376
1377 if (lpage == 0 && cline == 1)
1378 {
1379 lpage = cpage;
1380 last_screen_top_ptr = screen_top_ptr;
1381 last_file_pos = file_pos;
1382 }
1383 }
1384
1385 get_next_line_position(&line_begin, &line_end, &line_is_short);
1386 if (line_end == NULL)
1387 {
1388 if (BUFFER_OOB(line_begin))
1389 break;
1390 line_end = buffer_end + 1;
1391 }
1392
1393 line_len = line_end - line_begin;
1394
1395 /* calculate line_len */
1396 str = oldstr = line_begin;
1397 j = -1;
1398 while (str < line_end) {
1399 oldstr = str;
1400 str = crop_at_width(str);
1401 j++;
1402 if (oldstr == str)
1403 {
1404 oldstr = line_end;
1405 break;
1406 }
1407 }
1408 /* width of un-displayed part of the line */
1409 line_width = j*draw_columns;
1410 spaces_width = 0;
1411 while (oldstr < line_end) {
1412 oldstr = get_ucs(oldstr, &ch);
1413 /* add width of displayed part of the line */
1414 if (ch)
1415 {
1416 int dw = glyph_width(ch);
1417
1418 /* avoid counting spaces at the end of the line */
1419 if (ch == ' ')
1420 {
1421 spaces_width += dw;
1422 }
1423 else
1424 {
1425 line_width += dw + spaces_width;
1426 spaces_width = 0;
1427 }
1428 }
1429 }
1430
1431 if (prefs.line_mode == JOIN) {
1432 if (line_begin[0] == 0) {
1433 line_begin++;
1434 if (prefs.word_mode == CHOP)
1435 line_end++;
1436 else
1437 line_len--;
1438 }
1439 for (j=k=spaces=0; j < line_len; j++) {
1440 if (k == max_chars)
1441 break;
1442
1443 c = line_begin[j];
1444 switch (c) {
1445 case ' ':
1446 spaces++;
1447 break;
1448 case 0:
1449 spaces = 0;
1450 scratch_buffer[k++] = ' ';
1451 break;
1452 default:
1453 while (spaces) {
1454 spaces--;
1455 scratch_buffer[k++] = ' ';
1456 if (k == max_chars - 1)
1457 break;
1458 }
1459 scratch_buffer[k++] = c;
1460 break;
1461 }
1462 }
1463 scratch_buffer[k] = 0;
1464 }
1465 else if (prefs.line_mode == REFLOW) {
1466 if (line_begin[0] == 0) {
1467 line_begin++;
1468 if (prefs.word_mode == CHOP)
1469 line_end++;
1470 else
1471 line_len--;
1472 }
1473
1474 indent_spaces = 0;
1475 if (!line_is_short) {
1476 multiple_spacing = false;
1477 width=spaces=0;
1478 for (str = line_begin; str < line_end; ) {
1479 str = get_ucs(str, &ch);
1480 switch (ch) {
1481 case 0:
1482 case ' ':
1483 if (str == line_begin)
1484 {
1485 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1486 {
1487 /* special case: indent the paragraph,
1488 * don't count spaces */
1489 indent_spaces = par_indent_spaces;
1490 }
1491 }
1492 else if (!multiple_spacing)
1493 spaces++;
1494 multiple_spacing = true;
1495 break;
1496 default:
1497 multiple_spacing = false;
1498 width += glyph_width(ch);
1499 break;
1500 }
1501 }
1502 if (multiple_spacing) spaces--;
1503
1504 if (spaces) {
1505 /* total number of spaces to insert between words */
1506 extra_spaces = (max_width-width)/glyph_width(' ')
1507 - indent_spaces;
1508 /* number of spaces between each word*/
1509 spaces_per_word = extra_spaces / spaces;
1510 /* number of words with n+1 spaces (to fill up) */
1511 extra_spaces = extra_spaces % spaces;
1512 if (spaces_per_word > 2) { /* too much spacing is awful */
1513 spaces_per_word = 3;
1514 extra_spaces = 0;
1515 }
1516 } else { /* this doesn't matter much... no spaces anyway */
1517 spaces_per_word = extra_spaces = 0;
1518 }
1519 } else { /* end of a paragraph: don't fill line */
1520 spaces_per_word = 1;
1521 extra_spaces = 0;
1522 }
1523 multiple_spacing = false;
1524 for (j=k=spaces=0; j < line_len; j++) {
1525 if (k == max_chars)
1526 break;
1527
1528 c = line_begin[j];
1529 switch (c) {
1530 case 0:
1531 if (j == line_len - 1)
1532 break;
1533 case ' ':
1534 if (j==0) {
1535 /* indent paragraph */
1536 if (prefs.word_mode == WRAP && prefs.alignment == LEFT)
1537 {
1538 for (j=0; j<par_indent_spaces; j++)
1539 scratch_buffer[k++] = ' ';
1540 j=0;
1541 }
1542 }
1543 else if (!multiple_spacing) {
1544 for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
1545 scratch_buffer[k++] = ' ';
1546 spaces++;
1547 }
1548 multiple_spacing = true;
1549 break;
1550 default:
1551 scratch_buffer[k++] = c;
1552 multiple_spacing = false;
1553 break;
1554 }
1555 }
1556 while (scratch_buffer[k-1] == ' ')
1557 k--;
1558 scratch_buffer[k] = 0;
1559 }
1560 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
1561 if (col != -1)
1562 {
1563 rb->memcpy(scratch_buffer, line_begin, line_len);
1564 scratch_buffer[line_len] = '\0';
1565 }
1566 }
1567
1568 /* create displayed line */
1569 if (col != -1)
1570 {
1571 decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
1572 rb->lcd_getstringsize(utf8_buffer, &disp_width, NULL);
1573 }
1574
1575 /* display on screen the displayed part of the line */
1576 if (col != -1)
1577 {
1578 bool in_page = (cline+i <= display_lines);
1579 int dpage = cpage + (in_page ? 0 : 1);
1580 int dline = cline + i - (in_page ? 0 : display_lines);
1581 bool bflag = (viewer_find_bookmark(dpage, dline) >= 0);
1582#ifdef HAVE_LCD_BITMAP
1583 int dy = i * pf->height + header_height;
1584 int dx = (prefs.alignment == LEFT)? left_col : LCD_WIDTH - disp_width;
1585#endif
1586 if (bflag)
1587#ifdef HAVE_LCD_BITMAP
1588 {
1589 rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG);
1590 rb->lcd_fillrect(left_col, dy, LCD_WIDTH - left_col, pf->height);
1591 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1592 }
1593 rb->lcd_putsxy(dx, dy, utf8_buffer);
1594 rb->lcd_set_drawmode(DRMODE_SOLID);
1595#else
1596 {
1597 rb->lcd_puts(left_col, i, BOOKMARK_ICON);
1598 }
1599 rb->lcd_puts(left_col+1, i, utf8_buffer);
1600#endif
1601 }
1602
1603 if (i == 0)
1604 next_line_ptr = line_end;
1605 }
1606 next_screen_ptr = line_end;
1607 if (BUFFER_OOB(next_screen_ptr))
1608 next_screen_ptr = NULL;
1609
1610#ifdef HAVE_LCD_BITMAP
1611 next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
1612
1613 if (prefs.need_scrollbar)
1614 viewer_scrollbar();
1615#else
1616 next_screen_to_draw_ptr = next_screen_ptr;
1617#endif
1618
1619#ifdef HAVE_LCD_BITMAP
1620 /* show header */
1621 viewer_show_header();
1622
1623 /* show footer */
1624 viewer_show_footer();
1625#endif
1626
1627 if (col != -1)
1628 rb->lcd_update();
1629}
1630
1631static void viewer_top(void)
1632{
1633 /* Read top of file into buffer
1634 and point screen pointer to top */
1635 if (file_pos != 0)
1636 {
1637 rb->splash(0, "Loading...");
1638
1639 file_pos = 0;
1640 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
1641 fill_buffer(0, buffer, buffer_size);
1642 }
1643
1644 screen_top_ptr = buffer;
1645 cpage = 1;
1646 cline = 1;
1647}
1648
1649static void viewer_bottom(void)
1650{
1651 unsigned char *line_begin;
1652 unsigned char *line_end;
1653
1654 rb->splash(0, "Loading...");
1655
1656 if (last_screen_top_ptr)
1657 {
1658 cpage = lpage;
1659 cline = 1;
1660 screen_top_ptr = last_screen_top_ptr;
1661 file_pos = last_file_pos;
1662 fill_buffer(file_pos, buffer, buffer_size);
1663 buffer_end = BUFFER_END();
1664 return;
1665 }
1666
1667 line_end = screen_top_ptr;
1668
1669 while (!BUFFER_EOF() || !BUFFER_OOB(line_end))
1670 {
1671 get_next_line_position(&line_begin, &line_end, NULL);
1672 if (line_end == NULL)
1673 break;
1674
1675 increment_current_line();
1676 if (cline == 1)
1677 screen_top_ptr = line_end;
1678 }
1679 lpage = cpage;
1680 cline = 1;
1681 last_screen_top_ptr = screen_top_ptr;
1682 last_file_pos = file_pos;
1683 buffer_end = BUFFER_END();
1684}
1685
1686#ifdef HAVE_LCD_BITMAP
1687static void init_need_scrollbar(void) {
1688 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1689 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1690 viewer_draw(-1);
1691 prefs.need_scrollbar = NEED_SCROLLBAR();
1692 draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
1693 par_indent_spaces = draw_columns/(5*glyph_width(' '));
1694 calc_max_width();
1695}
1696
1697static void init_header_and_footer(void)
1698{
1699 header_height = 0;
1700 footer_height = 0;
1701 if (rb->global_settings->statusbar == STATUSBAR_TOP)
1702 {
1703 if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
1704 header_height = STATUSBAR_HEIGHT;
1705
1706 if (prefs.footer_mode == FT_SBAR)
1707 prefs.footer_mode = FT_NONE;
1708 else if (prefs.footer_mode == FT_BOTH)
1709 prefs.footer_mode = FT_PAGE;
1710 }
1711 else if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
1712 {
1713 if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
1714 footer_height = STATUSBAR_HEIGHT;
1715
1716 if (prefs.header_mode == HD_SBAR)
1717 prefs.header_mode = HD_NONE;
1718 else if (prefs.header_mode == HD_BOTH)
1719 prefs.header_mode = HD_PATH;
1720 }
1721 else /* STATUSBAR_OFF || STATUSBAR_CUSTOM */
1722 {
1723 if (prefs.header_mode == HD_SBAR)
1724 prefs.header_mode = HD_NONE;
1725 else if (prefs.header_mode == HD_BOTH)
1726 prefs.header_mode = HD_PATH;
1727
1728 if (prefs.footer_mode == FT_SBAR)
1729 prefs.footer_mode = FT_NONE;
1730 else if (prefs.footer_mode == FT_BOTH)
1731 prefs.footer_mode = FT_PAGE;
1732 }
1733
1734 if (prefs.header_mode == HD_NONE || prefs.header_mode == HD_PATH ||
1735 prefs.footer_mode == FT_NONE || prefs.footer_mode == FT_PAGE)
1736 rb->gui_syncstatusbar_draw(rb->statusbars, false);
1737
1738 if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
1739 header_height += pf->height;
1740 if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
1741 footer_height += pf->height;
1742
1743 display_lines = (LCD_HEIGHT - header_height - footer_height) / pf->height;
1744
1745 lpage = 0;
1746 last_file_pos = 0;
1747 last_screen_top_ptr = NULL;
1748}
1749
1750static bool change_font(unsigned char *font)
1751{
1752 unsigned char buf[MAX_PATH];
1753
1754 if (font == NULL || *font == '\0')
1755 return false;
1756
1757 rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font);
1758 if (rb->font_load(NULL, buf) < 0) {
1759 rb->splash(HZ/2, "Font load failed.");
1760 return false;
1761 }
1762
1763 return true;
1764}
1765#endif
1766
1767static bool viewer_init(void)
1768{
1769#ifdef HAVE_LCD_BITMAP
1770 /* initialize fonts */
1771 pf = rb->font_get(FONT_UI);
1772 if (pf == NULL)
1773 return false;
1774
1775 draw_columns = display_columns = LCD_WIDTH;
1776#else
1777 /* REAL fixed pitch :) all chars use up 1 cell */
1778 display_lines = 2;
1779 draw_columns = display_columns = 11;
1780 par_indent_spaces = 2;
1781#endif
1782
1783 fd = rb->open(file_name, O_RDONLY);
1784 if (fd < 0)
1785 return false;
1786
1787 /* Init mac_text value used in processing buffer */
1788 mac_text = false;
1789
1790 return true;
1791}
1792
1793/* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8,
1794 * then file size decreases only BOM_SIZE.
1795 */
1796static void get_filesize(void)
1797{
1798 file_size = rb->filesize(fd);
1799 if (file_size == -1)
1800 return;
1801
1802 if (prefs.encoding == UTF_8 && is_bom)
1803 file_size -= BOM_SIZE;
1804}
1805
1806static int bm_comp(const void *a, const void *b)
1807{
1808 struct bookmark_info *pa;
1809 struct bookmark_info *pb;
1810
1811 pa = (struct bookmark_info*)a;
1812 pb = (struct bookmark_info*)b;
1813
1814 if (pa->page != pb->page)
1815 return pa->page - pb->page;
1816
1817 return pa->line - pb->line;
1818}
1819
1820static void viewer_add_bookmark(void)
1821{
1822 if (bookmark_count >= MAX_BOOKMARKS-1)
1823 return;
1824
1825 bookmarks[bookmark_count].file_position
1826 = file_pos + screen_top_ptr - buffer;
1827 bookmarks[bookmark_count].page = cpage;
1828 bookmarks[bookmark_count].line = cline;
1829 bookmarks[bookmark_count].flag = BOOKMARK_USER;
1830 bookmark_count++;
1831}
1832
1833static int viewer_add_last_read_bookmark(void)
1834{
1835 int i;
1836
1837 i = viewer_find_bookmark(cpage, cline);
1838 if (i >= 0)
1839 bookmarks[i].flag |= BOOKMARK_LAST;
1840 else
1841 {
1842 viewer_add_bookmark();
1843 i = bookmark_count-1;
1844 bookmarks[i].flag = BOOKMARK_LAST;
1845 }
1846 return i;
1847}
1848
1849static void viewer_remove_bookmark(int i)
1850{
1851 int j;
1852
1853 if (i < 0 || i >= bookmark_count)
1854 return;
1855
1856 for (j = i+1; j < bookmark_count; j++)
1857 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1858 sizeof(struct bookmark_info));
1859
1860 bookmark_count--;
1861}
1862
1863static void viewer_remove_last_read_bookmark(void)
1864{
1865 int i, j;
1866
1867 for (i = 0; i < bookmark_count; i++)
1868 {
1869 if (bookmarks[i].flag & BOOKMARK_LAST)
1870 {
1871 if (bookmarks[i].flag == BOOKMARK_LAST)
1872 {
1873 for (j = i+1; j < bookmark_count; j++)
1874 rb->memcpy(&bookmarks[j-1], &bookmarks[j],
1875 sizeof(struct bookmark_info));
1876
1877 bookmark_count--;
1878 }
1879 else
1880 bookmarks[i].flag = BOOKMARK_USER;
1881 break;
1882 }
1883 }
1884}
1885
1886static int viewer_get_last_read_bookmark(void)
1887{
1888 int i;
1889
1890 for (i = 0; i < bookmark_count; i++)
1891 {
1892 if (bookmarks[i].flag & BOOKMARK_LAST)
1893 return i;
1894 }
1895 return -1;
1896}
1897
1898static void viewer_select_bookmark(int initval)
1899{
1900 int i;
1901 int ipage = 0;
1902 int iline = 0;
1903 int screen_pos;
1904 int screen_top;
1905 int selected = -1;
1906
1907 struct opt_items items[bookmark_count];
1908 unsigned char names[bookmark_count][38];
1909
1910 if (initval >= 0 && initval < bookmark_count)
1911 {
1912 ipage = bookmarks[initval].page;
1913 iline = bookmarks[initval].line;
1914 }
1915
1916 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
1917 bm_comp);
1918
1919 for (i = 0; i < bookmark_count; i++)
1920 {
1921 rb->snprintf(names[i], sizeof(names[0]),
1922#if CONFIG_KEYPAD != PLAYER_PAD
1923 "%sPage: %d Line: %d",
1924#else
1925 "%sP:%d L:%d",
1926#endif
1927 (bookmarks[i].flag&BOOKMARK_LAST)? "*":" ",
1928 bookmarks[i].page,
1929 bookmarks[i].line);
1930 items[i].string = names[i];
1931 items[i].voice_id = -1;
1932 if (selected < 0 && bookmarks[i].page == ipage && bookmarks[i].line == iline)
1933 selected = i;
1934 }
1935
1936 rb->set_option("Select bookmark", &selected, INT, items,
1937 sizeof(items) / sizeof(items[0]), NULL);
1938
1939 if (selected < 0 || selected >= bookmark_count)
1940 {
1941 if (initval < 0 || (selected = viewer_get_last_read_bookmark()) < 0)
1942 {
1943 if (initval < 0)
1944 rb->splash(HZ, "Start the first page.");
1945 file_pos = 0;
1946 screen_top_ptr = buffer;
1947 cpage = 1;
1948 cline = 1;
1949 buffer_end = BUFFER_END();
1950 return;
1951 }
1952 }
1953
1954 screen_pos = bookmarks[selected].file_position;
1955 screen_top = screen_pos % buffer_size;
1956 file_pos = screen_pos - screen_top;
1957 screen_top_ptr = buffer + screen_top;
1958 cpage = bookmarks[selected].page;
1959 cline = bookmarks[selected].line;
1960 buffer_end = BUFFER_END();
1961}
1962
1963static void viewer_default_preferences(void)
1964{
1965 prefs.word_mode = WRAP;
1966 prefs.line_mode = NORMAL;
1967 prefs.view_mode = NARROW;
1968 prefs.alignment = LEFT;
1969 prefs.scroll_mode = PAGE;
1970 prefs.page_mode = NO_OVERLAP;
1971 prefs.scrollbar_mode = SB_OFF;
1972 rb->memset(prefs.font, 0, MAX_PATH);
1973#ifdef HAVE_LCD_BITMAP
1974 prefs.header_mode = HD_BOTH;
1975 prefs.footer_mode = FT_BOTH;
1976 rb->snprintf(prefs.font, MAX_PATH, "%s", rb->global_settings->font_file);
1977#else
1978 prefs.header_mode = HD_NONE;
1979 prefs.footer_mode = FT_NONE;
1980#endif
1981 prefs.autoscroll_speed = 1;
1982 /* Set codepage to system default */
1983 prefs.encoding = rb->global_settings->default_codepage;
1984}
1985
1986static bool viewer_read_preferences(int pfd, int version, struct preferences *prf)
1987{
1988 unsigned char buf[PREFERENCES_SIZE];
1989 unsigned char *p = buf;
1990 int read_size = PREFERENCES_SIZE;
1991
1992 if (version == 0)
1993 read_size--;
1994
1995 if (rb->read(pfd, buf, read_size) != read_size)
1996 return false;
1997
1998 prf->word_mode = *p++;
1999 prf->line_mode = *p++;
2000 prf->view_mode = *p++;
2001 if (version > 0)
2002 prf->alignment = *p++;
2003 else
2004 prf->alignment = LEFT;
2005 prf->encoding = *p++;
2006 prf->scrollbar_mode = *p++;
2007 prf->need_scrollbar = *p++;
2008 prf->page_mode = *p++;
2009 prf->header_mode = *p++;
2010 prf->footer_mode = *p++;
2011 prf->scroll_mode = *p++;
2012 prf->autoscroll_speed = *p++;
2013 rb->memcpy(prf->font, p, MAX_PATH);
2014 return true;
2015}
2016
2017static bool viewer_write_preferences(int pfd, const struct preferences *prf)
2018{
2019 unsigned char buf[PREFERENCES_SIZE];
2020 unsigned char *p = buf;
2021
2022 *p++ = prf->word_mode;
2023 *p++ = prf->line_mode;
2024 *p++ = prf->view_mode;
2025 *p++ = prf->alignment;
2026 *p++ = prf->encoding;
2027 *p++ = prf->scrollbar_mode;
2028 *p++ = prf->need_scrollbar;
2029 *p++ = prf->page_mode;
2030 *p++ = prf->header_mode;
2031 *p++ = prf->footer_mode;
2032 *p++ = prf->scroll_mode;
2033 *p++ = prf->autoscroll_speed;
2034 rb->memcpy(p, prf->font, MAX_PATH);
2035
2036 return (rb->write(pfd, buf, sizeof(buf)) == sizeof(buf));
2037}
2038
2039static bool viewer_read_bookmark_info(int bfd, struct bookmark_info *b)
2040{
2041 unsigned char buf[BOOKMARK_SIZE];
2042
2043 if (rb->read(bfd, buf, sizeof(buf)) != sizeof(buf))
2044 return false;
2045
2046 b->file_position = (buf[0] << 24)|(buf[1] << 16)|(buf[2] << 8)|buf[3];
2047 b->page = (buf[4] << 8)|buf[5];
2048 b->line = buf[6];
2049 b->flag = buf[7];
2050
2051 return true;
2052}
2053
2054static bool viewer_read_bookmark_infos(int bfd)
2055{
2056 unsigned char c;
2057 int i;
2058
2059 if (rb->read(bfd, &c, 1) != 1)
2060 {
2061 bookmark_count = 0;
2062 return false;
2063 }
2064
2065 bookmark_count = c;
2066 if (bookmark_count > MAX_BOOKMARKS)
2067 bookmark_count = MAX_BOOKMARKS;
2068
2069 for (i = 0; i < bookmark_count; i++)
2070 {
2071 if (!viewer_read_bookmark_info(bfd, &bookmarks[i]))
2072 {
2073 bookmark_count = i;
2074 return false;
2075 }
2076 }
2077 return true;
2078}
2079
2080static bool viewer_write_bookmark_info(int bfd, struct bookmark_info *b)
2081{
2082 unsigned char buf[BOOKMARK_SIZE];
2083 unsigned char *p = buf;
2084 unsigned long ul;
2085
2086 ul = b->file_position;
2087 *p++ = ul >> 24;
2088 *p++ = ul >> 16;
2089 *p++ = ul >> 8;
2090 *p++ = ul;
2091
2092 ul = b->page;
2093 *p++ = ul >> 8;
2094 *p++ = ul;
2095
2096 *p++ = b->line;
2097 *p = b->flag;
2098
2099 return (rb->write(bfd, buf, sizeof(buf)) == sizeof(buf));
2100}
2101
2102static bool viewer_write_bookmark_infos(int bfd)
2103{
2104 unsigned char c = bookmark_count;
2105 int i;
2106
2107 if (rb->write(bfd, &c, 1) != 1)
2108 return false;
2109
2110 for (i = 0; i < bookmark_count; i++)
2111 {
2112 if (!viewer_write_bookmark_info(bfd, &bookmarks[i]))
2113 return false;
2114 }
2115
2116 return true;
2117}
2118
2119static bool viewer_load_global_settings(void)
2120{
2121 unsigned buf[GLOBAL_SETTINGS_H_SIZE];
2122 int sfd = rb->open(GLOBAL_SETTINGS_FILE, O_RDONLY);
2123 int version;
2124 bool res = false;
2125
2126 if (sfd < 0)
2127 return false;
2128
2129 if ((rb->read(sfd, buf, GLOBAL_SETTINGS_H_SIZE) == GLOBAL_SETTINGS_H_SIZE) ||
2130 (rb->memcmp(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1) == 0))
2131 {
2132 version = buf[GLOBAL_SETTINGS_H_SIZE - 1] - GLOBAL_SETTINGS_FIRST_VERSION;
2133 res = viewer_read_preferences(sfd, version, &prefs);
2134 }
2135 rb->close(sfd);
2136 return res;
2137}
2138
2139static bool viewer_save_global_settings(void)
2140{
2141 int sfd = rb->open(GLOBAL_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
2142 unsigned char buf[GLOBAL_SETTINGS_H_SIZE];
2143
2144 if (sfd < 0)
2145 return false;
2146
2147 rb->memcpy(buf, GLOBAL_SETTINGS_HEADER, GLOBAL_SETTINGS_H_SIZE - 1);
2148 buf[GLOBAL_SETTINGS_H_SIZE - 1] = GLOBAL_SETTINGS_VERSION;
2149
2150 if (rb->write(sfd, buf, GLOBAL_SETTINGS_H_SIZE) != GLOBAL_SETTINGS_H_SIZE ||
2151 !viewer_write_preferences(sfd, &prefs))
2152 {
2153 rb->close(sfd);
2154 rb->remove(GLOBAL_SETTINGS_TMP_FILE);
2155 return false;
2156 }
2157 rb->close(sfd);
2158 rb->remove(GLOBAL_SETTINGS_FILE);
2159 rb->rename(GLOBAL_SETTINGS_TMP_FILE, GLOBAL_SETTINGS_FILE);
2160 return true;
2161}
2162
2163static bool viewer_convert_settings(int sfd, int dfd, int old_ver)
2164{
2165 struct preferences new_prefs;
2166 off_t old_pos;
2167 off_t new_pos;
2168 unsigned char buf[MAX_PATH + 2];
2169 int settings_size;
2170
2171 rb->read(sfd, buf, MAX_PATH + 2);
2172 rb->write(dfd, buf, MAX_PATH + 2);
2173
2174 settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
2175
2176 old_pos = rb->lseek(sfd, 0, SEEK_CUR);
2177 new_pos = rb->lseek(dfd, 0, SEEK_CUR);
2178
2179 /*
2180 * when the settings size != preferences size + bookmarks size,
2181 * settings data are considered to be old version.
2182 */
2183 if (old_ver > 0 && ((settings_size - PREFERENCES_SIZE) % 8) == 0)
2184 old_ver = 0;
2185
2186 if (!viewer_read_preferences(sfd, old_ver, &new_prefs))
2187 return false;
2188
2189 if (!viewer_write_preferences(dfd, &new_prefs))
2190 return false;
2191
2192 settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos);
2193
2194 if (settings_size > 0)
2195 {
2196 rb->read(sfd, buf, settings_size);
2197 rb->write(dfd, buf, settings_size);
2198 }
2199
2200 settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos;
2201 buf[0] = settings_size >> 8;
2202 buf[1] = settings_size;
2203 rb->lseek(dfd, new_pos - 2, SEEK_SET);
2204 rb->write(dfd, buf, 2);
2205 rb->lseek(dfd, settings_size, SEEK_CUR);
2206 return true;
2207}
2208
2209static bool viewer_convert_settings_file(void)
2210{
2211 unsigned char buf[SETTINGS_H_SIZE+2];
2212 int sfd;
2213 int tfd;
2214 int i;
2215 int fcount;
2216 int version;
2217 bool res = true;
2218
2219 if ((sfd = rb->open(SETTINGS_FILE, O_RDONLY)) < 0)
2220 return false;
2221
2222 if ((tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
2223 {
2224 rb->close(sfd);
2225 return false;
2226 }
2227
2228 rb->read(sfd, buf, SETTINGS_H_SIZE + 2);
2229
2230 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2231 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE + 1];
2232 buf[SETTINGS_H_SIZE - 1] = SETTINGS_VERSION;
2233
2234 rb->write(tfd, buf, SETTINGS_H_SIZE + 2);
2235
2236 for (i = 0; i < fcount; i++)
2237 {
2238 if (!viewer_convert_settings(sfd, tfd, version))
2239 {
2240 res = false;
2241 break;
2242 }
2243 }
2244
2245 rb->close(sfd);
2246 rb->close(tfd);
2247
2248 if (!res)
2249 rb->remove(SETTINGS_TMP_FILE);
2250 else
2251 {
2252 rb->remove(SETTINGS_FILE);
2253 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2254 }
2255 return res;
2256}
2257
2258static bool viewer_load_settings(void)
2259{
2260 unsigned char buf[MAX_PATH+2];
2261 unsigned int fcount;
2262 unsigned int i;
2263 bool res = false;
2264 int sfd;
2265 unsigned int size;
2266 int version;
2267
2268 sfd = rb->open(SETTINGS_FILE, O_RDONLY);
2269 if (sfd < 0)
2270 goto read_end;
2271
2272 if ((rb->read(sfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2273 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2274 {
2275 /* illegal setting file */
2276 rb->close(sfd);
2277
2278 if (rb->file_exists(SETTINGS_FILE))
2279 rb->remove(SETTINGS_FILE);
2280
2281 goto read_end;
2282 }
2283
2284 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2285 {
2286 rb->close(sfd);
2287 if (!viewer_convert_settings_file())
2288 goto read_end;
2289
2290 return viewer_load_settings();
2291 }
2292
2293 version = buf[SETTINGS_H_SIZE - 1] - SETTINGS_FIRST_VERSION;
2294 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2295 for (i = 0; i < fcount; i++)
2296 {
2297 if (rb->read(sfd, buf, MAX_PATH+2) != MAX_PATH+2)
2298 break;
2299
2300 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2301
2302 /*
2303 * when the settings size != preferences size + bookmarks size,
2304 * the settings file converts to the newer.
2305 */
2306 if (version > 0 && ((size - PREFERENCES_SIZE) % 8) == 0)
2307 {
2308 rb->close(sfd);
2309 if (!viewer_convert_settings_file())
2310 break;
2311
2312 return viewer_load_settings();
2313 }
2314
2315 if (rb->strcmp(buf, file_name))
2316 {
2317 if (rb->lseek(sfd, size, SEEK_CUR) < 0)
2318 break;
2319 continue;
2320 }
2321 if (!viewer_read_preferences(sfd, version, &prefs))
2322 break;
2323
2324 res = viewer_read_bookmark_infos(sfd);
2325 break;
2326 }
2327
2328 rb->close(sfd);
2329
2330read_end:
2331 if (!res)
2332 {
2333 /* load global settings */
2334 if (!viewer_load_global_settings())
2335 viewer_default_preferences();
2336
2337 file_pos = 0;
2338 screen_top_ptr = buffer;
2339 cpage = 1;
2340 cline = 1;
2341 bookmark_count = 0;
2342 }
2343
2344 rb->memcpy(&old_prefs, &prefs, sizeof(struct preferences));
2345 calc_max_width();
2346
2347 if (bookmark_count > 1)
2348 viewer_select_bookmark(-1);
2349 else if (bookmark_count == 1)
2350 {
2351 int screen_pos;
2352 int screen_top;
2353
2354 screen_pos = bookmarks[0].file_position;
2355 screen_top = screen_pos % buffer_size;
2356 file_pos = screen_pos - screen_top;
2357 screen_top_ptr = buffer + screen_top;
2358 cpage = bookmarks[0].page;
2359 cline = bookmarks[0].line;
2360 }
2361
2362 viewer_remove_last_read_bookmark();
2363
2364 check_bom();
2365 get_filesize();
2366
2367 buffer_end = BUFFER_END(); /* Update whenever file_pos changes */
2368
2369 if (BUFFER_OOB(screen_top_ptr))
2370 screen_top_ptr = buffer;
2371
2372 fill_buffer(file_pos, buffer, buffer_size);
2373 if (prefs.scroll_mode == PAGE && cline > 1)
2374 viewer_scroll_to_top_line();
2375
2376 /* remember the current position */
2377 start_position = file_pos + screen_top_ptr - buffer;
2378
2379#ifdef HAVE_LCD_BITMAP
2380 /* load prefs font if it is different than the global settings font */
2381 if (rb->strcmp(prefs.font, rb->global_settings->font_file)) {
2382 if (!change_font(prefs.font)) {
2383 /* fallback by resetting prefs font to the global settings font */
2384 rb->memset(prefs.font, 0, MAX_PATH);
2385 rb->snprintf(prefs.font, MAX_PATH, "%s",
2386 rb->global_settings->font_file);
2387
2388 if (!change_font(prefs.font))
2389 return false;
2390 }
2391 }
2392
2393 init_need_scrollbar();
2394 init_header_and_footer();
2395#endif
2396
2397 return true;
2398}
2399
2400static bool copy_bookmark_file(int sfd, int dfd, off_t start, off_t size)
2401{
2402 off_t rsize;
2403
2404 if (rb->lseek(sfd, start, SEEK_SET) < 0)
2405 return false;
2406
2407 while (size > 0)
2408 {
2409 if (size > buffer_size)
2410 rsize = buffer_size;
2411 else
2412 rsize = size;
2413 size -= rsize;
2414
2415 if (rb->read(sfd, buffer, rsize) != rsize ||
2416 rb->write(dfd, buffer, rsize) != rsize)
2417 return false;
2418 }
2419 return true;
2420}
2421
2422static bool viewer_save_settings(void)
2423{
2424 unsigned char buf[MAX_PATH+2];
2425 unsigned int fcount = 0;
2426 unsigned int i;
2427 int idx;
2428 int ofd;
2429 int tfd;
2430 off_t first_copy_size = 0;
2431 off_t second_copy_start_pos = 0;
2432 off_t size;
2433
2434 /* add reading page to bookmarks */
2435 idx = viewer_find_bookmark(cpage, cline);
2436 if (idx >= 0)
2437 bookmarks[idx].flag |= BOOKMARK_LAST;
2438 else
2439 {
2440 viewer_add_bookmark();
2441 bookmarks[bookmark_count-1].flag = BOOKMARK_LAST;
2442 }
2443
2444 tfd = rb->open(SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
2445 if (tfd < 0)
2446 return false;
2447
2448 ofd = rb->open(SETTINGS_FILE, O_RDWR);
2449 if (ofd >= 0)
2450 {
2451 if ((rb->read(ofd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2) ||
2452 rb->memcmp(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1))
2453 {
2454 rb->close(ofd);
2455 goto save_err;
2456 }
2457
2458 if (buf[SETTINGS_H_SIZE - 1] != SETTINGS_VERSION)
2459 {
2460 rb->close(ofd);
2461 if (!viewer_convert_settings_file())
2462 goto save_err;
2463
2464 viewer_save_settings();
2465 }
2466
2467 fcount = (buf[SETTINGS_H_SIZE] << 8) | buf[SETTINGS_H_SIZE+1];
2468
2469 for (i = 0; i < fcount; i++)
2470 {
2471 if (rb->read(ofd, buf, MAX_PATH+2) != MAX_PATH+2)
2472 {
2473 rb->close(ofd);
2474 goto save_err;
2475 }
2476 size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
2477 if (rb->strcmp(buf, file_name))
2478 {
2479 if (rb->lseek(ofd, size, SEEK_CUR) < 0)
2480 {
2481 rb->close(ofd);
2482 goto save_err;
2483 }
2484 }
2485 else
2486 {
2487 first_copy_size = rb->lseek(ofd, 0, SEEK_CUR);
2488 if (first_copy_size < 0)
2489 {
2490 rb->close(ofd);
2491 goto save_err;
2492 }
2493 second_copy_start_pos = first_copy_size + size;
2494 first_copy_size -= MAX_PATH+2;
2495 fcount--;
2496 break;
2497 }
2498 }
2499 if (first_copy_size == 0)
2500 first_copy_size = rb->filesize(ofd);
2501
2502 if (!copy_bookmark_file(ofd, tfd, 0, first_copy_size))
2503 {
2504 rb->close(ofd);
2505 goto save_err;
2506 }
2507 if (second_copy_start_pos > 0)
2508 {
2509 if (!copy_bookmark_file(ofd, tfd, second_copy_start_pos,
2510 rb->filesize(ofd) - second_copy_start_pos))
2511 {
2512 rb->close(ofd);
2513 goto save_err;
2514 }
2515 }
2516 rb->close(ofd);
2517 }
2518 else
2519 {
2520 rb->memcpy(buf, SETTINGS_HEADER, SETTINGS_H_SIZE - 1);
2521 buf[SETTINGS_H_SIZE-1] = SETTINGS_VERSION;
2522 buf[SETTINGS_H_SIZE ] = 0;
2523 buf[SETTINGS_H_SIZE+1] = 0;
2524 if (rb->write(tfd, buf, SETTINGS_H_SIZE+2) != SETTINGS_H_SIZE+2)
2525 goto save_err;
2526 }
2527
2528 /* copy to current read file's bookmarks */
2529 rb->memset(buf, 0, MAX_PATH);
2530 rb->snprintf(buf, MAX_PATH, "%s", file_name);
2531
2532 size = PREFERENCES_SIZE + bookmark_count * BOOKMARK_SIZE + 1;
2533 buf[MAX_PATH] = size >> 8;
2534 buf[MAX_PATH+1] = size;
2535
2536 if (rb->write(tfd, buf, MAX_PATH+2) != MAX_PATH+2)
2537 goto save_err;
2538
2539 if (!viewer_write_preferences(tfd, &prefs))
2540 goto save_err;
2541
2542 if (!viewer_write_bookmark_infos(tfd))
2543 goto save_err;
2544
2545 if (rb->lseek(tfd, SETTINGS_H_SIZE, SEEK_SET) < 0)
2546 goto save_err;
2547
2548 fcount++;
2549 buf[0] = fcount >> 8;
2550 buf[1] = fcount;
2551
2552 if (rb->write(tfd, buf, 2) != 2)
2553 goto save_err;
2554
2555 rb->close(tfd);
2556
2557 rb->remove(SETTINGS_FILE);
2558 rb->rename(SETTINGS_TMP_FILE, SETTINGS_FILE);
2559
2560 return true;
2561
2562save_err:
2563 rb->close(tfd);
2564 rb->remove(SETTINGS_TMP_FILE);
2565 return false;
2566}
2567
2568static void viewer_exit(void *parameter)
2569{
2570 (void)parameter;
2571
2572 /* save preference and bookmarks */
2573 if (!viewer_save_settings())
2574 rb->splash(HZ, "Can't save preference and bookmarks.");
2575
2576 rb->close(fd);
2577#ifdef HAVE_LCD_BITMAP
2578 if (rb->strcmp(prefs.font, rb->global_settings->font_file))
2579 change_font(rb->global_settings->font_file);
2580#endif
2581}
2582
2583static void calc_page(void)
2584{
2585 int i;
2586 unsigned char *line_begin;
2587 unsigned char *line_end;
2588 off_t sfp;
2589 unsigned char *sstp;
2590
2591 rb->splash(0, "Calculating page/line number...");
2592
2593 /* add reading page to bookmarks */
2594 viewer_add_last_read_bookmark();
2595
2596 rb->qsort(bookmarks, bookmark_count, sizeof(struct bookmark_info),
2597 bm_comp);
2598
2599 cpage = 1;
2600 cline = 1;
2601 file_pos = 0;
2602 screen_top_ptr = buffer;
2603 buffer_end = BUFFER_END();
2604
2605 fill_buffer(file_pos, buffer, buffer_size);
2606 line_end = line_begin = buffer;
2607
2608 /* update page and line of all bookmark */
2609 for (i = 0; i < bookmark_count; i++)
2610 {
2611 sfp = bookmarks[i].file_position;
2612 sstp = buffer;
2613
2614 while ((line_begin > sstp || sstp >= line_end) ||
2615 (file_pos > sfp || sfp >= file_pos + BUFFER_END() - buffer))
2616 {
2617 get_next_line_position(&line_begin, &line_end, NULL);
2618 if (line_end == NULL)
2619 break;
2620
2621 next_line_ptr = line_end;
2622
2623 if (sstp == buffer &&
2624 file_pos <= sfp && sfp < file_pos + BUFFER_END() - buffer)
2625 sstp = sfp - file_pos + buffer;
2626
2627 increment_current_line();
2628 }
2629
2630 decrement_current_line();
2631 bookmarks[i].page = cpage;
2632 bookmarks[i].line = cline;
2633 bookmarks[i].file_position = file_pos + (line_begin - buffer);
2634 increment_current_line();
2635 }
2636
2637 /* remove reading page's bookmark */
2638 for (i = 0; i < bookmark_count; i++)
2639 {
2640 if (bookmarks[i].flag & BOOKMARK_LAST)
2641 {
2642 int screen_pos;
2643 int screen_top;
2644
2645 screen_pos = bookmarks[i].file_position;
2646 screen_top = screen_pos % buffer_size;
2647 file_pos = screen_pos - screen_top;
2648 screen_top_ptr = buffer + screen_top;
2649
2650 cpage = bookmarks[i].page;
2651 cline = bookmarks[i].line;
2652 bookmarks[i].flag ^= BOOKMARK_LAST;
2653 buffer_end = BUFFER_END();
2654
2655 fill_buffer(file_pos, buffer, buffer_size);
2656
2657 if (bookmarks[i].flag == 0)
2658 viewer_remove_bookmark(i);
2659
2660 if (prefs.scroll_mode == PAGE && cline > 1)
2661 viewer_scroll_to_top_line();
2662 break;
2663 }
2664 }
2665}
2666
2667static int col_limit(int col)
2668{
2669 if (col < 0)
2670 col = 0;
2671 else
2672 if (col >= max_width - draw_columns)
2673 col = max_width - draw_columns;
2674
2675 return col;
2676}
2677
2678/* settings helper functions */
2679
2680static bool encoding_setting(void)
2681{
2682 static struct opt_items names[NUM_CODEPAGES];
2683 int idx;
2684 bool res;
2685 enum codepages oldenc = prefs.encoding;
2686
2687 for (idx = 0; idx < NUM_CODEPAGES; idx++)
2688 {
2689 names[idx].string = rb->get_codepage_name(idx);
2690 names[idx].voice_id = -1;
2691 }
2692
2693 res = rb->set_option("Encoding", &prefs.encoding, INT, names,
2694 sizeof(names) / sizeof(names[0]), NULL);
2695
2696 /* When prefs.encoding changes into UTF-8 or changes from UTF-8,
2697 * filesize (file_size) might change.
2698 * In addition, if prefs.encoding is UTF-8, then BOM does not read.
2699 */
2700 if (oldenc != prefs.encoding && (oldenc == UTF_8 || prefs.encoding == UTF_8))
2701 {
2702 check_bom();
2703 get_filesize();
2704 fill_buffer(file_pos, buffer, buffer_size);
2705 }
2706
2707 return res;
2708}
2709
2710static bool word_wrap_setting(void)
2711{
2712 static const struct opt_items names[] = {
2713 {"On", -1},
2714 {"Off (Chop Words)", -1},
2715 };
2716
2717 return rb->set_option("Word Wrap", &prefs.word_mode, INT,
2718 names, 2, NULL);
2719}
2720
2721static bool line_mode_setting(void)
2722{
2723 static const struct opt_items names[] = {
2724 {"Normal", -1},
2725 {"Join Lines", -1},
2726 {"Expand Lines", -1},
2727#ifdef HAVE_LCD_BITMAP
2728 {"Reflow Lines", -1},
2729#endif
2730 };
2731
2732 return rb->set_option("Line Mode", &prefs.line_mode, INT, names,
2733 sizeof(names) / sizeof(names[0]), NULL);
2734}
2735
2736static bool view_mode_setting(void)
2737{
2738 static const struct opt_items names[] = {
2739 {"No (Narrow)", -1},
2740 {"Yes", -1},
2741 };
2742 bool ret;
2743 ret = rb->set_option("Wide View", &prefs.view_mode, INT,
2744 names , 2, NULL);
2745 if (prefs.view_mode == NARROW)
2746 col = 0;
2747 calc_max_width();
2748 return ret;
2749}
2750
2751static bool scroll_mode_setting(void)
2752{
2753 static const struct opt_items names[] = {
2754 {"Scroll by Page", -1},
2755 {"Scroll by Line", -1},
2756 };
2757
2758 return rb->set_option("Scroll Mode", &prefs.scroll_mode, INT,
2759 names, 2, NULL);
2760}
2761
2762#ifdef HAVE_LCD_BITMAP
2763static bool page_mode_setting(void)
2764{
2765 static const struct opt_items names[] = {
2766 {"No", -1},
2767 {"Yes", -1},
2768 };
2769
2770 return rb->set_option("Overlap Pages", &prefs.page_mode, INT,
2771 names, 2, NULL);
2772}
2773
2774static bool scrollbar_setting(void)
2775{
2776 static const struct opt_items names[] = {
2777 {"Off", -1},
2778 {"On", -1}
2779 };
2780
2781 return rb->set_option("Show Scrollbar", &prefs.scrollbar_mode, INT,
2782 names, 2, NULL);
2783}
2784
2785static bool header_setting(void)
2786{
2787 int len = (rb->global_settings->statusbar == STATUSBAR_TOP)? 4 : 2;
2788 struct opt_items names[len];
2789
2790 names[0].string = "None";
2791 names[0].voice_id = -1;
2792 names[1].string = "File path";
2793 names[1].voice_id = -1;
2794
2795 if (rb->global_settings->statusbar == STATUSBAR_TOP)
2796 {
2797 names[2].string = "Status bar";
2798 names[2].voice_id = -1;
2799 names[3].string = "Both";
2800 names[3].voice_id = -1;
2801 }
2802
2803 return rb->set_option("Show Header", &prefs.header_mode, INT,
2804 names, len, NULL);
2805}
2806
2807static bool footer_setting(void)
2808{
2809 int len = (rb->global_settings->statusbar == STATUSBAR_BOTTOM)? 4 : 2;
2810 struct opt_items names[len];
2811
2812 names[0].string = "None";
2813 names[0].voice_id = -1;
2814 names[1].string = "Page Num";
2815 names[1].voice_id = -1;
2816
2817 if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
2818 {
2819 names[2].string = "Status bar";
2820 names[2].voice_id = -1;
2821 names[3].string = "Both";
2822 names[3].voice_id = -1;
2823 }
2824
2825 return rb->set_option("Show Footer", &prefs.footer_mode, INT,
2826 names, len, NULL);
2827}
2828
2829static int font_comp(const void *a, const void *b)
2830{
2831 struct opt_items *pa;
2832 struct opt_items *pb;
2833
2834 pa = (struct opt_items *)a;
2835 pb = (struct opt_items *)b;
2836
2837 return rb->strcmp(pa->string, pb->string);
2838}
2839
2840static bool font_setting(void)
2841{
2842 int count = 0;
2843 DIR *dir;
2844 struct dirent *entry;
2845 int i = 0;
2846 int len;
2847 int new_font = 0;
2848 int old_font;
2849 bool res;
2850 int size = 0;
2851
2852 dir = rb->opendir(FONT_DIR);
2853 if (!dir)
2854 {
2855 rb->splash(HZ/2, "Font dir is not accessible");
2856 return false;
2857 }
2858
2859 while (1)
2860 {
2861 entry = rb->readdir(dir);
2862
2863 if (entry == NULL)
2864 break;
2865
2866 len = rb->strlen(entry->d_name);
2867 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2868 continue;
2869 size += len-3;
2870 count++;
2871 }
2872 rb->closedir(dir);
2873
2874 struct opt_items names[count];
2875 unsigned char font_names[size];
2876 unsigned char *p = font_names;
2877
2878 dir = rb->opendir(FONT_DIR);
2879 if (!dir)
2880 {
2881 rb->splash(HZ/2, "Font dir is not accessible");
2882 return false;
2883 }
2884
2885 while (1)
2886 {
2887 entry = rb->readdir(dir);
2888
2889 if (entry == NULL)
2890 break;
2891
2892 len = rb->strlen(entry->d_name);
2893 if (len < 4 || rb->strcmp(entry->d_name + len-4, ".fnt"))
2894 continue;
2895
2896 rb->snprintf(p, len-3, "%s", entry->d_name);
2897 names[i].string = p;
2898 names[i].voice_id = -1;
2899 p += len-3;
2900 i++;
2901 if (i >= count)
2902 break;
2903 }
2904 rb->closedir(dir);
2905
2906 rb->qsort(names, count, sizeof(struct opt_items), font_comp);
2907
2908 for (i = 0; i < count; i++)
2909 {
2910 if (!rb->strcmp(names[i].string, prefs.font))
2911 {
2912 new_font = i;
2913 break;
2914 }
2915 }
2916 old_font = new_font;
2917
2918 res = rb->set_option("Select Font", &new_font, INT,
2919 names, count, NULL);
2920
2921 if (new_font != old_font)
2922 {
2923 /* load selected font */
2924 if (!change_font((unsigned char *)names[new_font].string)) {
2925 /* revert by re-loading the preferences font */
2926 change_font(prefs.font);
2927 return false;
2928 }
2929 rb->memset(prefs.font, 0, MAX_PATH);
2930 rb->snprintf(prefs.font, MAX_PATH, "%s", names[new_font].string);
2931 }
2932
2933 return res;
2934}
2935
2936static bool alignment_setting(void)
2937{
2938 static const struct opt_items names[] = {
2939 {"Left", -1},
2940 {"Right", -1},
2941 };
2942
2943 return rb->set_option("Alignment", &prefs.alignment, INT,
2944 names , 2, NULL);
2945}
2946#endif
2947
2948static bool autoscroll_speed_setting(void)
2949{
2950 return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
2951 &prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
2952}
2953
2954MENUITEM_FUNCTION(encoding_item, 0, "Encoding", encoding_setting,
2955 NULL, NULL, Icon_NOICON);
2956MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", word_wrap_setting,
2957 NULL, NULL, Icon_NOICON);
2958MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", line_mode_setting,
2959 NULL, NULL, Icon_NOICON);
2960MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", view_mode_setting,
2961 NULL, NULL, Icon_NOICON);
2962#ifdef HAVE_LCD_BITMAP
2963MENUITEM_FUNCTION(alignment_item, 0, "Alignment", alignment_setting,
2964 NULL, NULL, Icon_NOICON);
2965MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", scrollbar_setting,
2966 NULL, NULL, Icon_NOICON);
2967MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", page_mode_setting,
2968 NULL, NULL, Icon_NOICON);
2969MENUITEM_FUNCTION(header_item, 0, "Show Header", header_setting,
2970 NULL, NULL, Icon_NOICON);
2971MENUITEM_FUNCTION(footer_item, 0, "Show Footer", footer_setting,
2972 NULL, NULL, Icon_NOICON);
2973MENUITEM_FUNCTION(font_item, 0, "Font", font_setting,
2974 NULL, NULL, Icon_NOICON);
2975#endif
2976MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", scroll_mode_setting,
2977 NULL, NULL, Icon_NOICON);
2978MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
2979 autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
2980MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
2981 &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
2982#ifdef HAVE_LCD_BITMAP
2983 &alignment_item, &scrollbar_item, &page_mode_item, &header_item,
2984 &footer_item, &font_item,
2985#endif
2986 &scroll_mode_item, &autoscroll_speed_item);
2987
2988static bool viewer_options_menu(bool is_global)
2989{
2990 bool result;
2991 struct preferences tmp_prefs;
2992
2993 rb->memcpy(&tmp_prefs, &prefs, sizeof(struct preferences));
2994
2995 result = (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB);
2996
2997 if (!is_global && rb->memcmp(&tmp_prefs, &prefs, sizeof(struct preferences)))
2998 {
2999 /* Show-scrollbar mode for current view-width mode */
3000#ifdef HAVE_LCD_BITMAP
3001 init_need_scrollbar();
3002 init_header_and_footer();
3003#endif
3004 calc_page();
3005 }
3006 return result;
3007}
3008
3009static void viewer_menu(void)
3010{
3011 int result;
3012
3013 MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
3014 "Return", "Viewer Options",
3015 "Show Playback Menu", "Select Bookmark",
3016 "Global Settings", "Quit");
3017
3018 result = rb->do_menu(&menu, NULL, NULL, false);
3019 switch (result)
3020 {
3021 case 0: /* return */
3022 break;
3023 case 1: /* change settings */
3024 done = viewer_options_menu(false);
3025 break;
3026 case 2: /* playback control */
3027 playback_control(NULL);
3028 break;
3029 case 3: /* select bookmark */
3030 viewer_select_bookmark(viewer_add_last_read_bookmark());
3031 viewer_remove_last_read_bookmark();
3032 fill_buffer(file_pos, buffer, buffer_size);
3033 if (prefs.scroll_mode == PAGE && cline > 1)
3034 viewer_scroll_to_top_line();
3035 break;
3036 case 4: /* change global settings */
3037 {
3038 struct preferences orig_prefs;
3039
3040 rb->memcpy(&orig_prefs, &prefs, sizeof(struct preferences));
3041 if (!viewer_load_global_settings())
3042 viewer_default_preferences();
3043 done = viewer_options_menu(true);
3044 viewer_save_global_settings();
3045 rb->memcpy(&prefs, &orig_prefs, sizeof(struct preferences));
3046 }
3047 break;
3048 case 5: /* quit */
3049 viewer_exit(NULL);
3050 done = true;
3051 break;
3052 }
3053 viewer_draw(col);
3054}
3055
3056enum plugin_status plugin_start(const void* file)
3057{
3058 int button, i, ok;
3059 int lastbutton = BUTTON_NONE;
3060 bool autoscroll = false;
3061 long old_tick;
3062 size_t buf_size;
3063
3064 old_tick = *rb->current_tick;
3065
3066 /* get the plugin buffer */
3067 buffer = rb->plugin_get_buffer(&buf_size);
3068 buffer_size = buf_size;
3069 if (buffer_size == 0)
3070 {
3071 rb->splash(HZ, "buffer does not allocate !!");
3072 return PLUGIN_ERROR;
3073 }
3074 block_size = buffer_size / 3;
3075 buffer_size = 3 * block_size;
3076
3077 if (!file)
3078 return PLUGIN_ERROR;
3079
3080 file_name = file;
3081 ok = viewer_init();
3082 if (!ok) {
3083 rb->splash(HZ, "Error opening file.");
3084 return PLUGIN_ERROR;
3085 }
3086
3087 if (!viewer_load_settings()) /* load the preferences and bookmark */
3088 return PLUGIN_ERROR;
3089
3090#if LCD_DEPTH > 1
3091 rb->lcd_set_backdrop(NULL);
3092#endif
3093
3094 viewer_draw(col);
3095
3096 while (!done) {
3097
3098 if(autoscroll)
3099 {
3100 if(old_tick <= *rb->current_tick - (110-prefs.autoscroll_speed*10))
3101 {
3102 viewer_scroll_down(true);
3103 viewer_draw(col);
3104 old_tick = *rb->current_tick;
3105 }
3106 }
3107
3108 button = rb->button_get_w_tmo(HZ/10);
3109
3110 if (prefs.view_mode != WIDE) {
3111 /* when not in wide view mode, the SCREEN_LEFT and SCREEN_RIGHT
3112 buttons jump to the beginning and end of the file. To stop
3113 users doing this by accident, replace non-held occurrences
3114 with page up/down instead. */
3115 if (button == VIEWER_SCREEN_LEFT)
3116 button = VIEWER_PAGE_UP;
3117 else if (button == VIEWER_SCREEN_RIGHT)
3118 button = VIEWER_PAGE_DOWN;
3119 }
3120
3121 switch (button) {
3122 case VIEWER_MENU:
3123#ifdef VIEWER_MENU2
3124 case VIEWER_MENU2:
3125#endif
3126 viewer_menu();
3127 break;
3128
3129 case VIEWER_AUTOSCROLL:
3130#ifdef VIEWER_AUTOSCROLL_PRE
3131 if (lastbutton != VIEWER_AUTOSCROLL_PRE)
3132 break;
3133#endif
3134 autoscroll = !autoscroll;
3135 break;
3136
3137 case VIEWER_PAGE_UP:
3138 case VIEWER_PAGE_UP | BUTTON_REPEAT:
3139#ifdef VIEWER_PAGE_UP2
3140 case VIEWER_PAGE_UP2:
3141 case VIEWER_PAGE_UP2 | BUTTON_REPEAT:
3142#endif
3143 if (prefs.scroll_mode == PAGE)
3144 {
3145 /* Page up */
3146#ifdef HAVE_LCD_BITMAP
3147 for (i = prefs.page_mode==OVERLAP? 1:0; i < display_lines; i++)
3148#else
3149 for (i = 0; i < display_lines; i++)
3150#endif
3151 viewer_scroll_up();
3152 }
3153 else
3154 viewer_scroll_up();
3155 old_tick = *rb->current_tick;
3156 viewer_draw(col);
3157 break;
3158
3159 case VIEWER_PAGE_DOWN:
3160 case VIEWER_PAGE_DOWN | BUTTON_REPEAT:
3161#ifdef VIEWER_PAGE_DOWN2
3162 case VIEWER_PAGE_DOWN2:
3163 case VIEWER_PAGE_DOWN2 | BUTTON_REPEAT:
3164#endif
3165 if (prefs.scroll_mode == PAGE)
3166 {
3167 /* Page down */
3168 if (next_screen_ptr != NULL)
3169 {
3170 screen_top_ptr = next_screen_to_draw_ptr;
3171 if (cpage < MAX_PAGE)
3172 cpage++;
3173 }
3174 }
3175 else
3176 viewer_scroll_down(autoscroll);
3177 old_tick = *rb->current_tick;
3178 viewer_draw(col);
3179 break;
3180
3181 case VIEWER_SCREEN_LEFT:
3182 case VIEWER_SCREEN_LEFT | BUTTON_REPEAT:
3183 if (prefs.view_mode == WIDE) {
3184 /* Screen left */
3185 col = col_limit(col - draw_columns);
3186 }
3187 else { /* prefs.view_mode == NARROW */
3188 /* Top of file */
3189 viewer_top();
3190 }
3191
3192 viewer_draw(col);
3193 break;
3194
3195 case VIEWER_SCREEN_RIGHT:
3196 case VIEWER_SCREEN_RIGHT | BUTTON_REPEAT:
3197 if (prefs.view_mode == WIDE) {
3198 /* Screen right */
3199 col = col_limit(col + draw_columns);
3200 }
3201 else { /* prefs.view_mode == NARROW */
3202 /* Bottom of file */
3203 viewer_bottom();
3204 }
3205
3206 viewer_draw(col);
3207 break;
3208
3209#ifdef VIEWER_LINE_UP
3210 case VIEWER_LINE_UP:
3211 case VIEWER_LINE_UP | BUTTON_REPEAT:
3212 /* Scroll up one line */
3213 viewer_scroll_up();
3214 old_tick = *rb->current_tick;
3215 viewer_draw(col);
3216 break;
3217
3218 case VIEWER_LINE_DOWN:
3219 case VIEWER_LINE_DOWN | BUTTON_REPEAT:
3220 /* Scroll down one line */
3221 viewer_scroll_down(autoscroll);
3222 increment_current_line();
3223 old_tick = *rb->current_tick;
3224 viewer_draw(col);
3225 break;
3226#endif
3227#ifdef VIEWER_COLUMN_LEFT
3228 case VIEWER_COLUMN_LEFT:
3229 case VIEWER_COLUMN_LEFT | BUTTON_REPEAT:
3230 if (prefs.view_mode == WIDE) {
3231 /* Scroll left one column */
3232 col = col_limit(col - glyph_width('o'));
3233 viewer_draw(col);
3234 }
3235 break;
3236
3237 case VIEWER_COLUMN_RIGHT:
3238 case VIEWER_COLUMN_RIGHT | BUTTON_REPEAT:
3239 if (prefs.view_mode == WIDE) {
3240 /* Scroll right one column */
3241 col = col_limit(col + glyph_width('o'));
3242 viewer_draw(col);
3243 }
3244 break;
3245#endif
3246
3247#ifdef VIEWER_RC_QUIT
3248 case VIEWER_RC_QUIT:
3249#endif
3250 case VIEWER_QUIT:
3251#ifdef VIEWER_QUIT2
3252 case VIEWER_QUIT2:
3253#endif
3254 viewer_exit(NULL);
3255 done = true;
3256 break;
3257
3258 case VIEWER_BOOKMARK:
3259 {
3260 int idx = viewer_find_bookmark(cpage, cline);
3261
3262 if (idx < 0)
3263 {
3264 if (bookmark_count >= MAX_BOOKMARKS-1)
3265 rb->splash(HZ/2, "No more bookmarks");
3266 else
3267 {
3268 viewer_add_bookmark();
3269 rb->splash(HZ/2, "Bookmark added");
3270 }
3271 }
3272 else
3273 {
3274 viewer_remove_bookmark(idx);
3275 rb->splash(HZ/2, "Bookmark removed");
3276 }
3277 viewer_draw(col);
3278 }
3279 break;
3280
3281 default:
3282 if (rb->default_event_handler_ex(button, viewer_exit, NULL)
3283 == SYS_USB_CONNECTED)
3284 return PLUGIN_USB_CONNECTED;
3285 break;
3286 }
3287 if (button != BUTTON_NONE)
3288 {
3289 lastbutton = button;
3290 rb->yield();
3291 }
3292 }
3293 return PLUGIN_OK;
3294}