1 /***************************************************************************
4 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
5 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
6 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
7 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
11 * Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
13 * All files in this archive are subject to the GNU General Public License.
14 * See the file COPYING in the source tree root for full license agreement.
16 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
17 * KIND, either express or implied.
19 ****************************************************************************/
22 #include "playback_control.h"
23 #include "oldmenuapi.h"
27 #define SETTINGS_FILE "/.rockbox/viewers/viewer.dat" /* binary file, so dont use .cfg */
28 #define BOOKMARKS_FILE "/.rockbox/viewers/viewer_bookmarks.dat"
30 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
31 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
32 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
33 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
34 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
35 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
36 #define BUFFER_SIZE 0x3000 /* 12k: Mem reserved for buffered file data */
37 #define TOP_SECTOR buffer
38 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
39 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
40 #define SCROLLBAR_WIDTH 6
42 #define MAX_BOOKMARKED_FILES (((signed)BUFFER_SIZE/(signed)sizeof(struct bookmarked_file_info))-1)
44 /* Out-Of-Bounds test for any pointer to data in the buffer */
45 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
47 /* Does the buffer contain the beginning of the file? */
48 #define BUFFER_BOF() (file_pos==0)
50 /* Does the buffer contain the end of the file? */
51 #define BUFFER_EOF() (file_size-file_pos <= BUFFER_SIZE)
53 /* Formula for the endpoint address outside of buffer data */
54 #define BUFFER_END() \
55 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+BUFFER_SIZE))
57 /* Is the entire file being shown in one screen? */
58 #define ONE_SCREEN_FITS_ALL() \
59 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
61 /* Is a scrollbar called for on the current screen? */
62 #define NEED_SCROLLBAR() \
63 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
65 /* variable button definitions */
68 #if CONFIG_KEYPAD == RECORDER_PAD
69 #define VIEWER_QUIT BUTTON_OFF
70 #define VIEWER_PAGE_UP BUTTON_UP
71 #define VIEWER_PAGE_DOWN BUTTON_DOWN
72 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
73 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
74 #define VIEWER_MENU BUTTON_F1
75 #define VIEWER_AUTOSCROLL BUTTON_PLAY
76 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
77 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
78 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
79 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
81 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
82 #define VIEWER_QUIT BUTTON_OFF
83 #define VIEWER_PAGE_UP BUTTON_UP
84 #define VIEWER_PAGE_DOWN BUTTON_DOWN
85 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
86 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
87 #define VIEWER_MENU BUTTON_F1
88 #define VIEWER_AUTOSCROLL BUTTON_SELECT
89 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
90 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
91 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
92 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
95 #elif CONFIG_KEYPAD == ONDIO_PAD
96 #define VIEWER_QUIT BUTTON_OFF
97 #define VIEWER_PAGE_UP BUTTON_UP
98 #define VIEWER_PAGE_DOWN BUTTON_DOWN
99 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
100 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
101 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
102 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
103 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
106 #elif CONFIG_KEYPAD == PLAYER_PAD
107 #define VIEWER_QUIT BUTTON_STOP
108 #define VIEWER_PAGE_UP BUTTON_LEFT
109 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
110 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
111 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
112 #define VIEWER_MENU BUTTON_MENU
113 #define VIEWER_AUTOSCROLL BUTTON_PLAY
115 /* iRiver H1x0 && H3x0 keys */
116 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
117 (CONFIG_KEYPAD == IRIVER_H300_PAD)
118 #define VIEWER_QUIT BUTTON_OFF
119 #define VIEWER_PAGE_UP BUTTON_UP
120 #define VIEWER_PAGE_DOWN BUTTON_DOWN
121 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
122 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
123 #define VIEWER_MENU BUTTON_MODE
124 #define VIEWER_AUTOSCROLL BUTTON_SELECT
125 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
126 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
127 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
128 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
130 #define VIEWER_RC_QUIT BUTTON_RC_STOP
132 /* iPods with the 4G pad */
133 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_3G_PAD)
135 #define VIEWER_QUIT_PRE BUTTON_SELECT
136 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
137 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
138 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
139 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
140 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
141 #define VIEWER_MENU BUTTON_MENU
142 #define VIEWER_AUTOSCROLL BUTTON_PLAY
145 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
146 #define VIEWER_QUIT BUTTON_PLAY
147 #define VIEWER_PAGE_UP BUTTON_UP
148 #define VIEWER_PAGE_DOWN BUTTON_DOWN
149 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
150 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
151 #define VIEWER_MENU BUTTON_MODE
152 #define VIEWER_AUTOSCROLL BUTTON_SELECT
155 #elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
156 #define VIEWER_QUIT BUTTON_POWER
157 #define VIEWER_PAGE_UP BUTTON_UP
158 #define VIEWER_PAGE_DOWN BUTTON_DOWN
159 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
160 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
161 #define VIEWER_MENU BUTTON_SELECT
162 #define VIEWER_AUTOSCROLL BUTTON_PLAY
165 #elif CONFIG_KEYPAD == GIGABEAT_PAD
166 #define VIEWER_QUIT BUTTON_POWER
167 #define VIEWER_PAGE_UP BUTTON_UP
168 #define VIEWER_PAGE_DOWN BUTTON_DOWN
169 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
170 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
171 #define VIEWER_MENU BUTTON_MENU
172 #define VIEWER_AUTOSCROLL BUTTON_A
174 /* Sansa E200 keys */
175 #elif CONFIG_KEYPAD == SANSA_E200_PAD
176 #define VIEWER_QUIT BUTTON_POWER
177 #define VIEWER_PAGE_UP BUTTON_UP
178 #define VIEWER_PAGE_DOWN BUTTON_DOWN
179 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
180 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
181 #define VIEWER_MENU BUTTON_SELECT
182 #define VIEWER_AUTOSCROLL BUTTON_REC
183 #define VIEWER_LINE_UP BUTTON_SCROLL_UP
184 #define VIEWER_LINE_DOWN BUTTON_SCROLL_DOWN
186 /* iriver H10 keys */
187 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
188 #define VIEWER_QUIT BUTTON_POWER
189 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
190 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
191 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
192 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
193 #define VIEWER_MENU BUTTON_REW
194 #define VIEWER_AUTOSCROLL BUTTON_PLAY
198 /* stuff for the bookmarking */
199 struct bookmarked_file_info
{
202 char filename
[MAX_PATH
];
205 struct bookmark_file_data
{
206 signed int bookmarked_files_count
;
207 struct bookmarked_file_info bookmarks
[];
220 REFLOW
, /* won't be set on charcell LCD, must be last */
243 } encoding
; /* FIXME: What should default encoding be? */
245 #ifdef HAVE_LCD_BITMAP
256 #endif /* HAVE_LCD_BITMAP */
263 int autoscroll_speed
;
267 static unsigned char buffer
[BUFFER_SIZE
+ 1];
268 static unsigned char line_break
[] = {0,0x20,9,0xB,0xC,'-'};
269 static int display_columns
; /* number of (pixel) columns on the display */
270 static int display_lines
; /* number of lines on the display */
271 static int draw_columns
; /* number of (pixel) columns available for text */
272 static int par_indent_spaces
; /* number of spaces to indent first paragraph */
274 static char *file_name
;
275 static long file_size
;
276 static bool mac_text
;
277 static long file_pos
; /* Position of the top of the buffer in the file */
278 static unsigned char *buffer_end
; /*Set to BUFFER_END() when file_pos changes*/
279 static int max_line_len
;
280 static unsigned char *screen_top_ptr
;
281 static unsigned char *next_screen_ptr
;
282 static unsigned char *next_screen_to_draw_ptr
;
283 static unsigned char *next_line_ptr
;
284 static struct plugin_api
* rb
;
285 #ifdef HAVE_LCD_BITMAP
286 static struct font
*pf
;
290 int glyph_width(int ch
)
295 #ifdef HAVE_LCD_BITMAP
296 return rb
->font_get_width(pf
, ch
);
302 unsigned char* get_ucs(const unsigned char* str
, unsigned short* ch
)
304 unsigned char utf8_tmp
[6];
307 if (prefs
.encoding
== UTF8
)
308 return (unsigned char*)rb
->utf8decode(str
, ch
);
310 count
= BUFFER_OOB(str
+2)? 1:2;
311 rb
->iso_decode(str
, utf8_tmp
, prefs
.encoding
, count
);
312 rb
->utf8decode(utf8_tmp
, ch
);
314 if ((prefs
.encoding
== SJIS
&& *str
> 0xA0 && *str
< 0xE0) || prefs
.encoding
< SJIS
)
315 return (unsigned char*)str
+1;
317 return (unsigned char*)str
+2;
323 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
324 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
325 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
326 static unsigned char* crop_at_width(const unsigned char* p
)
330 const unsigned char *oldp
= p
;
334 while (LINE_IS_NOT_FULL
) {
337 ADVANCE_COUNTERS(ch
);
340 return (unsigned char*)oldp
;
343 static unsigned char* find_first_feed(const unsigned char* p
, int size
)
347 for (i
=0; i
< size
; i
++)
349 return (unsigned char*) p
+i
;
354 static unsigned char* find_last_feed(const unsigned char* p
, int size
)
358 for (i
=size
-1; i
>=0; i
--)
360 return (unsigned char*) p
+i
;
365 static unsigned char* find_last_space(const unsigned char* p
, int size
)
369 k
= (prefs
.line_mode
==JOIN
) || (prefs
.line_mode
==REFLOW
) ? 0:1;
371 if (!BUFFER_OOB(&p
[size
]))
372 for (j
=k
; j
< ((int) sizeof(line_break
)) - 1; j
++)
373 if (p
[size
] == line_break
[j
])
374 return (unsigned char*) p
+size
;
376 for (i
=size
-1; i
>=0; i
--)
377 for (j
=k
; j
< (int) sizeof(line_break
); j
++)
378 if (p
[i
] == line_break
[j
])
379 return (unsigned char*) p
+i
;
384 static unsigned char* find_next_line(const unsigned char* cur_line
, bool *is_short
)
386 const unsigned char *next_line
= NULL
;
387 int size
, i
, j
, k
, width
, search_len
, spaces
, newlines
;
391 if (is_short
!= NULL
)
394 if BUFFER_OOB(cur_line
)
397 if (prefs
.view_mode
== WIDE
) {
398 search_len
= MAX_WIDTH
;
400 else { /* prefs.view_mode == NARROW */
401 search_len
= crop_at_width(cur_line
) - cur_line
;
404 size
= BUFFER_OOB(cur_line
+search_len
) ? buffer_end
-cur_line
: search_len
;
406 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
)) {
407 /* Need to scan ahead and possibly increase search_len and size,
408 or possibly set next_line at second hard return in a row. */
411 for (j
=k
=width
=spaces
=newlines
=0; ; j
++) {
412 if (BUFFER_OOB(cur_line
+j
))
415 size
= search_len
= j
;
422 if (prefs
.line_mode
== REFLOW
) {
425 next_line
= cur_line
+ size
;
426 return (unsigned char*) next_line
;
428 if (j
==0) /* i=1 is intentional */
429 for (i
=0; i
<par_indent_spaces
; i
++)
430 ADVANCE_COUNTERS(' ');
432 if (!first_chars
) spaces
++;
438 next_line
= cur_line
+ size
- spaces
;
439 if (next_line
!= cur_line
)
440 return (unsigned char*) next_line
;
446 if (BUFFER_OOB(cur_line
+size
) || size
> 2*search_len
)
449 spaces
= first_chars
? 0:1;
453 if (prefs
.line_mode
==JOIN
|| newlines
>0) {
456 ADVANCE_COUNTERS(' ');
458 size
= search_len
= j
;
464 /* REFLOW, multiple spaces between words: count only
465 * one. If more are needed, they will be added
469 ADVANCE_COUNTERS(' ');
471 size
= search_len
= j
;
482 /* find first hard return */
483 next_line
= find_first_feed(cur_line
, size
);
486 if (next_line
== NULL
)
487 if (size
== search_len
) {
488 if (prefs
.word_mode
== WRAP
) /* Find last space */
489 next_line
= find_last_space(cur_line
, size
);
491 if (next_line
== NULL
)
492 next_line
= crop_at_width(cur_line
);
494 if (prefs
.word_mode
== WRAP
)
496 i
<WRAP_TRIM
&& isspace(next_line
[0]) && !BUFFER_OOB(next_line
);
501 if (prefs
.line_mode
== EXPAND
)
502 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
503 if (next_line
[0] == 0)
504 if (next_line
!= cur_line
)
505 return (unsigned char*) next_line
;
507 /* If next_line is pointing to a zero, increment it; i.e.,
508 leave the terminator at the end of cur_line. If pointing
509 to a hyphen, increment only if there is room to display
510 the hyphen on current line (won't apply in WIDE mode,
511 since it's guarenteed there won't be room). */
512 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
513 if (next_line
[0] == 0)/* ||
514 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
517 if (BUFFER_OOB(next_line
))
523 return (unsigned char*) next_line
;
526 static unsigned char* find_prev_line(const unsigned char* cur_line
)
528 const unsigned char *prev_line
= NULL
;
529 const unsigned char *p
;
531 if BUFFER_OOB(cur_line
)
534 /* To wrap consistently at the same places, we must
535 start with a known hard return, then work downwards.
536 We can either search backwards for a hard return,
537 or simply start wrapping downwards from top of buffer.
538 If current line is not near top of buffer, this is
539 a file with long lines (paragraphs). We would need to
540 read earlier sectors before we could decide how to
541 properly wrap the lines above the current line, but
542 it probably is not worth the disk access. Instead,
543 start with top of buffer and wrap down from there.
544 This may result in some lines wrapping at different
545 points from where they wrap when scrolling down.
546 If buffer is at top of file, start at top of buffer. */
548 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
))
549 prev_line
= p
= NULL
;
551 prev_line
= p
= find_last_feed(buffer
, cur_line
-buffer
-1);
552 /* Null means no line feeds in buffer above current line. */
554 if (prev_line
== NULL
)
555 if (BUFFER_BOF() || cur_line
- buffer
> READ_PREV_ZONE
)
556 prev_line
= p
= buffer
;
557 /* (else return NULL and read previous block) */
559 /* Wrap downwards until too far, then use the one before. */
560 while (p
< cur_line
&& p
!= NULL
) {
562 p
= find_next_line(prev_line
, NULL
);
565 if (BUFFER_OOB(prev_line
))
568 return (unsigned char*) prev_line
;
571 static void fill_buffer(long pos
, unsigned char* buf
, unsigned size
)
573 /* Read from file and preprocess the data */
574 /* To minimize disk access, always read on sector boundaries */
576 bool found_CR
= false;
578 rb
->lseek(fd
, pos
, SEEK_SET
);
579 numread
= rb
->read(fd
, buf
, size
);
580 rb
->button_clear_queue(); /* clear button queue */
582 for(i
= 0; i
< numread
; i
++) {
599 case 0: /* No break between case 0 and default, intentionally */
612 static int read_and_synch(int direction
)
614 /* Read next (or prev) block, and reposition global pointers. */
615 /* direction: 1 for down (i.e., further into file), -1 for up */
616 int move_size
, move_vector
, offset
;
617 unsigned char *fill_buf
;
619 if (direction
== -1) /* up */ {
620 move_size
= SMALL_BLOCK_SIZE
;
622 fill_buf
= TOP_SECTOR
;
623 rb
->memcpy(BOTTOM_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
624 rb
->memcpy(MID_SECTOR
, TOP_SECTOR
, SMALL_BLOCK_SIZE
);
627 if (prefs
.view_mode
== WIDE
) {
628 /* WIDE mode needs more buffer so we have to read smaller blocks */
629 move_size
= SMALL_BLOCK_SIZE
;
630 offset
= LARGE_BLOCK_SIZE
;
631 fill_buf
= BOTTOM_SECTOR
;
632 rb
->memcpy(TOP_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
633 rb
->memcpy(MID_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
636 move_size
= LARGE_BLOCK_SIZE
;
637 offset
= SMALL_BLOCK_SIZE
;
638 fill_buf
= MID_SECTOR
;
639 rb
->memcpy(TOP_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
642 move_vector
= direction
* move_size
;
643 screen_top_ptr
-= move_vector
;
644 file_pos
+= move_vector
;
645 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
646 fill_buffer(file_pos
+ offset
, fill_buf
, move_size
);
650 static void viewer_scroll_up(void)
654 p
= find_prev_line(screen_top_ptr
);
655 if (p
== NULL
&& !BUFFER_BOF()) {
657 p
= find_prev_line(screen_top_ptr
);
663 static void viewer_scroll_down(void)
665 if (next_screen_ptr
!= NULL
)
666 screen_top_ptr
= next_line_ptr
;
669 #ifdef HAVE_LCD_BITMAP
670 static void viewer_scrollbar(void) {
671 int items
, min_shown
, max_shown
;
673 items
= (int) file_size
; /* (SH1 int is same as long) */
674 min_shown
= (int) file_pos
+ (screen_top_ptr
- buffer
);
676 if (next_screen_ptr
== NULL
)
679 max_shown
= min_shown
+ (next_screen_ptr
- screen_top_ptr
);
681 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, 0, SCROLLBAR_WIDTH
-1,
682 LCD_HEIGHT
, items
, min_shown
, max_shown
, VERTICAL
);
686 static void viewer_draw(int col
)
688 int i
, j
, k
, line_len
, line_width
, resynch_move
, spaces
, left_col
=0;
689 int width
, extra_spaces
, indent_spaces
, spaces_per_word
;
690 bool multiple_spacing
, line_is_short
;
692 unsigned char *str
, *oldstr
;
693 unsigned char *line_begin
;
694 unsigned char *line_end
;
696 unsigned char scratch_buffer
[MAX_COLUMNS
+ 1];
697 unsigned char utf8_buffer
[MAX_COLUMNS
*4 + 1];
698 unsigned char *endptr
;
700 /* If col==-1 do all calculations but don't display */
702 #ifdef HAVE_LCD_BITMAP
703 left_col
= prefs
.need_scrollbar
? SCROLLBAR_WIDTH
:0;
707 rb
->lcd_clear_display();
710 line_begin
= line_end
= screen_top_ptr
;
712 for (i
= 0; i
< display_lines
; i
++) {
713 if (BUFFER_OOB(line_end
))
714 break; /* Happens after display last line at BUFFER_EOF() */
716 line_begin
= line_end
;
717 line_end
= find_next_line(line_begin
, &line_is_short
);
719 if (line_end
== NULL
) {
721 if (i
< display_lines
- 1 && !BUFFER_BOF()) {
723 rb
->lcd_clear_display();
725 for (; i
< display_lines
- 1; i
++)
728 line_begin
= line_end
= screen_top_ptr
;
733 line_end
= buffer_end
;
737 resynch_move
= read_and_synch(1); /* Read block & move ptrs */
738 line_begin
-= resynch_move
;
740 next_line_ptr
-= resynch_move
;
742 line_end
= find_next_line(line_begin
, NULL
);
743 if (line_end
== NULL
) /* Should not really happen */
747 line_len
= line_end
- line_begin
;
749 /* calculate line_len */
750 str
= oldstr
= line_begin
;
752 while (str
< line_end
) {
754 str
= crop_at_width(str
);
757 line_width
= j
*draw_columns
;
758 while (oldstr
< line_end
) {
759 oldstr
= get_ucs(oldstr
, &ch
);
760 line_width
+= glyph_width(ch
);
763 if (prefs
.line_mode
== JOIN
) {
764 if (line_begin
[0] == 0) {
766 if (prefs
.word_mode
== CHOP
)
771 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
772 if (k
== MAX_COLUMNS
)
782 scratch_buffer
[k
++] = ' ';
787 scratch_buffer
[k
++] = ' ';
788 if (k
== MAX_COLUMNS
- 1)
791 scratch_buffer
[k
++] = c
;
796 scratch_buffer
[k
] = 0;
797 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
798 prefs
.encoding
, draw_columns
/glyph_width('i'));
802 else if (prefs
.line_mode
== REFLOW
) {
803 if (line_begin
[0] == 0) {
805 if (prefs
.word_mode
== CHOP
)
812 if (!line_is_short
) {
813 multiple_spacing
= false;
815 for (str
= line_begin
; str
< line_end
; ) {
816 str
= get_ucs(str
, &ch
);
820 if ((str
== line_begin
) && (prefs
.word_mode
==WRAP
))
821 /* special case: indent the paragraph,
822 * don't count spaces */
823 indent_spaces
= par_indent_spaces
;
824 else if (!multiple_spacing
)
826 multiple_spacing
= true;
829 multiple_spacing
= false;
830 width
+= glyph_width(ch
);
834 if (multiple_spacing
) spaces
--;
837 /* total number of spaces to insert between words */
838 extra_spaces
= (draw_columns
-width
)/glyph_width(' ')
840 /* number of spaces between each word*/
841 spaces_per_word
= extra_spaces
/ spaces
;
842 /* number of words with n+1 spaces (to fill up) */
843 extra_spaces
= extra_spaces
% spaces
;
844 if (spaces_per_word
> 2) { /* too much spacing is awful */
848 } else { /* this doesn't matter much... no spaces anyway */
849 spaces_per_word
= extra_spaces
= 0;
851 } else { /* end of a paragraph: don't fill line */
856 multiple_spacing
= false;
857 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
858 if (k
== MAX_COLUMNS
)
865 if (j
==0 && prefs
.word_mode
==WRAP
) { /* indent paragraph */
866 for (j
=0; j
<par_indent_spaces
; j
++)
867 scratch_buffer
[k
++] = ' ';
870 else if (!multiple_spacing
) {
871 for (width
= spaces
<extra_spaces
? -1:0; width
< spaces_per_word
; width
++)
872 scratch_buffer
[k
++] = ' ';
875 multiple_spacing
= true;
878 scratch_buffer
[k
++] = c
;
879 multiple_spacing
= false;
885 scratch_buffer
[k
] = 0;
886 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
887 prefs
.encoding
, k
-col
);
891 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
893 if (line_width
> col
) {
894 str
= oldstr
= line_begin
;
897 while( (width
<draw_columns
) && (oldstr
<line_end
) )
899 oldstr
= get_ucs(oldstr
, &ch
);
901 k
-= glyph_width(ch
);
904 width
+= glyph_width(ch
);
908 if(prefs
.view_mode
==WIDE
)
909 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
910 prefs
.encoding
, oldstr
-line_begin
);
912 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
913 prefs
.encoding
, line_end
-line_begin
);
917 if (col
!= -1 && line_width
> col
)
918 #ifdef HAVE_LCD_BITMAP
919 rb
->lcd_putsxy(left_col
, i
*pf
->height
, utf8_buffer
);
921 rb
->lcd_puts(left_col
, i
, utf8_buffer
);
923 if (line_width
> max_line_len
)
924 max_line_len
= line_width
;
927 next_line_ptr
= line_end
;
929 next_screen_ptr
= line_end
;
930 if (BUFFER_OOB(next_screen_ptr
))
931 next_screen_ptr
= NULL
;
933 #ifdef HAVE_LCD_BITMAP
934 next_screen_to_draw_ptr
= prefs
.page_mode
==OVERLAP
? line_begin
: next_screen_ptr
;
936 if (prefs
.need_scrollbar
)
939 next_screen_to_draw_ptr
= next_screen_ptr
;
946 static void viewer_top(void)
948 /* Read top of file into buffer
949 and point screen pointer to top */
951 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
952 screen_top_ptr
= buffer
;
953 fill_buffer(0, buffer
, BUFFER_SIZE
);
956 static void viewer_bottom(void)
958 /* Read bottom of file into buffer
959 and point screen pointer to bottom */
962 if (file_size
> BUFFER_SIZE
) {
963 /* Find last buffer in file, round up to next sector boundary */
964 last_sectors
= file_size
- BUFFER_SIZE
+ SMALL_BLOCK_SIZE
;
965 last_sectors
/= SMALL_BLOCK_SIZE
;
966 last_sectors
*= SMALL_BLOCK_SIZE
;
971 file_pos
= last_sectors
;
972 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
973 screen_top_ptr
= buffer_end
-1;
974 fill_buffer(last_sectors
, buffer
, BUFFER_SIZE
);
977 #ifdef HAVE_LCD_BITMAP
978 static void init_need_scrollbar(void) {
979 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
980 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
982 prefs
.need_scrollbar
= NEED_SCROLLBAR();
983 draw_columns
= prefs
.need_scrollbar
? display_columns
-SCROLLBAR_WIDTH
: display_columns
;
984 par_indent_spaces
= draw_columns
/(5*glyph_width(' '));
987 #define init_need_scrollbar()
990 static bool viewer_init(void)
992 #ifdef HAVE_LCD_BITMAP
994 pf
= rb
->font_get(FONT_UI
);
996 display_lines
= LCD_HEIGHT
/ pf
->height
;
997 draw_columns
= display_columns
= LCD_WIDTH
;
999 /* REAL fixed pitch :) all chars use up 1 cell */
1001 draw_columns
= display_columns
= 11;
1002 par_indent_spaces
= 2;
1005 fd
= rb
->open(file_name
, O_RDONLY
);
1009 file_size
= rb
->filesize(fd
);
1013 /* Init mac_text value used in processing buffer */
1016 /* Set codepage to system default */
1017 prefs
.encoding
= rb
->global_settings
->default_codepage
;
1019 /* Read top of file into buffer;
1020 init file_pos, buffer_end, screen_top_ptr */
1023 /* Init prefs.need_scrollbar value */
1024 init_need_scrollbar();
1029 static void viewer_reset_settings(void)
1031 prefs
.word_mode
= WRAP
;
1032 prefs
.line_mode
= NORMAL
;
1033 prefs
.view_mode
= NARROW
;
1034 prefs
.scroll_mode
= PAGE
;
1035 #ifdef HAVE_LCD_BITMAP
1036 prefs
.page_mode
= NO_OVERLAP
;
1037 prefs
.scrollbar_mode
= SB_OFF
;
1039 prefs
.autoscroll_speed
= 1;
1042 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1046 settings_fd
=rb
->open(SETTINGS_FILE
, O_RDONLY
);
1047 if (settings_fd
< 0)
1049 rb
->splash(HZ
*2, "No Settings File");
1052 if (rb
->filesize(settings_fd
) != sizeof(struct preferences
))
1054 rb
->splash(HZ
*2, "Settings File Invalid");
1058 rb
->read(settings_fd
, &prefs
, sizeof(struct preferences
));
1059 rb
->close(settings_fd
);
1060 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_RDONLY
);
1061 if (settings_fd
>= 0)
1063 struct bookmark_file_data
*data
= (struct bookmark_file_data
*)buffer
; /* grab the text buffer */
1065 data
->bookmarked_files_count
= 0;
1066 rb
->read(settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int)); /* figure out how many items to read */
1067 if (data
->bookmarked_files_count
> MAX_BOOKMARKED_FILES
)
1068 data
->bookmarked_files_count
= MAX_BOOKMARKED_FILES
; /* dump the older files */
1069 rb
->read(settings_fd
, data
->bookmarks
,
1070 sizeof(struct bookmarked_file_info
) * data
->bookmarked_files_count
);
1071 rb
->close(settings_fd
);
1072 for (i
=0; i
< data
->bookmarked_files_count
; i
++)
1074 if (!rb
->strcmp(file_name
, data
->bookmarks
[i
].filename
))
1077 if (i
< data
->bookmarked_files_count
)
1079 /* it is in the list, write everything back in the correct order, and reload the file correctly */
1080 settings_fd
= rb
->creat(BOOKMARKS_FILE
);
1081 if (settings_fd
>=0 )
1083 if (data
->bookmarked_files_count
> MAX_BOOKMARKED_FILES
)
1084 data
->bookmarked_files_count
= MAX_BOOKMARKED_FILES
; /* dump the older files */
1085 rb
->write (settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1086 /* write this item, then all up to it, then all after it */
1087 rb
->write (settings_fd
, &data
->bookmarks
[i
], sizeof(struct bookmarked_file_info
));
1088 rb
->write (settings_fd
, data
->bookmarks
, sizeof(struct bookmarked_file_info
)*i
);
1089 rb
->write (settings_fd
, &data
->bookmarks
[i
+1],
1090 sizeof(struct bookmarked_file_info
)*(data
->bookmarked_files_count
-i
-1));
1091 rb
->close(settings_fd
);
1093 file_pos
= data
->bookmarks
[i
].file_position
;
1094 screen_top_ptr
= buffer
+ data
->bookmarks
[i
].top_ptr_pos
;
1096 else /* not in list, write the list to the file */
1098 settings_fd
= rb
->creat(BOOKMARKS_FILE
);
1099 if (settings_fd
>=0 )
1101 if ((data
->bookmarked_files_count
+ 1) > MAX_BOOKMARKED_FILES
)
1102 data
->bookmarked_files_count
= MAX_BOOKMARKED_FILES
; /* dump the older files */
1103 else data
->bookmarked_files_count
++;
1104 rb
->write (settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1105 rb
->PREFIX(lseek
)(settings_fd
,sizeof(struct bookmarked_file_info
),SEEK_CUR
);
1106 // rb->memset(&dummy,0,sizeof(struct bookmarked_file_info)); /* the actual info will be written on exit */
1107 //rb->write (settings_fd, &dummy, sizeof(struct bookmarked_file_info));
1108 rb
->write (settings_fd
, data
->bookmarks
, sizeof(struct bookmarked_file_info
)*(data
->bookmarked_files_count
));
1109 rb
->close(settings_fd
);
1112 } /* BOOKMARKS_FILE opened ok */
1113 init_need_scrollbar();
1115 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1117 if (BUFFER_OOB(screen_top_ptr
)) {
1118 screen_top_ptr
= buffer
;
1121 fill_buffer(file_pos
, buffer
, BUFFER_SIZE
);
1124 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1127 settings_fd
= rb
->creat(SETTINGS_FILE
); /* create the settings file */
1129 rb
->write (settings_fd
, &prefs
, sizeof(struct preferences
));
1130 rb
->close(settings_fd
);
1132 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_WRONLY
|O_CREAT
);
1133 if (settings_fd
>= 0 )
1135 struct bookmarked_file_info b
;
1136 b
.file_position
= file_pos
;
1137 b
.top_ptr_pos
= screen_top_ptr
- buffer
;
1138 rb
->memset(&b
.filename
[0],0,MAX_PATH
);
1139 rb
->strcpy(b
.filename
,file_name
);
1140 rb
->PREFIX(lseek
)(settings_fd
,sizeof(signed int),SEEK_SET
);
1141 rb
->write (settings_fd
, &b
, sizeof(struct bookmarked_file_info
));
1142 rb
->close(settings_fd
);
1146 static void viewer_exit(void *parameter
)
1150 viewer_save_settings();
1154 static int col_limit(int col
)
1159 if (col
> max_line_len
- 2*glyph_width('o'))
1160 col
= max_line_len
- 2*glyph_width('o');
1165 /* settings helper functions */
1167 static bool encoding_setting(void)
1169 static const struct opt_items names
[] = {
1174 {"ISO-8859-11", -1},
1185 return rb
->set_option("Encoding", &prefs
.encoding
, INT
, names
,
1186 sizeof(names
) / sizeof(names
[0]), NULL
);
1189 static bool word_wrap_setting(void)
1191 static const struct opt_items names
[] = {
1193 {"Off (Chop Words)", -1},
1196 return rb
->set_option("Word Wrap", &prefs
.word_mode
, INT
,
1200 static bool line_mode_setting(void)
1202 static const struct opt_items names
[] = {
1205 {"Expand Lines", -1},
1206 #ifdef HAVE_LCD_BITMAP
1207 {"Reflow Lines", -1},
1211 return rb
->set_option("Line Mode", &prefs
.line_mode
, INT
, names
,
1212 sizeof(names
) / sizeof(names
[0]), NULL
);
1215 static bool view_mode_setting(void)
1217 static const struct opt_items names
[] = {
1218 {"No (Narrow)", -1},
1222 ret
= rb
->set_option("Wide View", &prefs
.view_mode
, INT
,
1224 if (prefs
.view_mode
== NARROW
)
1229 static bool scroll_mode_setting(void)
1231 static const struct opt_items names
[] = {
1232 {"Scroll by Page", -1},
1233 {"Scroll by Line", -1},
1236 return rb
->set_option("Scroll Mode", &prefs
.scroll_mode
, INT
,
1240 #ifdef HAVE_LCD_BITMAP
1241 static bool page_mode_setting(void)
1243 static const struct opt_items names
[] = {
1248 return rb
->set_option("Overlap Pages", &prefs
.page_mode
, INT
,
1252 static bool scrollbar_setting(void)
1254 static const struct opt_items names
[] = {
1259 return rb
->set_option("Show Scrollbar", &prefs
.scrollbar_mode
, INT
,
1264 static bool autoscroll_speed_setting(void)
1266 return rb
->set_int("Auto-scroll Speed", "", UNIT_INT
,
1267 &prefs
.autoscroll_speed
, NULL
, 1, 1, 10, NULL
);
1270 static bool viewer_options_menu(void)
1275 static const struct menu_item items
[] = {
1276 {"Encoding", encoding_setting
},
1277 {"Word Wrap", word_wrap_setting
},
1278 {"Line Mode", line_mode_setting
},
1279 {"Wide View", view_mode_setting
},
1280 #ifdef HAVE_LCD_BITMAP
1281 {"Show Scrollbar", scrollbar_setting
},
1282 {"Overlap Pages", page_mode_setting
},
1284 {"Scroll Mode", scroll_mode_setting
},
1285 {"Auto-Scroll Speed", autoscroll_speed_setting
},
1287 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
),
1288 NULL
, NULL
, NULL
, NULL
);
1290 result
= menu_run(m
);
1292 #ifdef HAVE_LCD_BITMAP
1293 rb
->lcd_setmargins(0,0);
1295 /* Show-scrollbar mode for current view-width mode */
1296 if (!ONE_SCREEN_FITS_ALL())
1297 if (prefs
.scrollbar_mode
== true)
1298 init_need_scrollbar();
1303 static void viewer_menu(void)
1307 static const struct menu_item items
[] = {
1309 {"Viewer Options", NULL
},
1310 {"Show Playback Menu", NULL
},
1314 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
), NULL
, NULL
, NULL
, NULL
);
1315 result
=menu_show(m
);
1319 rb
->splash(1, "Saving Settings");
1324 case 1: /* change settings */
1325 done
= viewer_options_menu();
1327 case 2: /* playback control */
1328 playback_control(rb
);
1330 case 3: /* return */
1334 #ifdef HAVE_LCD_BITMAP
1335 rb
->lcd_setmargins(0,0);
1340 enum plugin_status
plugin_start(struct plugin_api
* api
, void* file
)
1343 int lastbutton
= BUTTON_NONE
;
1344 bool autoscroll
= false;
1348 old_tick
= *rb
->current_tick
;
1351 return PLUGIN_ERROR
;
1356 rb
->splash(HZ
, "Error");
1361 viewer_reset_settings(); /* load defaults first */
1362 viewer_load_settings(); /* .. then try to load from disk */
1370 if(old_tick
<= *rb
->current_tick
- (110-prefs
.autoscroll_speed
*10))
1372 viewer_scroll_down();
1374 old_tick
= *rb
->current_tick
;
1378 button
= rb
->button_get_w_tmo(HZ
/10);
1384 case VIEWER_AUTOSCROLL
:
1385 #ifdef VIEWER_AUTOSCROLL_PRE
1386 if (lastbutton
!= VIEWER_AUTOSCROLL_PRE
)
1389 autoscroll
= !autoscroll
;
1392 case VIEWER_PAGE_UP
:
1393 case VIEWER_PAGE_UP
| BUTTON_REPEAT
:
1394 if (prefs
.scroll_mode
== PAGE
)
1397 #ifdef HAVE_LCD_BITMAP
1398 for (i
= prefs
.page_mode
==OVERLAP
? 1:0; i
< display_lines
; i
++)
1400 for (i
= 0; i
< display_lines
; i
++)
1406 old_tick
= *rb
->current_tick
;
1410 case VIEWER_PAGE_DOWN
:
1411 case VIEWER_PAGE_DOWN
| BUTTON_REPEAT
:
1412 if (prefs
.scroll_mode
== PAGE
)
1415 if (next_screen_ptr
!= NULL
)
1416 screen_top_ptr
= next_screen_to_draw_ptr
;
1419 viewer_scroll_down();
1420 old_tick
= *rb
->current_tick
;
1424 case VIEWER_SCREEN_LEFT
:
1425 case VIEWER_SCREEN_LEFT
| BUTTON_REPEAT
:
1426 if (prefs
.view_mode
== WIDE
) {
1428 col
-= draw_columns
;
1429 col
= col_limit(col
);
1431 else { /* prefs.view_mode == NARROW */
1439 case VIEWER_SCREEN_RIGHT
:
1440 case VIEWER_SCREEN_RIGHT
| BUTTON_REPEAT
:
1441 if (prefs
.view_mode
== WIDE
) {
1443 col
+= draw_columns
;
1444 col
= col_limit(col
);
1446 else { /* prefs.view_mode == NARROW */
1447 /* Bottom of file */
1454 #ifdef VIEWER_LINE_UP
1455 case VIEWER_LINE_UP
:
1456 case VIEWER_LINE_UP
| BUTTON_REPEAT
:
1457 /* Scroll up one line */
1459 old_tick
= *rb
->current_tick
;
1463 case VIEWER_LINE_DOWN
:
1464 case VIEWER_LINE_DOWN
| BUTTON_REPEAT
:
1465 /* Scroll down one line */
1466 if (next_screen_ptr
!= NULL
)
1467 screen_top_ptr
= next_line_ptr
;
1468 old_tick
= *rb
->current_tick
;
1472 #ifdef VIEWER_COLUMN_LEFT
1473 case VIEWER_COLUMN_LEFT
:
1474 case VIEWER_COLUMN_LEFT
| BUTTON_REPEAT
:
1475 if (prefs
.view_mode
== WIDE
) {
1476 /* Scroll left one column */
1477 col
-= glyph_width('o');
1478 col
= col_limit(col
);
1483 case VIEWER_COLUMN_RIGHT
:
1484 case VIEWER_COLUMN_RIGHT
| BUTTON_REPEAT
:
1485 if (prefs
.view_mode
== WIDE
) {
1486 /* Scroll right one column */
1487 col
+= glyph_width('o');
1488 col
= col_limit(col
);
1494 #ifdef VIEWER_RC_QUIT
1495 case VIEWER_RC_QUIT
:
1503 if (rb
->default_event_handler_ex(button
, viewer_exit
, NULL
)
1504 == SYS_USB_CONNECTED
)
1505 return PLUGIN_USB_CONNECTED
;
1508 if (button
!= BUTTON_NONE
)
1510 lastbutton
= button
;