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 VIEWERS_DIR "/viewer.dat" /* binary file, so dont use .cfg */
28 #define BOOKMARKS_FILE VIEWERS_DIR "/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 TOP_SECTOR buffer
37 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
38 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
39 #define SCROLLBAR_WIDTH 6
41 #define MAX_BOOKMARKED_FILES ((buffer_size/(signed)sizeof(struct bookmarked_file_info))-1)
43 /* Out-Of-Bounds test for any pointer to data in the buffer */
44 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
46 /* Does the buffer contain the beginning of the file? */
47 #define BUFFER_BOF() (file_pos==0)
49 /* Does the buffer contain the end of the file? */
50 #define BUFFER_EOF() (file_size-file_pos <= buffer_size)
52 /* Formula for the endpoint address outside of buffer data */
53 #define BUFFER_END() \
54 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+buffer_size))
56 /* Is the entire file being shown in one screen? */
57 #define ONE_SCREEN_FITS_ALL() \
58 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
60 /* Is a scrollbar called for on the current screen? */
61 #define NEED_SCROLLBAR() \
62 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
64 /* variable button definitions */
67 #if CONFIG_KEYPAD == RECORDER_PAD
68 #define VIEWER_QUIT BUTTON_OFF
69 #define VIEWER_PAGE_UP BUTTON_UP
70 #define VIEWER_PAGE_DOWN BUTTON_DOWN
71 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
72 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
73 #define VIEWER_MENU BUTTON_F1
74 #define VIEWER_AUTOSCROLL BUTTON_PLAY
75 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
76 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
77 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
78 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
80 #elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
81 #define VIEWER_QUIT BUTTON_OFF
82 #define VIEWER_PAGE_UP BUTTON_UP
83 #define VIEWER_PAGE_DOWN BUTTON_DOWN
84 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
85 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
86 #define VIEWER_MENU BUTTON_F1
87 #define VIEWER_AUTOSCROLL BUTTON_SELECT
88 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
89 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
90 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
91 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
94 #elif CONFIG_KEYPAD == ONDIO_PAD
95 #define VIEWER_QUIT BUTTON_OFF
96 #define VIEWER_PAGE_UP BUTTON_UP
97 #define VIEWER_PAGE_DOWN BUTTON_DOWN
98 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
99 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
100 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
101 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
102 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
105 #elif CONFIG_KEYPAD == PLAYER_PAD
106 #define VIEWER_QUIT BUTTON_STOP
107 #define VIEWER_PAGE_UP BUTTON_LEFT
108 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
109 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
110 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
111 #define VIEWER_MENU BUTTON_MENU
112 #define VIEWER_AUTOSCROLL BUTTON_PLAY
114 /* iRiver H1x0 && H3x0 keys */
115 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
116 (CONFIG_KEYPAD == IRIVER_H300_PAD)
117 #define VIEWER_QUIT BUTTON_OFF
118 #define VIEWER_PAGE_UP BUTTON_UP
119 #define VIEWER_PAGE_DOWN BUTTON_DOWN
120 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
121 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
122 #define VIEWER_MENU BUTTON_MODE
123 #define VIEWER_AUTOSCROLL BUTTON_SELECT
124 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
125 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
126 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
127 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
129 #define VIEWER_RC_QUIT BUTTON_RC_STOP
132 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
133 (CONFIG_KEYPAD == IPOD_3G_PAD) || \
134 (CONFIG_KEYPAD == IPOD_1G2G_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_BACK
184 #define VIEWER_LINE_DOWN BUTTON_SCROLL_FWD
186 /* Sansa C200 keys */
187 #elif CONFIG_KEYPAD == SANSA_C200_PAD
188 #define VIEWER_QUIT BUTTON_POWER
189 #define VIEWER_PAGE_UP BUTTON_VOL_UP
190 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
191 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
192 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
193 #define VIEWER_MENU BUTTON_SELECT
194 #define VIEWER_AUTOSCROLL BUTTON_REC
195 #define VIEWER_LINE_UP BUTTON_UP
196 #define VIEWER_LINE_DOWN BUTTON_DOWN
198 /* iriver H10 keys */
199 #elif CONFIG_KEYPAD == IRIVER_H10_PAD
200 #define VIEWER_QUIT BUTTON_POWER
201 #define VIEWER_PAGE_UP BUTTON_SCROLL_UP
202 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_DOWN
203 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
204 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
205 #define VIEWER_MENU BUTTON_REW
206 #define VIEWER_AUTOSCROLL BUTTON_PLAY
208 #elif CONFIG_KEYPAD == MROBE500_PAD
209 #define VIEWER_QUIT BUTTON_POWER
210 #define VIEWER_PAGE_UP BUTTON_RC_PLAY
211 #define VIEWER_PAGE_DOWN BUTTON_RC_DOWN
212 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
213 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
214 #define VIEWER_MENU BUTTON_RC_HEART
215 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
217 #elif CONFIG_KEYPAD == GIGABEAT_S_PAD
218 #define VIEWER_QUIT BUTTON_BACK
219 #define VIEWER_PAGE_UP BUTTON_VOL_UP
220 #define VIEWER_PAGE_DOWN BUTTON_VOL_DOWN
221 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
222 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
223 #define VIEWER_MENU BUTTON_MENU
224 #define VIEWER_AUTOSCROLL BUTTON_PLAY
228 /* stuff for the bookmarking */
229 struct bookmarked_file_info
{
232 char filename
[MAX_PATH
];
235 struct bookmark_file_data
{
236 signed int bookmarked_files_count
;
237 struct bookmarked_file_info bookmarks
[];
250 REFLOW
, /* won't be set on charcell LCD, must be last */
274 } encoding
; /* FIXME: What should default encoding be? */
276 #ifdef HAVE_LCD_BITMAP
287 #endif /* HAVE_LCD_BITMAP */
294 int autoscroll_speed
;
298 struct preferences prefs
;
299 struct preferences old_prefs
;
301 static unsigned char *buffer
;
302 static long buffer_size
;
303 static unsigned char line_break
[] = {0,0x20,9,0xB,0xC,'-'};
304 static int display_columns
; /* number of (pixel) columns on the display */
305 static int display_lines
; /* number of lines on the display */
306 static int draw_columns
; /* number of (pixel) columns available for text */
307 static int par_indent_spaces
; /* number of spaces to indent first paragraph */
309 static char *file_name
;
310 static long file_size
;
311 static long start_position
; /* position in the file after the viewer is started */
312 static bool mac_text
;
313 static long file_pos
; /* Position of the top of the buffer in the file */
314 static unsigned char *buffer_end
; /*Set to BUFFER_END() when file_pos changes*/
315 static int max_line_len
;
316 static unsigned char *screen_top_ptr
;
317 static unsigned char *next_screen_ptr
;
318 static unsigned char *next_screen_to_draw_ptr
;
319 static unsigned char *next_line_ptr
;
320 static struct plugin_api
* rb
;
321 #ifdef HAVE_LCD_BITMAP
322 static struct font
*pf
;
326 int glyph_width(int ch
)
331 #ifdef HAVE_LCD_BITMAP
332 return rb
->font_get_width(pf
, ch
);
338 unsigned char* get_ucs(const unsigned char* str
, unsigned short* ch
)
340 unsigned char utf8_tmp
[6];
343 if (prefs
.encoding
== UTF8
)
344 return (unsigned char*)rb
->utf8decode(str
, ch
);
346 count
= BUFFER_OOB(str
+2)? 1:2;
347 rb
->iso_decode(str
, utf8_tmp
, prefs
.encoding
, count
);
348 rb
->utf8decode(utf8_tmp
, ch
);
350 if ((prefs
.encoding
== SJIS
&& *str
> 0xA0 && *str
< 0xE0) || prefs
.encoding
< SJIS
)
351 return (unsigned char*)str
+1;
353 return (unsigned char*)str
+2;
359 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
360 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
361 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
362 static unsigned char* crop_at_width(const unsigned char* p
)
366 const unsigned char *oldp
= p
;
370 while (LINE_IS_NOT_FULL
) {
373 ADVANCE_COUNTERS(ch
);
376 return (unsigned char*)oldp
;
379 static unsigned char* find_first_feed(const unsigned char* p
, int size
)
383 for (i
=0; i
< size
; i
++)
385 return (unsigned char*) p
+i
;
390 static unsigned char* find_last_feed(const unsigned char* p
, int size
)
394 for (i
=size
-1; i
>=0; i
--)
396 return (unsigned char*) p
+i
;
401 static unsigned char* find_last_space(const unsigned char* p
, int size
)
405 k
= (prefs
.line_mode
==JOIN
) || (prefs
.line_mode
==REFLOW
) ? 0:1;
407 if (!BUFFER_OOB(&p
[size
]))
408 for (j
=k
; j
< ((int) sizeof(line_break
)) - 1; j
++)
409 if (p
[size
] == line_break
[j
])
410 return (unsigned char*) p
+size
;
412 for (i
=size
-1; i
>=0; i
--)
413 for (j
=k
; j
< (int) sizeof(line_break
); j
++)
415 if (!((p
[i
] == '-') && (prefs
.word_mode
== WRAP
)))
416 if (p
[i
] == line_break
[j
])
417 return (unsigned char*) p
+i
;
423 static unsigned char* find_next_line(const unsigned char* cur_line
, bool *is_short
)
425 const unsigned char *next_line
= NULL
;
426 int size
, i
, j
, k
, width
, search_len
, spaces
, newlines
;
430 if (is_short
!= NULL
)
433 if BUFFER_OOB(cur_line
)
436 if (prefs
.view_mode
== WIDE
) {
437 search_len
= MAX_WIDTH
;
439 else { /* prefs.view_mode == NARROW */
440 search_len
= crop_at_width(cur_line
) - cur_line
;
443 size
= BUFFER_OOB(cur_line
+search_len
) ? buffer_end
-cur_line
: search_len
;
445 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
)) {
446 /* Need to scan ahead and possibly increase search_len and size,
447 or possibly set next_line at second hard return in a row. */
450 for (j
=k
=width
=spaces
=newlines
=0; ; j
++) {
451 if (BUFFER_OOB(cur_line
+j
))
454 size
= search_len
= j
;
461 if (prefs
.line_mode
== REFLOW
) {
464 next_line
= cur_line
+ size
;
465 return (unsigned char*) next_line
;
467 if (j
==0) /* i=1 is intentional */
468 for (i
=0; i
<par_indent_spaces
; i
++)
469 ADVANCE_COUNTERS(' ');
471 if (!first_chars
) spaces
++;
477 next_line
= cur_line
+ size
- spaces
;
478 if (next_line
!= cur_line
)
479 return (unsigned char*) next_line
;
485 if (BUFFER_OOB(cur_line
+size
) || size
> 2*search_len
)
488 spaces
= first_chars
? 0:1;
492 if (prefs
.line_mode
==JOIN
|| newlines
>0) {
495 ADVANCE_COUNTERS(' ');
497 size
= search_len
= j
;
503 /* REFLOW, multiple spaces between words: count only
504 * one. If more are needed, they will be added
508 ADVANCE_COUNTERS(' ');
510 size
= search_len
= j
;
521 /* find first hard return */
522 next_line
= find_first_feed(cur_line
, size
);
525 if (next_line
== NULL
)
526 if (size
== search_len
) {
527 if (prefs
.word_mode
== WRAP
) /* Find last space */
528 next_line
= find_last_space(cur_line
, size
);
530 if (next_line
== NULL
)
531 next_line
= crop_at_width(cur_line
);
533 if (prefs
.word_mode
== WRAP
)
535 i
<WRAP_TRIM
&& isspace(next_line
[0]) && !BUFFER_OOB(next_line
);
540 if (prefs
.line_mode
== EXPAND
)
541 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
542 if (next_line
[0] == 0)
543 if (next_line
!= cur_line
)
544 return (unsigned char*) next_line
;
546 /* If next_line is pointing to a zero, increment it; i.e.,
547 leave the terminator at the end of cur_line. If pointing
548 to a hyphen, increment only if there is room to display
549 the hyphen on current line (won't apply in WIDE mode,
550 since it's guarenteed there won't be room). */
551 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
552 if (next_line
[0] == 0)/* ||
553 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
556 if (BUFFER_OOB(next_line
))
562 return (unsigned char*) next_line
;
565 static unsigned char* find_prev_line(const unsigned char* cur_line
)
567 const unsigned char *prev_line
= NULL
;
568 const unsigned char *p
;
570 if BUFFER_OOB(cur_line
)
573 /* To wrap consistently at the same places, we must
574 start with a known hard return, then work downwards.
575 We can either search backwards for a hard return,
576 or simply start wrapping downwards from top of buffer.
577 If current line is not near top of buffer, this is
578 a file with long lines (paragraphs). We would need to
579 read earlier sectors before we could decide how to
580 properly wrap the lines above the current line, but
581 it probably is not worth the disk access. Instead,
582 start with top of buffer and wrap down from there.
583 This may result in some lines wrapping at different
584 points from where they wrap when scrolling down.
585 If buffer is at top of file, start at top of buffer. */
587 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
))
588 prev_line
= p
= NULL
;
590 prev_line
= p
= find_last_feed(buffer
, cur_line
-buffer
-1);
591 /* Null means no line feeds in buffer above current line. */
593 if (prev_line
== NULL
)
594 if (BUFFER_BOF() || cur_line
- buffer
> READ_PREV_ZONE
)
595 prev_line
= p
= buffer
;
596 /* (else return NULL and read previous block) */
598 /* Wrap downwards until too far, then use the one before. */
599 while (p
< cur_line
&& p
!= NULL
) {
601 p
= find_next_line(prev_line
, NULL
);
604 if (BUFFER_OOB(prev_line
))
607 return (unsigned char*) prev_line
;
610 static void fill_buffer(long pos
, unsigned char* buf
, unsigned size
)
612 /* Read from file and preprocess the data */
613 /* To minimize disk access, always read on sector boundaries */
615 bool found_CR
= false;
617 rb
->lseek(fd
, pos
, SEEK_SET
);
618 numread
= rb
->read(fd
, buf
, size
);
619 rb
->button_clear_queue(); /* clear button queue */
621 for(i
= 0; i
< numread
; i
++) {
638 case 0: /* No break between case 0 and default, intentionally */
651 static int read_and_synch(int direction
)
653 /* Read next (or prev) block, and reposition global pointers. */
654 /* direction: 1 for down (i.e., further into file), -1 for up */
655 int move_size
, move_vector
, offset
;
656 unsigned char *fill_buf
;
658 if (direction
== -1) /* up */ {
659 move_size
= SMALL_BLOCK_SIZE
;
661 fill_buf
= TOP_SECTOR
;
662 rb
->memcpy(BOTTOM_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
663 rb
->memcpy(MID_SECTOR
, TOP_SECTOR
, SMALL_BLOCK_SIZE
);
666 if (prefs
.view_mode
== WIDE
) {
667 /* WIDE mode needs more buffer so we have to read smaller blocks */
668 move_size
= SMALL_BLOCK_SIZE
;
669 offset
= LARGE_BLOCK_SIZE
;
670 fill_buf
= BOTTOM_SECTOR
;
671 rb
->memcpy(TOP_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
672 rb
->memcpy(MID_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
675 move_size
= LARGE_BLOCK_SIZE
;
676 offset
= SMALL_BLOCK_SIZE
;
677 fill_buf
= MID_SECTOR
;
678 rb
->memcpy(TOP_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
681 move_vector
= direction
* move_size
;
682 screen_top_ptr
-= move_vector
;
683 file_pos
+= move_vector
;
684 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
685 fill_buffer(file_pos
+ offset
, fill_buf
, move_size
);
689 static void viewer_scroll_up(void)
693 p
= find_prev_line(screen_top_ptr
);
694 if (p
== NULL
&& !BUFFER_BOF()) {
696 p
= find_prev_line(screen_top_ptr
);
702 static void viewer_scroll_down(void)
704 if (next_screen_ptr
!= NULL
)
705 screen_top_ptr
= next_line_ptr
;
708 #ifdef HAVE_LCD_BITMAP
709 static void viewer_scrollbar(void) {
710 int items
, min_shown
, max_shown
;
712 items
= (int) file_size
; /* (SH1 int is same as long) */
713 min_shown
= (int) file_pos
+ (screen_top_ptr
- buffer
);
715 if (next_screen_ptr
== NULL
)
718 max_shown
= min_shown
+ (next_screen_ptr
- screen_top_ptr
);
720 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, 0, SCROLLBAR_WIDTH
-1,
721 LCD_HEIGHT
, items
, min_shown
, max_shown
, VERTICAL
);
725 static void viewer_draw(int col
)
727 int i
, j
, k
, line_len
, line_width
, resynch_move
, spaces
, left_col
=0;
728 int width
, extra_spaces
, indent_spaces
, spaces_per_word
;
729 bool multiple_spacing
, line_is_short
;
731 unsigned char *str
, *oldstr
;
732 unsigned char *line_begin
;
733 unsigned char *line_end
;
735 unsigned char scratch_buffer
[MAX_COLUMNS
+ 1];
736 unsigned char utf8_buffer
[MAX_COLUMNS
*4 + 1];
737 unsigned char *endptr
;
739 /* If col==-1 do all calculations but don't display */
741 #ifdef HAVE_LCD_BITMAP
742 left_col
= prefs
.need_scrollbar
? SCROLLBAR_WIDTH
:0;
746 rb
->lcd_clear_display();
749 line_begin
= line_end
= screen_top_ptr
;
751 for (i
= 0; i
< display_lines
; i
++) {
752 if (BUFFER_OOB(line_end
))
753 break; /* Happens after display last line at BUFFER_EOF() */
755 line_begin
= line_end
;
756 line_end
= find_next_line(line_begin
, &line_is_short
);
758 if (line_end
== NULL
) {
760 if (i
< display_lines
- 1 && !BUFFER_BOF()) {
762 rb
->lcd_clear_display();
764 for (; i
< display_lines
- 1; i
++)
767 line_begin
= line_end
= screen_top_ptr
;
772 line_end
= buffer_end
;
776 resynch_move
= read_and_synch(1); /* Read block & move ptrs */
777 line_begin
-= resynch_move
;
779 next_line_ptr
-= resynch_move
;
781 line_end
= find_next_line(line_begin
, NULL
);
782 if (line_end
== NULL
) /* Should not really happen */
786 line_len
= line_end
- line_begin
;
788 /* calculate line_len */
789 str
= oldstr
= line_begin
;
791 while (str
< line_end
) {
793 str
= crop_at_width(str
);
796 line_width
= j
*draw_columns
;
797 while (oldstr
< line_end
) {
798 oldstr
= get_ucs(oldstr
, &ch
);
799 line_width
+= glyph_width(ch
);
802 if (prefs
.line_mode
== JOIN
) {
803 if (line_begin
[0] == 0) {
805 if (prefs
.word_mode
== CHOP
)
810 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
811 if (k
== MAX_COLUMNS
)
821 scratch_buffer
[k
++] = ' ';
826 scratch_buffer
[k
++] = ' ';
827 if (k
== MAX_COLUMNS
- 1)
830 scratch_buffer
[k
++] = c
;
835 scratch_buffer
[k
] = 0;
836 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
837 prefs
.encoding
, draw_columns
/glyph_width('i'));
841 else if (prefs
.line_mode
== REFLOW
) {
842 if (line_begin
[0] == 0) {
844 if (prefs
.word_mode
== CHOP
)
851 if (!line_is_short
) {
852 multiple_spacing
= false;
854 for (str
= line_begin
; str
< line_end
; ) {
855 str
= get_ucs(str
, &ch
);
859 if ((str
== line_begin
) && (prefs
.word_mode
==WRAP
))
860 /* special case: indent the paragraph,
861 * don't count spaces */
862 indent_spaces
= par_indent_spaces
;
863 else if (!multiple_spacing
)
865 multiple_spacing
= true;
868 multiple_spacing
= false;
869 width
+= glyph_width(ch
);
873 if (multiple_spacing
) spaces
--;
876 /* total number of spaces to insert between words */
877 extra_spaces
= (draw_columns
-width
)/glyph_width(' ')
879 /* number of spaces between each word*/
880 spaces_per_word
= extra_spaces
/ spaces
;
881 /* number of words with n+1 spaces (to fill up) */
882 extra_spaces
= extra_spaces
% spaces
;
883 if (spaces_per_word
> 2) { /* too much spacing is awful */
887 } else { /* this doesn't matter much... no spaces anyway */
888 spaces_per_word
= extra_spaces
= 0;
890 } else { /* end of a paragraph: don't fill line */
895 multiple_spacing
= false;
896 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
897 if (k
== MAX_COLUMNS
)
904 if (j
==0 && prefs
.word_mode
==WRAP
) { /* indent paragraph */
905 for (j
=0; j
<par_indent_spaces
; j
++)
906 scratch_buffer
[k
++] = ' ';
909 else if (!multiple_spacing
) {
910 for (width
= spaces
<extra_spaces
? -1:0; width
< spaces_per_word
; width
++)
911 scratch_buffer
[k
++] = ' ';
914 multiple_spacing
= true;
917 scratch_buffer
[k
++] = c
;
918 multiple_spacing
= false;
924 scratch_buffer
[k
] = 0;
925 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
926 prefs
.encoding
, k
-col
);
930 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
932 if (line_width
> col
) {
933 str
= oldstr
= line_begin
;
936 while( (width
<draw_columns
) && (oldstr
<line_end
) )
938 oldstr
= get_ucs(oldstr
, &ch
);
940 k
-= glyph_width(ch
);
943 width
+= glyph_width(ch
);
947 if(prefs
.view_mode
==WIDE
)
948 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
949 prefs
.encoding
, oldstr
-line_begin
);
951 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
952 prefs
.encoding
, line_end
-line_begin
);
956 if (col
!= -1 && line_width
> col
)
957 #ifdef HAVE_LCD_BITMAP
958 rb
->lcd_putsxy(left_col
, i
*pf
->height
, utf8_buffer
);
960 rb
->lcd_puts(left_col
, i
, utf8_buffer
);
962 if (line_width
> max_line_len
)
963 max_line_len
= line_width
;
966 next_line_ptr
= line_end
;
968 next_screen_ptr
= line_end
;
969 if (BUFFER_OOB(next_screen_ptr
))
970 next_screen_ptr
= NULL
;
972 #ifdef HAVE_LCD_BITMAP
973 next_screen_to_draw_ptr
= prefs
.page_mode
==OVERLAP
? line_begin
: next_screen_ptr
;
975 if (prefs
.need_scrollbar
)
978 next_screen_to_draw_ptr
= next_screen_ptr
;
985 static void viewer_top(void)
987 /* Read top of file into buffer
988 and point screen pointer to top */
992 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
993 fill_buffer(0, buffer
, buffer_size
);
996 screen_top_ptr
= buffer
;
999 static void viewer_bottom(void)
1001 /* Read bottom of file into buffer
1002 and point screen pointer to bottom */
1005 if (file_size
> buffer_size
) {
1006 /* Find last buffer in file, round up to next sector boundary */
1007 last_sectors
= file_size
- buffer_size
+ SMALL_BLOCK_SIZE
;
1008 last_sectors
/= SMALL_BLOCK_SIZE
;
1009 last_sectors
*= SMALL_BLOCK_SIZE
;
1015 if (file_pos
!= last_sectors
)
1017 file_pos
= last_sectors
;
1018 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1019 fill_buffer(last_sectors
, buffer
, buffer_size
);
1022 screen_top_ptr
= buffer_end
-1;
1025 #ifdef HAVE_LCD_BITMAP
1026 static void init_need_scrollbar(void) {
1027 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1028 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1030 prefs
.need_scrollbar
= NEED_SCROLLBAR();
1031 draw_columns
= prefs
.need_scrollbar
? display_columns
-SCROLLBAR_WIDTH
: display_columns
;
1032 par_indent_spaces
= draw_columns
/(5*glyph_width(' '));
1035 #define init_need_scrollbar()
1038 static bool viewer_init(void)
1040 #ifdef HAVE_LCD_BITMAP
1042 pf
= rb
->font_get(FONT_UI
);
1044 display_lines
= LCD_HEIGHT
/ pf
->height
;
1045 draw_columns
= display_columns
= LCD_WIDTH
;
1047 /* REAL fixed pitch :) all chars use up 1 cell */
1049 draw_columns
= display_columns
= 11;
1050 par_indent_spaces
= 2;
1053 fd
= rb
->open(file_name
, O_RDONLY
);
1057 file_size
= rb
->filesize(fd
);
1061 /* Init mac_text value used in processing buffer */
1067 static void viewer_default_settings(void)
1069 prefs
.word_mode
= WRAP
;
1070 prefs
.line_mode
= NORMAL
;
1071 prefs
.view_mode
= NARROW
;
1072 prefs
.scroll_mode
= PAGE
;
1073 #ifdef HAVE_LCD_BITMAP
1074 prefs
.page_mode
= NO_OVERLAP
;
1075 prefs
.scrollbar_mode
= SB_OFF
;
1077 prefs
.autoscroll_speed
= 1;
1078 /* Set codepage to system default */
1079 prefs
.encoding
= rb
->global_settings
->default_codepage
;
1082 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1085 struct bookmark_file_data
*data
;
1086 struct bookmarked_file_info this_bookmark
;
1088 /* read settings file */
1089 settings_fd
=rb
->open(SETTINGS_FILE
, O_RDONLY
);
1090 if ((settings_fd
>= 0) && (rb
->filesize(settings_fd
) == sizeof(struct preferences
)))
1092 rb
->read(settings_fd
, &prefs
, sizeof(struct preferences
));
1093 rb
->close(settings_fd
);
1097 /* load default settings if there is no settings file */
1098 viewer_default_settings();
1101 rb
->memcpy(&old_prefs
, &prefs
, sizeof(struct preferences
));
1103 data
= (struct bookmark_file_data
*)buffer
; /* grab the text buffer */
1104 data
->bookmarked_files_count
= 0;
1106 /* read bookmarks if file exists */
1107 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_RDONLY
);
1108 if (settings_fd
>= 0)
1110 /* figure out how many items to read */
1111 rb
->read(settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1112 if (data
->bookmarked_files_count
> MAX_BOOKMARKED_FILES
)
1113 data
->bookmarked_files_count
= MAX_BOOKMARKED_FILES
;
1114 rb
->read(settings_fd
, data
->bookmarks
,
1115 sizeof(struct bookmarked_file_info
) * data
->bookmarked_files_count
);
1116 rb
->close(settings_fd
);
1120 screen_top_ptr
= buffer
;
1122 /* check if current file is in list */
1123 for (i
=0; i
< data
->bookmarked_files_count
; i
++)
1125 if (!rb
->strcmp(file_name
, data
->bookmarks
[i
].filename
))
1127 int screen_pos
= data
->bookmarks
[i
].file_position
+ data
->bookmarks
[i
].top_ptr_pos
;
1128 int screen_top
= screen_pos
% buffer_size
;
1129 file_pos
= screen_pos
- screen_top
;
1130 screen_top_ptr
= buffer
+ screen_top
;
1135 this_bookmark
.file_position
= file_pos
;
1136 this_bookmark
.top_ptr_pos
= screen_top_ptr
- buffer
;
1138 rb
->memset(&this_bookmark
.filename
[0],0,MAX_PATH
);
1139 rb
->strcpy(this_bookmark
.filename
,file_name
);
1141 /* prevent potential slot overflow */
1142 if (i
>= data
->bookmarked_files_count
)
1144 if (i
< MAX_BOOKMARKED_FILES
)
1145 data
->bookmarked_files_count
++;
1147 i
= MAX_BOOKMARKED_FILES
-1;
1150 /* write bookmark file with spare slot in first position
1151 to be filled in by viewer_save_settings */
1152 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_WRONLY
|O_CREAT
);
1153 if (settings_fd
>=0 )
1156 rb
->write (settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1158 /* write the current bookmark */
1159 rb
->write (settings_fd
, &this_bookmark
, sizeof(struct bookmarked_file_info
));
1161 /* write everything that was before this bookmark */
1162 rb
->write (settings_fd
, data
->bookmarks
, sizeof(struct bookmarked_file_info
)*i
);
1164 rb
->close(settings_fd
);
1167 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1169 if (BUFFER_OOB(screen_top_ptr
))
1171 screen_top_ptr
= buffer
;
1174 fill_buffer(file_pos
, buffer
, buffer_size
);
1176 /* remember the current position */
1177 start_position
= file_pos
+ screen_top_ptr
- buffer
;
1179 init_need_scrollbar();
1182 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1186 /* save the viewer settings if they have been changed */
1187 if (rb
->memcmp(&prefs
, &old_prefs
, sizeof(struct preferences
)))
1189 settings_fd
= rb
->creat(SETTINGS_FILE
); /* create the settings file */
1191 if (settings_fd
>= 0 )
1193 rb
->write (settings_fd
, &prefs
, sizeof(struct preferences
));
1194 rb
->close(settings_fd
);
1198 /* save the bookmark if the position has changed */
1199 if (file_pos
+ screen_top_ptr
- buffer
!= start_position
)
1201 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_WRONLY
|O_CREAT
);
1203 if (settings_fd
>= 0 )
1205 struct bookmarked_file_info b
;
1206 b
.file_position
= file_pos
+ screen_top_ptr
- buffer
;
1207 b
.top_ptr_pos
= 0; /* this is only kept for legassy reasons */
1208 rb
->memset(&b
.filename
[0],0,MAX_PATH
);
1209 rb
->strcpy(b
.filename
,file_name
);
1210 rb
->PREFIX(lseek
)(settings_fd
,sizeof(signed int),SEEK_SET
);
1211 rb
->write (settings_fd
, &b
, sizeof(struct bookmarked_file_info
));
1212 rb
->close(settings_fd
);
1217 static void viewer_exit(void *parameter
)
1221 viewer_save_settings();
1225 static int col_limit(int col
)
1230 if (col
> max_line_len
- 2*glyph_width('o'))
1231 col
= max_line_len
- 2*glyph_width('o');
1236 /* settings helper functions */
1238 static bool encoding_setting(void)
1240 static const struct opt_items names
[] = {
1245 {"ISO-8859-11", -1},
1257 return rb
->set_option("Encoding", &prefs
.encoding
, INT
, names
,
1258 sizeof(names
) / sizeof(names
[0]), NULL
);
1261 static bool word_wrap_setting(void)
1263 static const struct opt_items names
[] = {
1265 {"Off (Chop Words)", -1},
1268 return rb
->set_option("Word Wrap", &prefs
.word_mode
, INT
,
1272 static bool line_mode_setting(void)
1274 static const struct opt_items names
[] = {
1277 {"Expand Lines", -1},
1278 #ifdef HAVE_LCD_BITMAP
1279 {"Reflow Lines", -1},
1283 return rb
->set_option("Line Mode", &prefs
.line_mode
, INT
, names
,
1284 sizeof(names
) / sizeof(names
[0]), NULL
);
1287 static bool view_mode_setting(void)
1289 static const struct opt_items names
[] = {
1290 {"No (Narrow)", -1},
1294 ret
= rb
->set_option("Wide View", &prefs
.view_mode
, INT
,
1296 if (prefs
.view_mode
== NARROW
)
1301 static bool scroll_mode_setting(void)
1303 static const struct opt_items names
[] = {
1304 {"Scroll by Page", -1},
1305 {"Scroll by Line", -1},
1308 return rb
->set_option("Scroll Mode", &prefs
.scroll_mode
, INT
,
1312 #ifdef HAVE_LCD_BITMAP
1313 static bool page_mode_setting(void)
1315 static const struct opt_items names
[] = {
1320 return rb
->set_option("Overlap Pages", &prefs
.page_mode
, INT
,
1324 static bool scrollbar_setting(void)
1326 static const struct opt_items names
[] = {
1331 return rb
->set_option("Show Scrollbar", &prefs
.scrollbar_mode
, INT
,
1336 static bool autoscroll_speed_setting(void)
1338 return rb
->set_int("Auto-scroll Speed", "", UNIT_INT
,
1339 &prefs
.autoscroll_speed
, NULL
, 1, 1, 10, NULL
);
1342 static bool viewer_options_menu(void)
1347 static const struct menu_item items
[] = {
1348 {"Encoding", encoding_setting
},
1349 {"Word Wrap", word_wrap_setting
},
1350 {"Line Mode", line_mode_setting
},
1351 {"Wide View", view_mode_setting
},
1352 #ifdef HAVE_LCD_BITMAP
1353 {"Show Scrollbar", scrollbar_setting
},
1354 {"Overlap Pages", page_mode_setting
},
1356 {"Scroll Mode", scroll_mode_setting
},
1357 {"Auto-Scroll Speed", autoscroll_speed_setting
},
1359 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
),
1360 NULL
, NULL
, NULL
, NULL
);
1362 result
= menu_run(m
);
1364 #ifdef HAVE_LCD_BITMAP
1365 rb
->lcd_setmargins(0,0);
1367 /* Show-scrollbar mode for current view-width mode */
1368 init_need_scrollbar();
1373 static void viewer_menu(void)
1377 static const struct menu_item items
[] = {
1379 {"Viewer Options", NULL
},
1380 {"Show Playback Menu", NULL
},
1384 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
), NULL
, NULL
, NULL
, NULL
);
1385 result
=menu_show(m
);
1393 case 1: /* change settings */
1394 done
= viewer_options_menu();
1396 case 2: /* playback control */
1397 playback_control(rb
);
1399 case 3: /* return */
1403 #ifdef HAVE_LCD_BITMAP
1404 rb
->lcd_setmargins(0,0);
1409 enum plugin_status
plugin_start(struct plugin_api
* api
, void* file
)
1412 int lastbutton
= BUTTON_NONE
;
1413 bool autoscroll
= false;
1417 old_tick
= *rb
->current_tick
;
1419 /* get the plugin buffer */
1420 buffer
= rb
->plugin_get_buffer((size_t *)&buffer_size
);
1423 return PLUGIN_ERROR
;
1428 rb
->splash(HZ
, "Error opening file.");
1429 return PLUGIN_ERROR
;
1432 viewer_load_settings(); /* load the preferences and bookmark */
1435 rb
->lcd_set_backdrop(NULL
);
1444 if(old_tick
<= *rb
->current_tick
- (110-prefs
.autoscroll_speed
*10))
1446 viewer_scroll_down();
1448 old_tick
= *rb
->current_tick
;
1452 button
= rb
->button_get_w_tmo(HZ
/10);
1458 case VIEWER_AUTOSCROLL
:
1459 #ifdef VIEWER_AUTOSCROLL_PRE
1460 if (lastbutton
!= VIEWER_AUTOSCROLL_PRE
)
1463 autoscroll
= !autoscroll
;
1466 case VIEWER_PAGE_UP
:
1467 case VIEWER_PAGE_UP
| BUTTON_REPEAT
:
1468 if (prefs
.scroll_mode
== PAGE
)
1471 #ifdef HAVE_LCD_BITMAP
1472 for (i
= prefs
.page_mode
==OVERLAP
? 1:0; i
< display_lines
; i
++)
1474 for (i
= 0; i
< display_lines
; i
++)
1480 old_tick
= *rb
->current_tick
;
1484 case VIEWER_PAGE_DOWN
:
1485 case VIEWER_PAGE_DOWN
| BUTTON_REPEAT
:
1486 if (prefs
.scroll_mode
== PAGE
)
1489 if (next_screen_ptr
!= NULL
)
1490 screen_top_ptr
= next_screen_to_draw_ptr
;
1493 viewer_scroll_down();
1494 old_tick
= *rb
->current_tick
;
1498 case VIEWER_SCREEN_LEFT
:
1499 case VIEWER_SCREEN_LEFT
| BUTTON_REPEAT
:
1500 if (prefs
.view_mode
== WIDE
) {
1502 col
-= draw_columns
;
1503 col
= col_limit(col
);
1505 else { /* prefs.view_mode == NARROW */
1513 case VIEWER_SCREEN_RIGHT
:
1514 case VIEWER_SCREEN_RIGHT
| BUTTON_REPEAT
:
1515 if (prefs
.view_mode
== WIDE
) {
1517 col
+= draw_columns
;
1518 col
= col_limit(col
);
1520 else { /* prefs.view_mode == NARROW */
1521 /* Bottom of file */
1528 #ifdef VIEWER_LINE_UP
1529 case VIEWER_LINE_UP
:
1530 case VIEWER_LINE_UP
| BUTTON_REPEAT
:
1531 /* Scroll up one line */
1533 old_tick
= *rb
->current_tick
;
1537 case VIEWER_LINE_DOWN
:
1538 case VIEWER_LINE_DOWN
| BUTTON_REPEAT
:
1539 /* Scroll down one line */
1540 if (next_screen_ptr
!= NULL
)
1541 screen_top_ptr
= next_line_ptr
;
1542 old_tick
= *rb
->current_tick
;
1546 #ifdef VIEWER_COLUMN_LEFT
1547 case VIEWER_COLUMN_LEFT
:
1548 case VIEWER_COLUMN_LEFT
| BUTTON_REPEAT
:
1549 if (prefs
.view_mode
== WIDE
) {
1550 /* Scroll left one column */
1551 col
-= glyph_width('o');
1552 col
= col_limit(col
);
1557 case VIEWER_COLUMN_RIGHT
:
1558 case VIEWER_COLUMN_RIGHT
| BUTTON_REPEAT
:
1559 if (prefs
.view_mode
== WIDE
) {
1560 /* Scroll right one column */
1561 col
+= glyph_width('o');
1562 col
= col_limit(col
);
1568 #ifdef VIEWER_RC_QUIT
1569 case VIEWER_RC_QUIT
:
1577 if (rb
->default_event_handler_ex(button
, viewer_exit
, NULL
)
1578 == SYS_USB_CONNECTED
)
1579 return PLUGIN_USB_CONNECTED
;
1582 if (button
!= BUTTON_NONE
)
1584 lastbutton
= button
;