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
226 #elif CONFIG_KEYPAD == MROBE100_PAD
227 #define VIEWER_QUIT BUTTON_POWER
228 #define VIEWER_PAGE_UP BUTTON_UP
229 #define VIEWER_PAGE_DOWN BUTTON_DOWN
230 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
231 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
232 #define VIEWER_MENU BUTTON_MENU
233 #define VIEWER_AUTOSCROLL BUTTON_DISPLAY
236 #error No keymap defined!
239 /* stuff for the bookmarking */
240 struct bookmarked_file_info
{
243 char filename
[MAX_PATH
];
246 struct bookmark_file_data
{
247 signed int bookmarked_files_count
;
248 struct bookmarked_file_info bookmarks
[];
261 REFLOW
, /* won't be set on charcell LCD, must be last */
285 } encoding
; /* FIXME: What should default encoding be? */
287 #ifdef HAVE_LCD_BITMAP
298 #endif /* HAVE_LCD_BITMAP */
305 int autoscroll_speed
;
309 struct preferences prefs
;
310 struct preferences old_prefs
;
312 static unsigned char *buffer
;
313 static long buffer_size
;
314 static unsigned char line_break
[] = {0,0x20,9,0xB,0xC,'-'};
315 static int display_columns
; /* number of (pixel) columns on the display */
316 static int display_lines
; /* number of lines on the display */
317 static int draw_columns
; /* number of (pixel) columns available for text */
318 static int par_indent_spaces
; /* number of spaces to indent first paragraph */
320 static char *file_name
;
321 static long file_size
;
322 static long start_position
; /* position in the file after the viewer is started */
323 static bool mac_text
;
324 static long file_pos
; /* Position of the top of the buffer in the file */
325 static unsigned char *buffer_end
; /*Set to BUFFER_END() when file_pos changes*/
326 static int max_line_len
;
327 static unsigned char *screen_top_ptr
;
328 static unsigned char *next_screen_ptr
;
329 static unsigned char *next_screen_to_draw_ptr
;
330 static unsigned char *next_line_ptr
;
331 static struct plugin_api
* rb
;
332 #ifdef HAVE_LCD_BITMAP
333 static struct font
*pf
;
337 int glyph_width(int ch
)
342 #ifdef HAVE_LCD_BITMAP
343 return rb
->font_get_width(pf
, ch
);
349 unsigned char* get_ucs(const unsigned char* str
, unsigned short* ch
)
351 unsigned char utf8_tmp
[6];
354 if (prefs
.encoding
== UTF8
)
355 return (unsigned char*)rb
->utf8decode(str
, ch
);
357 count
= BUFFER_OOB(str
+2)? 1:2;
358 rb
->iso_decode(str
, utf8_tmp
, prefs
.encoding
, count
);
359 rb
->utf8decode(utf8_tmp
, ch
);
361 if ((prefs
.encoding
== SJIS
&& *str
> 0xA0 && *str
< 0xE0) || prefs
.encoding
< SJIS
)
362 return (unsigned char*)str
+1;
364 return (unsigned char*)str
+2;
370 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
371 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
372 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
373 static unsigned char* crop_at_width(const unsigned char* p
)
377 const unsigned char *oldp
= p
;
381 while (LINE_IS_NOT_FULL
) {
384 ADVANCE_COUNTERS(ch
);
387 return (unsigned char*)oldp
;
390 static unsigned char* find_first_feed(const unsigned char* p
, int size
)
394 for (i
=0; i
< size
; i
++)
396 return (unsigned char*) p
+i
;
401 static unsigned char* find_last_feed(const unsigned char* p
, int size
)
405 for (i
=size
-1; i
>=0; i
--)
407 return (unsigned char*) p
+i
;
412 static unsigned char* find_last_space(const unsigned char* p
, int size
)
416 k
= (prefs
.line_mode
==JOIN
) || (prefs
.line_mode
==REFLOW
) ? 0:1;
418 if (!BUFFER_OOB(&p
[size
]))
419 for (j
=k
; j
< ((int) sizeof(line_break
)) - 1; j
++)
420 if (p
[size
] == line_break
[j
])
421 return (unsigned char*) p
+size
;
423 for (i
=size
-1; i
>=0; i
--)
424 for (j
=k
; j
< (int) sizeof(line_break
); j
++)
426 if (!((p
[i
] == '-') && (prefs
.word_mode
== WRAP
)))
427 if (p
[i
] == line_break
[j
])
428 return (unsigned char*) p
+i
;
434 static unsigned char* find_next_line(const unsigned char* cur_line
, bool *is_short
)
436 const unsigned char *next_line
= NULL
;
437 int size
, i
, j
, k
, width
, search_len
, spaces
, newlines
;
441 if (is_short
!= NULL
)
444 if BUFFER_OOB(cur_line
)
447 if (prefs
.view_mode
== WIDE
) {
448 search_len
= MAX_WIDTH
;
450 else { /* prefs.view_mode == NARROW */
451 search_len
= crop_at_width(cur_line
) - cur_line
;
454 size
= BUFFER_OOB(cur_line
+search_len
) ? buffer_end
-cur_line
: search_len
;
456 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
)) {
457 /* Need to scan ahead and possibly increase search_len and size,
458 or possibly set next_line at second hard return in a row. */
461 for (j
=k
=width
=spaces
=newlines
=0; ; j
++) {
462 if (BUFFER_OOB(cur_line
+j
))
465 size
= search_len
= j
;
472 if (prefs
.line_mode
== REFLOW
) {
475 next_line
= cur_line
+ size
;
476 return (unsigned char*) next_line
;
478 if (j
==0) /* i=1 is intentional */
479 for (i
=0; i
<par_indent_spaces
; i
++)
480 ADVANCE_COUNTERS(' ');
482 if (!first_chars
) spaces
++;
488 next_line
= cur_line
+ size
- spaces
;
489 if (next_line
!= cur_line
)
490 return (unsigned char*) next_line
;
496 if (BUFFER_OOB(cur_line
+size
) || size
> 2*search_len
)
499 spaces
= first_chars
? 0:1;
503 if (prefs
.line_mode
==JOIN
|| newlines
>0) {
506 ADVANCE_COUNTERS(' ');
508 size
= search_len
= j
;
514 /* REFLOW, multiple spaces between words: count only
515 * one. If more are needed, they will be added
519 ADVANCE_COUNTERS(' ');
521 size
= search_len
= j
;
532 /* find first hard return */
533 next_line
= find_first_feed(cur_line
, size
);
536 if (next_line
== NULL
)
537 if (size
== search_len
) {
538 if (prefs
.word_mode
== WRAP
) /* Find last space */
539 next_line
= find_last_space(cur_line
, size
);
541 if (next_line
== NULL
)
542 next_line
= crop_at_width(cur_line
);
544 if (prefs
.word_mode
== WRAP
)
546 i
<WRAP_TRIM
&& isspace(next_line
[0]) && !BUFFER_OOB(next_line
);
551 if (prefs
.line_mode
== EXPAND
)
552 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
553 if (next_line
[0] == 0)
554 if (next_line
!= cur_line
)
555 return (unsigned char*) next_line
;
557 /* If next_line is pointing to a zero, increment it; i.e.,
558 leave the terminator at the end of cur_line. If pointing
559 to a hyphen, increment only if there is room to display
560 the hyphen on current line (won't apply in WIDE mode,
561 since it's guarenteed there won't be room). */
562 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
563 if (next_line
[0] == 0)/* ||
564 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
567 if (BUFFER_OOB(next_line
))
573 return (unsigned char*) next_line
;
576 static unsigned char* find_prev_line(const unsigned char* cur_line
)
578 const unsigned char *prev_line
= NULL
;
579 const unsigned char *p
;
581 if BUFFER_OOB(cur_line
)
584 /* To wrap consistently at the same places, we must
585 start with a known hard return, then work downwards.
586 We can either search backwards for a hard return,
587 or simply start wrapping downwards from top of buffer.
588 If current line is not near top of buffer, this is
589 a file with long lines (paragraphs). We would need to
590 read earlier sectors before we could decide how to
591 properly wrap the lines above the current line, but
592 it probably is not worth the disk access. Instead,
593 start with top of buffer and wrap down from there.
594 This may result in some lines wrapping at different
595 points from where they wrap when scrolling down.
596 If buffer is at top of file, start at top of buffer. */
598 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
))
599 prev_line
= p
= NULL
;
601 prev_line
= p
= find_last_feed(buffer
, cur_line
-buffer
-1);
602 /* Null means no line feeds in buffer above current line. */
604 if (prev_line
== NULL
)
605 if (BUFFER_BOF() || cur_line
- buffer
> READ_PREV_ZONE
)
606 prev_line
= p
= buffer
;
607 /* (else return NULL and read previous block) */
609 /* Wrap downwards until too far, then use the one before. */
610 while (p
< cur_line
&& p
!= NULL
) {
612 p
= find_next_line(prev_line
, NULL
);
615 if (BUFFER_OOB(prev_line
))
618 return (unsigned char*) prev_line
;
621 static void fill_buffer(long pos
, unsigned char* buf
, unsigned size
)
623 /* Read from file and preprocess the data */
624 /* To minimize disk access, always read on sector boundaries */
626 bool found_CR
= false;
628 rb
->lseek(fd
, pos
, SEEK_SET
);
629 numread
= rb
->read(fd
, buf
, size
);
630 rb
->button_clear_queue(); /* clear button queue */
632 for(i
= 0; i
< numread
; i
++) {
649 case 0: /* No break between case 0 and default, intentionally */
662 static int read_and_synch(int direction
)
664 /* Read next (or prev) block, and reposition global pointers. */
665 /* direction: 1 for down (i.e., further into file), -1 for up */
666 int move_size
, move_vector
, offset
;
667 unsigned char *fill_buf
;
669 if (direction
== -1) /* up */ {
670 move_size
= SMALL_BLOCK_SIZE
;
672 fill_buf
= TOP_SECTOR
;
673 rb
->memcpy(BOTTOM_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
674 rb
->memcpy(MID_SECTOR
, TOP_SECTOR
, SMALL_BLOCK_SIZE
);
677 if (prefs
.view_mode
== WIDE
) {
678 /* WIDE mode needs more buffer so we have to read smaller blocks */
679 move_size
= SMALL_BLOCK_SIZE
;
680 offset
= LARGE_BLOCK_SIZE
;
681 fill_buf
= BOTTOM_SECTOR
;
682 rb
->memcpy(TOP_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
683 rb
->memcpy(MID_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
686 move_size
= LARGE_BLOCK_SIZE
;
687 offset
= SMALL_BLOCK_SIZE
;
688 fill_buf
= MID_SECTOR
;
689 rb
->memcpy(TOP_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
692 move_vector
= direction
* move_size
;
693 screen_top_ptr
-= move_vector
;
694 file_pos
+= move_vector
;
695 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
696 fill_buffer(file_pos
+ offset
, fill_buf
, move_size
);
700 static void viewer_scroll_up(void)
704 p
= find_prev_line(screen_top_ptr
);
705 if (p
== NULL
&& !BUFFER_BOF()) {
707 p
= find_prev_line(screen_top_ptr
);
713 static void viewer_scroll_down(void)
715 if (next_screen_ptr
!= NULL
)
716 screen_top_ptr
= next_line_ptr
;
719 #ifdef HAVE_LCD_BITMAP
720 static void viewer_scrollbar(void) {
721 int items
, min_shown
, max_shown
;
723 items
= (int) file_size
; /* (SH1 int is same as long) */
724 min_shown
= (int) file_pos
+ (screen_top_ptr
- buffer
);
726 if (next_screen_ptr
== NULL
)
729 max_shown
= min_shown
+ (next_screen_ptr
- screen_top_ptr
);
731 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, 0, SCROLLBAR_WIDTH
-1,
732 LCD_HEIGHT
, items
, min_shown
, max_shown
, VERTICAL
);
736 static void viewer_draw(int col
)
738 int i
, j
, k
, line_len
, line_width
, resynch_move
, spaces
, left_col
=0;
739 int width
, extra_spaces
, indent_spaces
, spaces_per_word
;
740 bool multiple_spacing
, line_is_short
;
742 unsigned char *str
, *oldstr
;
743 unsigned char *line_begin
;
744 unsigned char *line_end
;
746 unsigned char scratch_buffer
[MAX_COLUMNS
+ 1];
747 unsigned char utf8_buffer
[MAX_COLUMNS
*4 + 1];
748 unsigned char *endptr
;
750 /* If col==-1 do all calculations but don't display */
752 #ifdef HAVE_LCD_BITMAP
753 left_col
= prefs
.need_scrollbar
? SCROLLBAR_WIDTH
:0;
757 rb
->lcd_clear_display();
760 line_begin
= line_end
= screen_top_ptr
;
762 for (i
= 0; i
< display_lines
; i
++) {
763 if (BUFFER_OOB(line_end
))
764 break; /* Happens after display last line at BUFFER_EOF() */
766 line_begin
= line_end
;
767 line_end
= find_next_line(line_begin
, &line_is_short
);
769 if (line_end
== NULL
) {
771 if (i
< display_lines
- 1 && !BUFFER_BOF()) {
773 rb
->lcd_clear_display();
775 for (; i
< display_lines
- 1; i
++)
778 line_begin
= line_end
= screen_top_ptr
;
783 line_end
= buffer_end
;
787 resynch_move
= read_and_synch(1); /* Read block & move ptrs */
788 line_begin
-= resynch_move
;
790 next_line_ptr
-= resynch_move
;
792 line_end
= find_next_line(line_begin
, NULL
);
793 if (line_end
== NULL
) /* Should not really happen */
797 line_len
= line_end
- line_begin
;
799 /* calculate line_len */
800 str
= oldstr
= line_begin
;
802 while (str
< line_end
) {
804 str
= crop_at_width(str
);
807 line_width
= j
*draw_columns
;
808 while (oldstr
< line_end
) {
809 oldstr
= get_ucs(oldstr
, &ch
);
810 line_width
+= glyph_width(ch
);
813 if (prefs
.line_mode
== JOIN
) {
814 if (line_begin
[0] == 0) {
816 if (prefs
.word_mode
== CHOP
)
821 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
822 if (k
== MAX_COLUMNS
)
832 scratch_buffer
[k
++] = ' ';
837 scratch_buffer
[k
++] = ' ';
838 if (k
== MAX_COLUMNS
- 1)
841 scratch_buffer
[k
++] = c
;
846 scratch_buffer
[k
] = 0;
847 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
848 prefs
.encoding
, draw_columns
/glyph_width('i'));
852 else if (prefs
.line_mode
== REFLOW
) {
853 if (line_begin
[0] == 0) {
855 if (prefs
.word_mode
== CHOP
)
862 if (!line_is_short
) {
863 multiple_spacing
= false;
865 for (str
= line_begin
; str
< line_end
; ) {
866 str
= get_ucs(str
, &ch
);
870 if ((str
== line_begin
) && (prefs
.word_mode
==WRAP
))
871 /* special case: indent the paragraph,
872 * don't count spaces */
873 indent_spaces
= par_indent_spaces
;
874 else if (!multiple_spacing
)
876 multiple_spacing
= true;
879 multiple_spacing
= false;
880 width
+= glyph_width(ch
);
884 if (multiple_spacing
) spaces
--;
887 /* total number of spaces to insert between words */
888 extra_spaces
= (draw_columns
-width
)/glyph_width(' ')
890 /* number of spaces between each word*/
891 spaces_per_word
= extra_spaces
/ spaces
;
892 /* number of words with n+1 spaces (to fill up) */
893 extra_spaces
= extra_spaces
% spaces
;
894 if (spaces_per_word
> 2) { /* too much spacing is awful */
898 } else { /* this doesn't matter much... no spaces anyway */
899 spaces_per_word
= extra_spaces
= 0;
901 } else { /* end of a paragraph: don't fill line */
906 multiple_spacing
= false;
907 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
908 if (k
== MAX_COLUMNS
)
915 if (j
==0 && prefs
.word_mode
==WRAP
) { /* indent paragraph */
916 for (j
=0; j
<par_indent_spaces
; j
++)
917 scratch_buffer
[k
++] = ' ';
920 else if (!multiple_spacing
) {
921 for (width
= spaces
<extra_spaces
? -1:0; width
< spaces_per_word
; width
++)
922 scratch_buffer
[k
++] = ' ';
925 multiple_spacing
= true;
928 scratch_buffer
[k
++] = c
;
929 multiple_spacing
= false;
935 scratch_buffer
[k
] = 0;
936 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
937 prefs
.encoding
, k
-col
);
941 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
943 if (line_width
> col
) {
944 str
= oldstr
= line_begin
;
947 while( (width
<draw_columns
) && (oldstr
<line_end
) )
949 oldstr
= get_ucs(oldstr
, &ch
);
951 k
-= glyph_width(ch
);
954 width
+= glyph_width(ch
);
958 if(prefs
.view_mode
==WIDE
)
959 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
960 prefs
.encoding
, oldstr
-line_begin
);
962 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
963 prefs
.encoding
, line_end
-line_begin
);
967 if (col
!= -1 && line_width
> col
)
968 #ifdef HAVE_LCD_BITMAP
969 rb
->lcd_putsxy(left_col
, i
*pf
->height
, utf8_buffer
);
971 rb
->lcd_puts(left_col
, i
, utf8_buffer
);
973 if (line_width
> max_line_len
)
974 max_line_len
= line_width
;
977 next_line_ptr
= line_end
;
979 next_screen_ptr
= line_end
;
980 if (BUFFER_OOB(next_screen_ptr
))
981 next_screen_ptr
= NULL
;
983 #ifdef HAVE_LCD_BITMAP
984 next_screen_to_draw_ptr
= prefs
.page_mode
==OVERLAP
? line_begin
: next_screen_ptr
;
986 if (prefs
.need_scrollbar
)
989 next_screen_to_draw_ptr
= next_screen_ptr
;
996 static void viewer_top(void)
998 /* Read top of file into buffer
999 and point screen pointer to top */
1003 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1004 fill_buffer(0, buffer
, buffer_size
);
1007 screen_top_ptr
= buffer
;
1010 static void viewer_bottom(void)
1012 /* Read bottom of file into buffer
1013 and point screen pointer to bottom */
1016 if (file_size
> buffer_size
) {
1017 /* Find last buffer in file, round up to next sector boundary */
1018 last_sectors
= file_size
- buffer_size
+ SMALL_BLOCK_SIZE
;
1019 last_sectors
/= SMALL_BLOCK_SIZE
;
1020 last_sectors
*= SMALL_BLOCK_SIZE
;
1026 if (file_pos
!= last_sectors
)
1028 file_pos
= last_sectors
;
1029 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1030 fill_buffer(last_sectors
, buffer
, buffer_size
);
1033 screen_top_ptr
= buffer_end
-1;
1036 #ifdef HAVE_LCD_BITMAP
1037 static void init_need_scrollbar(void) {
1038 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1039 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1041 prefs
.need_scrollbar
= NEED_SCROLLBAR();
1042 draw_columns
= prefs
.need_scrollbar
? display_columns
-SCROLLBAR_WIDTH
: display_columns
;
1043 par_indent_spaces
= draw_columns
/(5*glyph_width(' '));
1046 #define init_need_scrollbar()
1049 static bool viewer_init(void)
1051 #ifdef HAVE_LCD_BITMAP
1053 pf
= rb
->font_get(FONT_UI
);
1055 display_lines
= LCD_HEIGHT
/ pf
->height
;
1056 draw_columns
= display_columns
= LCD_WIDTH
;
1058 /* REAL fixed pitch :) all chars use up 1 cell */
1060 draw_columns
= display_columns
= 11;
1061 par_indent_spaces
= 2;
1064 fd
= rb
->open(file_name
, O_RDONLY
);
1068 file_size
= rb
->filesize(fd
);
1072 /* Init mac_text value used in processing buffer */
1078 static void viewer_default_settings(void)
1080 prefs
.word_mode
= WRAP
;
1081 prefs
.line_mode
= NORMAL
;
1082 prefs
.view_mode
= NARROW
;
1083 prefs
.scroll_mode
= PAGE
;
1084 #ifdef HAVE_LCD_BITMAP
1085 prefs
.page_mode
= NO_OVERLAP
;
1086 prefs
.scrollbar_mode
= SB_OFF
;
1088 prefs
.autoscroll_speed
= 1;
1089 /* Set codepage to system default */
1090 prefs
.encoding
= rb
->global_settings
->default_codepage
;
1093 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1096 struct bookmark_file_data
*data
;
1097 struct bookmarked_file_info this_bookmark
;
1099 /* read settings file */
1100 settings_fd
=rb
->open(SETTINGS_FILE
, O_RDONLY
);
1101 if ((settings_fd
>= 0) && (rb
->filesize(settings_fd
) == sizeof(struct preferences
)))
1103 rb
->read(settings_fd
, &prefs
, sizeof(struct preferences
));
1104 rb
->close(settings_fd
);
1108 /* load default settings if there is no settings file */
1109 viewer_default_settings();
1112 rb
->memcpy(&old_prefs
, &prefs
, sizeof(struct preferences
));
1114 data
= (struct bookmark_file_data
*)buffer
; /* grab the text buffer */
1115 data
->bookmarked_files_count
= 0;
1117 /* read bookmarks if file exists */
1118 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_RDONLY
);
1119 if (settings_fd
>= 0)
1121 /* figure out how many items to read */
1122 rb
->read(settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1123 if (data
->bookmarked_files_count
> MAX_BOOKMARKED_FILES
)
1124 data
->bookmarked_files_count
= MAX_BOOKMARKED_FILES
;
1125 rb
->read(settings_fd
, data
->bookmarks
,
1126 sizeof(struct bookmarked_file_info
) * data
->bookmarked_files_count
);
1127 rb
->close(settings_fd
);
1131 screen_top_ptr
= buffer
;
1133 /* check if current file is in list */
1134 for (i
=0; i
< data
->bookmarked_files_count
; i
++)
1136 if (!rb
->strcmp(file_name
, data
->bookmarks
[i
].filename
))
1138 int screen_pos
= data
->bookmarks
[i
].file_position
+ data
->bookmarks
[i
].top_ptr_pos
;
1139 int screen_top
= screen_pos
% buffer_size
;
1140 file_pos
= screen_pos
- screen_top
;
1141 screen_top_ptr
= buffer
+ screen_top
;
1146 this_bookmark
.file_position
= file_pos
;
1147 this_bookmark
.top_ptr_pos
= screen_top_ptr
- buffer
;
1149 rb
->memset(&this_bookmark
.filename
[0],0,MAX_PATH
);
1150 rb
->strcpy(this_bookmark
.filename
,file_name
);
1152 /* prevent potential slot overflow */
1153 if (i
>= data
->bookmarked_files_count
)
1155 if (i
< MAX_BOOKMARKED_FILES
)
1156 data
->bookmarked_files_count
++;
1158 i
= MAX_BOOKMARKED_FILES
-1;
1161 /* write bookmark file with spare slot in first position
1162 to be filled in by viewer_save_settings */
1163 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_WRONLY
|O_CREAT
);
1164 if (settings_fd
>=0 )
1167 rb
->write (settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1169 /* write the current bookmark */
1170 rb
->write (settings_fd
, &this_bookmark
, sizeof(struct bookmarked_file_info
));
1172 /* write everything that was before this bookmark */
1173 rb
->write (settings_fd
, data
->bookmarks
, sizeof(struct bookmarked_file_info
)*i
);
1175 rb
->close(settings_fd
);
1178 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1180 if (BUFFER_OOB(screen_top_ptr
))
1182 screen_top_ptr
= buffer
;
1185 fill_buffer(file_pos
, buffer
, buffer_size
);
1187 /* remember the current position */
1188 start_position
= file_pos
+ screen_top_ptr
- buffer
;
1190 init_need_scrollbar();
1193 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1197 /* save the viewer settings if they have been changed */
1198 if (rb
->memcmp(&prefs
, &old_prefs
, sizeof(struct preferences
)))
1200 settings_fd
= rb
->creat(SETTINGS_FILE
); /* create the settings file */
1202 if (settings_fd
>= 0 )
1204 rb
->write (settings_fd
, &prefs
, sizeof(struct preferences
));
1205 rb
->close(settings_fd
);
1209 /* save the bookmark if the position has changed */
1210 if (file_pos
+ screen_top_ptr
- buffer
!= start_position
)
1212 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_WRONLY
|O_CREAT
);
1214 if (settings_fd
>= 0 )
1216 struct bookmarked_file_info b
;
1217 b
.file_position
= file_pos
+ screen_top_ptr
- buffer
;
1218 b
.top_ptr_pos
= 0; /* this is only kept for legassy reasons */
1219 rb
->memset(&b
.filename
[0],0,MAX_PATH
);
1220 rb
->strcpy(b
.filename
,file_name
);
1221 rb
->PREFIX(lseek
)(settings_fd
,sizeof(signed int),SEEK_SET
);
1222 rb
->write (settings_fd
, &b
, sizeof(struct bookmarked_file_info
));
1223 rb
->close(settings_fd
);
1228 static void viewer_exit(void *parameter
)
1232 viewer_save_settings();
1236 static int col_limit(int col
)
1241 if (col
> max_line_len
- 2*glyph_width('o'))
1242 col
= max_line_len
- 2*glyph_width('o');
1247 /* settings helper functions */
1249 static bool encoding_setting(void)
1251 static const struct opt_items names
[] = {
1256 {"ISO-8859-11", -1},
1268 return rb
->set_option("Encoding", &prefs
.encoding
, INT
, names
,
1269 sizeof(names
) / sizeof(names
[0]), NULL
);
1272 static bool word_wrap_setting(void)
1274 static const struct opt_items names
[] = {
1276 {"Off (Chop Words)", -1},
1279 return rb
->set_option("Word Wrap", &prefs
.word_mode
, INT
,
1283 static bool line_mode_setting(void)
1285 static const struct opt_items names
[] = {
1288 {"Expand Lines", -1},
1289 #ifdef HAVE_LCD_BITMAP
1290 {"Reflow Lines", -1},
1294 return rb
->set_option("Line Mode", &prefs
.line_mode
, INT
, names
,
1295 sizeof(names
) / sizeof(names
[0]), NULL
);
1298 static bool view_mode_setting(void)
1300 static const struct opt_items names
[] = {
1301 {"No (Narrow)", -1},
1305 ret
= rb
->set_option("Wide View", &prefs
.view_mode
, INT
,
1307 if (prefs
.view_mode
== NARROW
)
1312 static bool scroll_mode_setting(void)
1314 static const struct opt_items names
[] = {
1315 {"Scroll by Page", -1},
1316 {"Scroll by Line", -1},
1319 return rb
->set_option("Scroll Mode", &prefs
.scroll_mode
, INT
,
1323 #ifdef HAVE_LCD_BITMAP
1324 static bool page_mode_setting(void)
1326 static const struct opt_items names
[] = {
1331 return rb
->set_option("Overlap Pages", &prefs
.page_mode
, INT
,
1335 static bool scrollbar_setting(void)
1337 static const struct opt_items names
[] = {
1342 return rb
->set_option("Show Scrollbar", &prefs
.scrollbar_mode
, INT
,
1347 static bool autoscroll_speed_setting(void)
1349 return rb
->set_int("Auto-scroll Speed", "", UNIT_INT
,
1350 &prefs
.autoscroll_speed
, NULL
, 1, 1, 10, NULL
);
1353 static bool viewer_options_menu(void)
1358 static const struct menu_item items
[] = {
1359 {"Encoding", encoding_setting
},
1360 {"Word Wrap", word_wrap_setting
},
1361 {"Line Mode", line_mode_setting
},
1362 {"Wide View", view_mode_setting
},
1363 #ifdef HAVE_LCD_BITMAP
1364 {"Show Scrollbar", scrollbar_setting
},
1365 {"Overlap Pages", page_mode_setting
},
1367 {"Scroll Mode", scroll_mode_setting
},
1368 {"Auto-Scroll Speed", autoscroll_speed_setting
},
1370 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
),
1371 NULL
, NULL
, NULL
, NULL
);
1373 result
= menu_run(m
);
1375 #ifdef HAVE_LCD_BITMAP
1376 rb
->lcd_setmargins(0,0);
1378 /* Show-scrollbar mode for current view-width mode */
1379 init_need_scrollbar();
1384 static void viewer_menu(void)
1388 static const struct menu_item items
[] = {
1390 {"Viewer Options", NULL
},
1391 {"Show Playback Menu", NULL
},
1395 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
), NULL
, NULL
, NULL
, NULL
);
1396 result
=menu_show(m
);
1404 case 1: /* change settings */
1405 done
= viewer_options_menu();
1407 case 2: /* playback control */
1408 playback_control(rb
);
1410 case 3: /* return */
1414 #ifdef HAVE_LCD_BITMAP
1415 rb
->lcd_setmargins(0,0);
1420 enum plugin_status
plugin_start(struct plugin_api
* api
, void* file
)
1423 int lastbutton
= BUTTON_NONE
;
1424 bool autoscroll
= false;
1428 old_tick
= *rb
->current_tick
;
1430 /* get the plugin buffer */
1431 buffer
= rb
->plugin_get_buffer((size_t *)&buffer_size
);
1434 return PLUGIN_ERROR
;
1439 rb
->splash(HZ
, "Error opening file.");
1440 return PLUGIN_ERROR
;
1443 viewer_load_settings(); /* load the preferences and bookmark */
1446 rb
->lcd_set_backdrop(NULL
);
1455 if(old_tick
<= *rb
->current_tick
- (110-prefs
.autoscroll_speed
*10))
1457 viewer_scroll_down();
1459 old_tick
= *rb
->current_tick
;
1463 button
= rb
->button_get_w_tmo(HZ
/10);
1469 case VIEWER_AUTOSCROLL
:
1470 #ifdef VIEWER_AUTOSCROLL_PRE
1471 if (lastbutton
!= VIEWER_AUTOSCROLL_PRE
)
1474 autoscroll
= !autoscroll
;
1477 case VIEWER_PAGE_UP
:
1478 case VIEWER_PAGE_UP
| BUTTON_REPEAT
:
1479 if (prefs
.scroll_mode
== PAGE
)
1482 #ifdef HAVE_LCD_BITMAP
1483 for (i
= prefs
.page_mode
==OVERLAP
? 1:0; i
< display_lines
; i
++)
1485 for (i
= 0; i
< display_lines
; i
++)
1491 old_tick
= *rb
->current_tick
;
1495 case VIEWER_PAGE_DOWN
:
1496 case VIEWER_PAGE_DOWN
| BUTTON_REPEAT
:
1497 if (prefs
.scroll_mode
== PAGE
)
1500 if (next_screen_ptr
!= NULL
)
1501 screen_top_ptr
= next_screen_to_draw_ptr
;
1504 viewer_scroll_down();
1505 old_tick
= *rb
->current_tick
;
1509 case VIEWER_SCREEN_LEFT
:
1510 case VIEWER_SCREEN_LEFT
| BUTTON_REPEAT
:
1511 if (prefs
.view_mode
== WIDE
) {
1513 col
-= draw_columns
;
1514 col
= col_limit(col
);
1516 else { /* prefs.view_mode == NARROW */
1524 case VIEWER_SCREEN_RIGHT
:
1525 case VIEWER_SCREEN_RIGHT
| BUTTON_REPEAT
:
1526 if (prefs
.view_mode
== WIDE
) {
1528 col
+= draw_columns
;
1529 col
= col_limit(col
);
1531 else { /* prefs.view_mode == NARROW */
1532 /* Bottom of file */
1539 #ifdef VIEWER_LINE_UP
1540 case VIEWER_LINE_UP
:
1541 case VIEWER_LINE_UP
| BUTTON_REPEAT
:
1542 /* Scroll up one line */
1544 old_tick
= *rb
->current_tick
;
1548 case VIEWER_LINE_DOWN
:
1549 case VIEWER_LINE_DOWN
| BUTTON_REPEAT
:
1550 /* Scroll down one line */
1551 if (next_screen_ptr
!= NULL
)
1552 screen_top_ptr
= next_line_ptr
;
1553 old_tick
= *rb
->current_tick
;
1557 #ifdef VIEWER_COLUMN_LEFT
1558 case VIEWER_COLUMN_LEFT
:
1559 case VIEWER_COLUMN_LEFT
| BUTTON_REPEAT
:
1560 if (prefs
.view_mode
== WIDE
) {
1561 /* Scroll left one column */
1562 col
-= glyph_width('o');
1563 col
= col_limit(col
);
1568 case VIEWER_COLUMN_RIGHT
:
1569 case VIEWER_COLUMN_RIGHT
| BUTTON_REPEAT
:
1570 if (prefs
.view_mode
== WIDE
) {
1571 /* Scroll right one column */
1572 col
+= glyph_width('o');
1573 col
= col_limit(col
);
1579 #ifdef VIEWER_RC_QUIT
1580 case VIEWER_RC_QUIT
:
1588 if (rb
->default_event_handler_ex(button
, viewer_exit
, NULL
)
1589 == SYS_USB_CONNECTED
)
1590 return PLUGIN_USB_CONNECTED
;
1593 if (button
!= BUTTON_NONE
)
1595 lastbutton
= button
;