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"
26 #define SETTINGS_FILE "/.rockbox/viewers/viewer.cfg"
28 #define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
29 #define MAX_COLUMNS 64 /* Max displayable string len (over-estimate) */
30 #define MAX_WIDTH 910 /* Max line length in WIDE mode */
31 #define READ_PREV_ZONE 910 /* Arbitrary number less than SMALL_BLOCK_SIZE */
32 #define SMALL_BLOCK_SIZE 0x1000 /* 4k: Smallest file chunk we will read */
33 #define LARGE_BLOCK_SIZE 0x2000 /* 8k: Preferable size of file chunk to read */
34 #define BUFFER_SIZE 0x3000 /* 12k: Mem reserved for buffered file data */
35 #define TOP_SECTOR buffer
36 #define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
37 #define BOTTOM_SECTOR (buffer + 2*(SMALL_BLOCK_SIZE))
39 /* Out-Of-Bounds test for any pointer to data in the buffer */
40 #define BUFFER_OOB(p) ((p) < buffer || (p) >= buffer_end)
42 /* Does the buffer contain the beginning of the file? */
43 #define BUFFER_BOF() (file_pos==0)
45 /* Does the buffer contain the end of the file? */
46 #define BUFFER_EOF() (file_size-file_pos <= BUFFER_SIZE)
48 /* Formula for the endpoint address outside of buffer data */
49 #define BUFFER_END() \
50 ((BUFFER_EOF()) ? (file_size-file_pos+buffer) : (buffer+BUFFER_SIZE))
52 /* Is the entire file being shown in one screen? */
53 #define ONE_SCREEN_FITS_ALL() \
54 (next_screen_ptr==NULL && screen_top_ptr==buffer && BUFFER_BOF())
56 /* Is a scrollbar called for on the current screen? */
57 #define NEED_SCROLLBAR() \
58 ((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
60 /* variable button definitions */
63 #if CONFIG_KEYPAD == RECORDER_PAD
64 #define VIEWER_QUIT BUTTON_OFF
65 #define VIEWER_PAGE_UP BUTTON_UP
66 #define VIEWER_PAGE_DOWN BUTTON_DOWN
67 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
68 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
69 #define VIEWER_MENU BUTTON_F1
70 #define VIEWER_AUTOSCROLL BUTTON_PLAY
71 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
72 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
73 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
74 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
77 #elif CONFIG_KEYPAD == ONDIO_PAD
78 #define VIEWER_QUIT BUTTON_OFF
79 #define VIEWER_PAGE_UP BUTTON_UP
80 #define VIEWER_PAGE_DOWN BUTTON_DOWN
81 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
82 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
83 #define VIEWER_MENU (BUTTON_MENU|BUTTON_REPEAT)
84 #define VIEWER_AUTOSCROLL_PRE BUTTON_MENU
85 #define VIEWER_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
88 #elif CONFIG_KEYPAD == PLAYER_PAD
89 #define VIEWER_QUIT BUTTON_STOP
90 #define VIEWER_PAGE_UP BUTTON_LEFT
91 #define VIEWER_PAGE_DOWN BUTTON_RIGHT
92 #define VIEWER_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
93 #define VIEWER_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
94 #define VIEWER_MENU BUTTON_MENU
95 #define VIEWER_AUTOSCROLL BUTTON_PLAY
97 /* iRiver H1x0 && H3x0 keys */
98 #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
99 (CONFIG_KEYPAD == IRIVER_H300_PAD)
100 #define VIEWER_QUIT BUTTON_OFF
101 #define VIEWER_PAGE_UP BUTTON_UP
102 #define VIEWER_PAGE_DOWN BUTTON_DOWN
103 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
104 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
105 #define VIEWER_MENU BUTTON_MODE
106 #define VIEWER_AUTOSCROLL BUTTON_SELECT
107 #define VIEWER_LINE_UP (BUTTON_ON | BUTTON_UP)
108 #define VIEWER_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
109 #define VIEWER_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
110 #define VIEWER_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
112 /* iPods with the 4G pad */
113 #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
114 (CONFIG_KEYPAD == IPOD_3G_PAD)
115 #define VIEWER_QUIT_PRE BUTTON_SELECT
116 #define VIEWER_QUIT (BUTTON_SELECT | BUTTON_MENU)
117 #define VIEWER_PAGE_UP BUTTON_SCROLL_BACK
118 #define VIEWER_PAGE_DOWN BUTTON_SCROLL_FWD
119 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
120 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
121 #define VIEWER_MENU BUTTON_MENU
122 #define VIEWER_AUTOSCROLL BUTTON_PLAY
125 #elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
126 #define VIEWER_QUIT BUTTON_PLAY
127 #define VIEWER_PAGE_UP BUTTON_UP
128 #define VIEWER_PAGE_DOWN BUTTON_DOWN
129 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
130 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
131 #define VIEWER_MENU BUTTON_MODE
132 #define VIEWER_AUTOSCROLL BUTTON_SELECT
135 #elif CONFIG_KEYPAD == IAUDIO_X5_PAD
136 #define VIEWER_QUIT BUTTON_POWER
137 #define VIEWER_PAGE_UP BUTTON_UP
138 #define VIEWER_PAGE_DOWN BUTTON_DOWN
139 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
140 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
141 #define VIEWER_MENU BUTTON_SELECT
142 #define VIEWER_AUTOSCROLL BUTTON_PLAY
145 #elif CONFIG_KEYPAD == GIGABEAT_PAD
146 #define VIEWER_QUIT BUTTON_POWER
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_MENU
152 #define VIEWER_AUTOSCROLL BUTTON_A
166 REFLOW
, /* won't be set on charcell LCD, must be last */
174 #ifdef HAVE_LCD_BITMAP
185 #endif /* HAVE_LCD_BITMAP */
192 int autoscroll_speed
;
195 static unsigned char buffer
[BUFFER_SIZE
+ 1];
196 static unsigned char line_break
[] = {0,0x20,'-',9,0xB,0xC};
197 static int display_columns
; /* number of (pixel) columns on the display */
198 static int display_lines
; /* number of lines on the display */
199 static int draw_columns
; /* number of (pixel) columns available for text */
200 static int par_indent_spaces
; /* number of spaces to indent first paragraph */
202 static char *file_name
;
203 static long file_size
;
204 static bool mac_text
;
205 static long file_pos
; /* Position of the top of the buffer in the file */
206 static unsigned char *buffer_end
; /*Set to BUFFER_END() when file_pos changes*/
207 static int max_line_len
;
208 static unsigned char *screen_top_ptr
;
209 static unsigned char *next_screen_ptr
;
210 static unsigned char *next_screen_to_draw_ptr
;
211 static unsigned char *next_line_ptr
;
212 static struct plugin_api
* rb
;
214 static unsigned char glyph_width
[256];
219 #define ADVANCE_COUNTERS(c) do { width += glyph_width[c]; k++; } while(0)
220 #define LINE_IS_FULL ((k>MAX_COLUMNS-1) || (width > draw_columns))
221 static unsigned char* crop_at_width(const unsigned char* p
)
226 while (!LINE_IS_FULL
)
227 ADVANCE_COUNTERS(p
[k
]);
229 return (unsigned char*) p
+k
-1;
232 static unsigned char* find_first_feed(const unsigned char* p
, int size
)
236 for (i
=0; i
< size
; i
++)
238 return (unsigned char*) p
+i
;
243 static unsigned char* find_last_feed(const unsigned char* p
, int size
)
247 for (i
=size
-1; i
>=0; i
--)
249 return (unsigned char*) p
+i
;
254 static unsigned char* find_last_space(const unsigned char* p
, int size
)
258 k
= (prefs
.line_mode
==JOIN
) || (prefs
.line_mode
==REFLOW
) ? 0:1;
260 for (i
=size
-1; i
>=0; i
--)
261 for (j
=k
; j
< (int) sizeof(line_break
); j
++)
262 if (p
[i
] == line_break
[j
])
263 return (unsigned char*) p
+i
;
268 static unsigned char* find_next_line(const unsigned char* cur_line
, bool *is_short
)
270 const unsigned char *next_line
= NULL
;
271 int size
, i
, j
, k
, width
, search_len
, spaces
, newlines
;
275 if (is_short
!= NULL
)
278 if BUFFER_OOB(cur_line
)
281 if (prefs
.view_mode
== WIDE
) {
282 search_len
= MAX_WIDTH
;
284 else { /* prefs.view_mode == NARROW */
285 search_len
= crop_at_width(cur_line
) - cur_line
;
288 size
= BUFFER_OOB(cur_line
+search_len
) ? buffer_end
-cur_line
: search_len
;
290 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
)) {
291 /* Need to scan ahead and possibly increase search_len and size,
292 or possibly set next_line at second hard return in a row. */
295 for (j
=k
=width
=spaces
=newlines
=0; ; j
++) {
296 if (BUFFER_OOB(cur_line
+j
))
299 size
= search_len
= j
;
306 if (prefs
.line_mode
== REFLOW
) {
309 next_line
= cur_line
+ size
;
310 return (unsigned char*) next_line
;
312 if (j
==0) /* i=1 is intentional */
313 for (i
=0; i
<par_indent_spaces
; i
++)
314 ADVANCE_COUNTERS(' ');
316 if (!first_chars
) spaces
++;
322 next_line
= cur_line
+ size
- spaces
;
323 if (next_line
!= cur_line
)
324 return (unsigned char*) next_line
;
330 if (BUFFER_OOB(cur_line
+size
) || size
> 2*search_len
)
333 spaces
= first_chars
? 0:1;
337 if (prefs
.line_mode
==JOIN
|| newlines
>0) {
340 ADVANCE_COUNTERS(' ');
342 size
= search_len
= j
;
348 /* REFLOW, multiple spaces between words: count only
349 * one. If more are needed, they will be added
353 ADVANCE_COUNTERS(' ');
355 size
= search_len
= j
;
366 /* find first hard return */
367 next_line
= find_first_feed(cur_line
, size
);
370 if (next_line
== NULL
)
371 if (size
== search_len
) {
372 if (prefs
.word_mode
== WRAP
) /* Find last space */
373 next_line
= find_last_space(cur_line
, size
);
375 if (next_line
== NULL
)
376 next_line
= crop_at_width(cur_line
);
378 if (prefs
.word_mode
== WRAP
)
380 i
<WRAP_TRIM
&& isspace(next_line
[0]) && !BUFFER_OOB(next_line
);
385 if (prefs
.line_mode
== EXPAND
)
386 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
387 if (next_line
[0] == 0)
388 if (next_line
!= cur_line
)
389 return (unsigned char*) next_line
;
391 /* If next_line is pointing to a zero, increment it; i.e.,
392 leave the terminator at the end of cur_line. If pointing
393 to a hyphen, increment only if there is room to display
394 the hyphen on current line (won't apply in WIDE mode,
395 since it's guarenteed there won't be room). */
396 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
397 if (next_line
[0] == 0 ||
398 (next_line
[0] == '-' && next_line
-cur_line
< draw_columns
))
401 if (BUFFER_OOB(next_line
))
407 return (unsigned char*) next_line
;
410 static unsigned char* find_prev_line(const unsigned char* cur_line
)
412 const unsigned char *prev_line
= NULL
;
413 const unsigned char *p
;
415 if BUFFER_OOB(cur_line
)
418 /* To wrap consistently at the same places, we must
419 start with a known hard return, then work downwards.
420 We can either search backwards for a hard return,
421 or simply start wrapping downwards from top of buffer.
422 If current line is not near top of buffer, this is
423 a file with long lines (paragraphs). We would need to
424 read earlier sectors before we could decide how to
425 properly wrap the lines above the current line, but
426 it probably is not worth the disk access. Instead,
427 start with top of buffer and wrap down from there.
428 This may result in some lines wrapping at different
429 points from where they wrap when scrolling down.
430 If buffer is at top of file, start at top of buffer. */
432 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
))
433 prev_line
= p
= NULL
;
435 prev_line
= p
= find_last_feed(buffer
, cur_line
-buffer
-1);
436 /* Null means no line feeds in buffer above current line. */
438 if (prev_line
== NULL
)
439 if (BUFFER_BOF() || cur_line
- buffer
> READ_PREV_ZONE
)
440 prev_line
= p
= buffer
;
441 /* (else return NULL and read previous block) */
443 /* Wrap downwards until too far, then use the one before. */
444 while (p
< cur_line
&& p
!= NULL
) {
446 p
= find_next_line(prev_line
, NULL
);
449 if (BUFFER_OOB(prev_line
))
452 return (unsigned char*) prev_line
;
455 static void fill_buffer(long pos
, unsigned char* buf
, unsigned size
)
457 /* Read from file and preprocess the data */
458 /* To minimize disk access, always read on sector boundaries */
460 bool found_CR
= false;
462 rb
->lseek(fd
, pos
, SEEK_SET
);
463 numread
= rb
->read(fd
, buf
, size
);
464 rb
->button_clear_queue(); /* clear button queue */
466 for(i
= 0; i
< numread
; i
++) {
483 case 0: /* No break between case 0 and default, intentionally */
496 static int read_and_synch(int direction
)
498 /* Read next (or prev) block, and reposition global pointers. */
499 /* direction: 1 for down (i.e., further into file), -1 for up */
500 int move_size
, move_vector
, offset
;
501 unsigned char *fill_buf
;
503 if (direction
== -1) /* up */ {
504 move_size
= SMALL_BLOCK_SIZE
;
506 fill_buf
= TOP_SECTOR
;
507 rb
->memcpy(BOTTOM_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
508 rb
->memcpy(MID_SECTOR
, TOP_SECTOR
, SMALL_BLOCK_SIZE
);
511 if (prefs
.view_mode
== WIDE
) {
512 /* WIDE mode needs more buffer so we have to read smaller blocks */
513 move_size
= SMALL_BLOCK_SIZE
;
514 offset
= LARGE_BLOCK_SIZE
;
515 fill_buf
= BOTTOM_SECTOR
;
516 rb
->memcpy(TOP_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
517 rb
->memcpy(MID_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
520 move_size
= LARGE_BLOCK_SIZE
;
521 offset
= SMALL_BLOCK_SIZE
;
522 fill_buf
= MID_SECTOR
;
523 rb
->memcpy(TOP_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
526 move_vector
= direction
* move_size
;
527 screen_top_ptr
-= move_vector
;
528 file_pos
+= move_vector
;
529 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
530 fill_buffer(file_pos
+ offset
, fill_buf
, move_size
);
534 static void viewer_scroll_up(void)
538 p
= find_prev_line(screen_top_ptr
);
539 if (p
== NULL
&& !BUFFER_BOF()) {
541 p
= find_prev_line(screen_top_ptr
);
547 static void viewer_scroll_down(void)
549 if (next_screen_ptr
!= NULL
)
550 screen_top_ptr
= next_line_ptr
;
553 #ifdef HAVE_LCD_BITMAP
554 static void viewer_scrollbar(void) {
555 int w
, h
, items
, min_shown
, max_shown
;
557 rb
->lcd_getstringsize("o", &w
, &h
);
558 items
= (int) file_size
; /* (SH1 int is same as long) */
559 min_shown
= (int) file_pos
+ (screen_top_ptr
- buffer
);
561 if (next_screen_ptr
== NULL
)
564 max_shown
= min_shown
+ (next_screen_ptr
- screen_top_ptr
);
566 rb
->scrollbar(0, 0, w
-2, LCD_HEIGHT
, items
, min_shown
, max_shown
, VERTICAL
);
570 static void viewer_draw(int col
)
572 int i
, j
, k
, line_len
, resynch_move
, spaces
, left_col
=0;
573 int width
, extra_spaces
, indent_spaces
, spaces_per_word
;
574 bool multiple_spacing
, line_is_short
;
575 unsigned char *line_begin
;
576 unsigned char *line_end
;
578 unsigned char scratch_buffer
[MAX_COLUMNS
+ 1];
579 unsigned char utf8_buffer
[MAX_COLUMNS
*4 + 1];
581 unsigned char *endptr
;
583 /* If col==-1 do all calculations but don't display */
585 #ifdef HAVE_LCD_BITMAP
586 left_col
= prefs
.need_scrollbar
? 1:0;
590 rb
->lcd_clear_display();
593 line_begin
= line_end
= screen_top_ptr
;
595 for (i
= 0; i
< display_lines
; i
++) {
596 if (BUFFER_OOB(line_end
))
597 break; /* Happens after display last line at BUFFER_EOF() */
599 line_begin
= line_end
;
600 line_end
= find_next_line(line_begin
, &line_is_short
);
602 if (line_end
== NULL
) {
604 if (i
< display_lines
- 1 && !BUFFER_BOF()) {
606 rb
->lcd_clear_display();
608 for (; i
< display_lines
- 1; i
++)
611 line_begin
= line_end
= screen_top_ptr
;
616 line_end
= buffer_end
;
620 resynch_move
= read_and_synch(1); /* Read block & move ptrs */
621 line_begin
-= resynch_move
;
623 next_line_ptr
-= resynch_move
;
625 line_end
= find_next_line(line_begin
, NULL
);
626 if (line_end
== NULL
) /* Should not really happen */
630 line_len
= line_end
- line_begin
;
632 if (prefs
.line_mode
== JOIN
) {
633 if (line_begin
[0] == 0) {
635 if (prefs
.word_mode
== CHOP
)
640 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
641 if (k
== MAX_COLUMNS
)
651 scratch_buffer
[k
++] = ' ';
656 scratch_buffer
[k
++] = ' ';
657 if (k
== MAX_COLUMNS
- 1)
660 scratch_buffer
[k
++] = c
;
667 scratch_buffer
[k
] = 0;
668 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
671 len
= rb
->utf8length(utf8_buffer
);
672 rb
->lcd_puts(left_col
, i
, utf8_buffer
);
675 else if (prefs
.line_mode
== REFLOW
) {
676 if (line_begin
[0] == 0) {
678 if (prefs
.word_mode
== CHOP
)
685 if (!line_is_short
) {
686 multiple_spacing
= false;
687 for (j
=width
=spaces
=0; j
< line_len
; j
++) {
692 if ((j
==0) && (prefs
.word_mode
==WRAP
))
693 /* special case: indent the paragraph,
694 * don't count spaces */
695 indent_spaces
= par_indent_spaces
;
696 else if (!multiple_spacing
)
698 multiple_spacing
= true;
701 multiple_spacing
= false;
702 width
+= glyph_width
[c
];
707 if (multiple_spacing
) spaces
--;
710 /* total number of spaces to insert between words */
711 extra_spaces
= (draw_columns
-width
) / glyph_width
[' ']
713 /* number of spaces between each word*/
714 spaces_per_word
= extra_spaces
/ spaces
;
715 /* number of words with n+1 spaces (to fill up) */
716 extra_spaces
= extra_spaces
% spaces
;
717 if (spaces_per_word
> 2) { /* too much spacing is awful */
721 } else { /* this doesn't matter much... no spaces anyway */
722 spaces_per_word
= extra_spaces
= 0;
724 } else { /* end of a paragraph: don't fill line */
729 multiple_spacing
= false;
730 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
731 if (k
== MAX_COLUMNS
)
738 if (j
==0 && prefs
.word_mode
==WRAP
) { /* indent paragraph */
739 for (j
=0; j
<par_indent_spaces
; j
++)
740 scratch_buffer
[k
++] = ' ';
743 else if (!multiple_spacing
) {
744 for (width
= spaces
<extra_spaces
? -1:0; width
< spaces_per_word
; width
++)
745 scratch_buffer
[k
++] = ' ';
748 multiple_spacing
= true;
751 scratch_buffer
[k
++] = c
;
752 multiple_spacing
= false;
759 scratch_buffer
[k
] = 0;
760 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
763 len
= rb
->utf8length(utf8_buffer
);
764 rb
->lcd_puts(left_col
, i
, utf8_buffer
);
767 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
769 if (line_len
> col
) {
772 endptr
= rb
->iso_decode(line_begin
+ col
, utf8_buffer
,
773 -1, line_end
-line_begin
);
775 len
= rb
->utf8length(utf8_buffer
);
776 rb
->lcd_puts(left_col
, i
, utf8_buffer
);
780 if (line_len
> max_line_len
)
781 max_line_len
= line_len
;
784 next_line_ptr
= line_end
;
786 next_screen_ptr
= line_end
;
787 if (BUFFER_OOB(next_screen_ptr
))
788 next_screen_ptr
= NULL
;
790 #ifdef HAVE_LCD_BITMAP
791 next_screen_to_draw_ptr
= prefs
.page_mode
==OVERLAP
? line_begin
: next_screen_ptr
;
793 if (prefs
.need_scrollbar
)
799 next_screen_to_draw_ptr
= next_screen_ptr
;
803 static void viewer_top(void)
805 /* Read top of file into buffer
806 and point screen pointer to top */
808 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
809 screen_top_ptr
= buffer
;
810 fill_buffer(0, buffer
, BUFFER_SIZE
);
813 static void viewer_bottom(void)
815 /* Read bottom of file into buffer
816 and point screen pointer to bottom */
819 if (file_size
> BUFFER_SIZE
) {
820 /* Find last buffer in file, round up to next sector boundary */
821 last_sectors
= file_size
- BUFFER_SIZE
+ SMALL_BLOCK_SIZE
;
822 last_sectors
/= SMALL_BLOCK_SIZE
;
823 last_sectors
*= SMALL_BLOCK_SIZE
;
828 file_pos
= last_sectors
;
829 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
830 screen_top_ptr
= buffer_end
-1;
831 fill_buffer(last_sectors
, buffer
, BUFFER_SIZE
);
834 #ifdef HAVE_LCD_BITMAP
835 static void init_need_scrollbar(void) {
836 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
837 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
839 prefs
.need_scrollbar
= NEED_SCROLLBAR();
840 draw_columns
= prefs
.need_scrollbar
? display_columns
-glyph_width
['o'] : display_columns
;
841 par_indent_spaces
= draw_columns
/(5*glyph_width
[' ']);
844 #define init_need_scrollbar()
847 static bool viewer_init(void)
849 #ifdef HAVE_LCD_BITMAP
853 pf
= rb
->font_get(FONT_UI
);
855 if (pf
->width
!= NULL
)
856 { /* variable pitch font -- fill structure from font width data */
857 ch
= pf
->defaultchar
- pf
->firstchar
;
858 rb
->memset(glyph_width
, pf
->width
[ch
], 256);
860 rb
->memcpy(&glyph_width
[idx
], pf
->width
, pf
->size
);
862 rb
->memset(&glyph_width
[idx
], pf
->width
[ch
], 256-idx
);
864 else /* fixed pitch font -- same width for all glyphs */
865 rb
->memset(glyph_width
, pf
->maxwidth
, 256);
867 display_lines
= LCD_HEIGHT
/ pf
->height
;
868 display_columns
= LCD_WIDTH
;
870 /* REAL fixed pitch :) all chars use up 1 cell */
872 draw_columns
= display_columns
= 11;
873 par_indent_spaces
= 2;
874 rb
->memset(glyph_width
, 1, 256);
877 fd
= rb
->open(file_name
, O_RDONLY
);
881 file_size
= rb
->filesize(fd
);
885 /* Init mac_text value used in processing buffer */
888 /* Read top of file into buffer;
889 init file_pos, buffer_end, screen_top_ptr */
892 /* Init prefs.need_scrollbar value */
893 init_need_scrollbar();
898 static void viewer_reset_settings(void)
900 prefs
.word_mode
= WRAP
;
901 prefs
.line_mode
= NORMAL
;
902 prefs
.view_mode
= NARROW
;
903 prefs
.scroll_mode
= PAGE
;
904 #ifdef HAVE_LCD_BITMAP
905 prefs
.page_mode
= NO_OVERLAP
;
906 prefs
.scrollbar_mode
= SB_OFF
;
908 prefs
.autoscroll_speed
= 1;
911 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
915 settings_fd
=rb
->open(SETTINGS_FILE
, O_RDONLY
);
918 rb
->splash(HZ
*2, true, "No Settings File");
921 if (rb
->filesize(settings_fd
) != sizeof(struct preferences
))
923 rb
->splash(HZ
*2, true, "Settings File Invalid");
927 rb
->read(settings_fd
, &prefs
, sizeof(struct preferences
));
928 rb
->close(settings_fd
);
930 init_need_scrollbar();
933 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
935 screen_top_ptr
= buffer
;
936 if (BUFFER_OOB(screen_top_ptr
)) {
937 screen_top_ptr
= buffer
;
940 fill_buffer(file_pos
, buffer
, BUFFER_SIZE
);
943 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
947 settings_fd
= rb
->creat(SETTINGS_FILE
, O_WRONLY
); /* create the settings file */
949 rb
->write (settings_fd
, &prefs
, sizeof(struct preferences
));
950 rb
->close(settings_fd
);
953 static void viewer_exit(void *parameter
)
957 viewer_save_settings();
961 static int col_limit(int col
)
966 if (col
> max_line_len
- 2)
967 col
= max_line_len
- 2;
972 /* settings helper functions */
974 static bool word_wrap_setting(void)
976 static const struct opt_items names
[] = {
978 {"Off (Chop Words)", -1},
981 return rb
->set_option("Word Wrap", &prefs
.word_mode
, INT
,
985 static bool line_mode_setting(void)
987 static const struct opt_items names
[] = {
990 {"Expand Lines", -1},
991 #ifdef HAVE_LCD_BITMAP
992 {"Reflow Lines", -1},
996 return rb
->set_option("Line Mode", &prefs
.line_mode
, INT
, names
,
997 sizeof(names
) / sizeof(names
[0]), NULL
);
1000 static bool view_mode_setting(void)
1002 static const struct opt_items names
[] = {
1003 {"No (Narrow)", -1},
1007 return rb
->set_option("Wide View", &prefs
.view_mode
, INT
,
1011 static bool scroll_mode_setting(void)
1013 static const struct opt_items names
[] = {
1014 {"Scroll by Page", -1},
1015 {"Scroll by Line", -1},
1018 return rb
->set_option("Scroll Mode", &prefs
.scroll_mode
, INT
,
1022 #ifdef HAVE_LCD_BITMAP
1023 static bool page_mode_setting(void)
1025 static const struct opt_items names
[] = {
1030 return rb
->set_option("Overlap Pages", &prefs
.page_mode
, INT
,
1034 static bool scrollbar_setting(void)
1036 static const struct opt_items names
[] = {
1041 return rb
->set_option("Show Scrollbar", &prefs
.scrollbar_mode
, INT
,
1046 static bool autoscroll_speed_setting(void)
1048 return rb
->set_int("Auto-scroll Speed", "", UNIT_INT
,
1049 &prefs
.autoscroll_speed
, NULL
, 1, 1, 10, NULL
);
1052 static bool viewer_options_menu(void)
1057 static const struct menu_item items
[] = {
1058 {"Word Wrap", word_wrap_setting
},
1059 {"Line Mode", line_mode_setting
},
1060 {"Wide View", view_mode_setting
},
1061 #ifdef HAVE_LCD_BITMAP
1062 {"Show Scrollbar", scrollbar_setting
},
1063 {"Overlap Pages", page_mode_setting
},
1065 {"Scroll Mode", scroll_mode_setting
},
1066 {"Auto-Scroll Speed", autoscroll_speed_setting
},
1068 m
= rb
->menu_init(items
, sizeof(items
) / sizeof(*items
),
1069 NULL
, NULL
, NULL
, NULL
);
1071 result
= rb
->menu_run(m
);
1073 #ifdef HAVE_LCD_BITMAP
1074 rb
->lcd_setmargins(0,0);
1076 /* Show-scrollbar mode for current view-width mode */
1077 if (!ONE_SCREEN_FITS_ALL())
1078 if (prefs
.scrollbar_mode
== true)
1079 init_need_scrollbar();
1084 static void viewer_menu(void)
1088 static const struct menu_item items
[] = {
1090 {"Viewer Options", NULL
},
1091 {"Show Playback Menu", NULL
},
1095 m
= rb
->menu_init(items
, sizeof(items
) / sizeof(*items
), NULL
, NULL
, NULL
, NULL
);
1096 result
=rb
->menu_show(m
);
1100 rb
->splash(1, true, "Saving Settings");
1105 case 1: /* change settings */
1106 done
= viewer_options_menu();
1108 case 2: /* playback control */
1109 playback_control(rb
);
1111 case 3: /* return */
1115 #ifdef HAVE_LCD_BITMAP
1116 rb
->lcd_setmargins(0,0);
1121 enum plugin_status
plugin_start(struct plugin_api
* api
, void* file
)
1124 int lastbutton
= BUTTON_NONE
;
1125 bool autoscroll
= false;
1129 old_tick
= *rb
->current_tick
;
1132 return PLUGIN_ERROR
;
1137 rb
->splash(HZ
, false, "Error");
1142 viewer_reset_settings(); /* load defaults first */
1143 viewer_load_settings(); /* .. then try to load from disk */
1151 if(old_tick
<= *rb
->current_tick
- (110-prefs
.autoscroll_speed
*10))
1153 viewer_scroll_down();
1155 old_tick
= *rb
->current_tick
;
1159 button
= rb
->button_get_w_tmo(HZ
/10);
1165 case VIEWER_AUTOSCROLL
:
1166 #ifdef VIEWER_AUTOSCROLL_PRE
1167 if (lastbutton
!= VIEWER_AUTOSCROLL_PRE
)
1170 autoscroll
= !autoscroll
;
1173 case VIEWER_PAGE_UP
:
1174 case VIEWER_PAGE_UP
| BUTTON_REPEAT
:
1175 if (prefs
.scroll_mode
== PAGE
)
1178 #ifdef HAVE_LCD_BITMAP
1179 for (i
= prefs
.page_mode
==OVERLAP
? 1:0; i
< display_lines
; i
++)
1181 for (i
= 0; i
< display_lines
; i
++)
1187 old_tick
= *rb
->current_tick
;
1191 case VIEWER_PAGE_DOWN
:
1192 case VIEWER_PAGE_DOWN
| BUTTON_REPEAT
:
1193 if (prefs
.scroll_mode
== PAGE
)
1196 if (next_screen_ptr
!= NULL
)
1197 screen_top_ptr
= next_screen_to_draw_ptr
;
1200 viewer_scroll_down();
1201 old_tick
= *rb
->current_tick
;
1205 case VIEWER_SCREEN_LEFT
:
1206 case VIEWER_SCREEN_LEFT
| BUTTON_REPEAT
:
1207 if (prefs
.view_mode
== WIDE
) {
1209 col
-= draw_columns
/glyph_width
['o'];
1210 col
= col_limit(col
);
1212 else { /* prefs.view_mode == NARROW */
1220 case VIEWER_SCREEN_RIGHT
:
1221 case VIEWER_SCREEN_RIGHT
| BUTTON_REPEAT
:
1222 if (prefs
.view_mode
== WIDE
) {
1224 col
+= draw_columns
/glyph_width
['o'];
1225 col
= col_limit(col
);
1227 else { /* prefs.view_mode == NARROW */
1228 /* Bottom of file */
1235 #ifdef VIEWER_LINE_UP
1236 case VIEWER_LINE_UP
:
1237 case VIEWER_LINE_UP
| BUTTON_REPEAT
:
1238 /* Scroll up one line */
1240 old_tick
= *rb
->current_tick
;
1244 case VIEWER_LINE_DOWN
:
1245 case VIEWER_LINE_DOWN
| BUTTON_REPEAT
:
1246 /* Scroll down one line */
1247 if (next_screen_ptr
!= NULL
)
1248 screen_top_ptr
= next_line_ptr
;
1249 old_tick
= *rb
->current_tick
;
1253 #ifdef VIEWER_COLUMN_LEFT
1254 case VIEWER_COLUMN_LEFT
:
1255 case VIEWER_COLUMN_LEFT
| BUTTON_REPEAT
:
1256 /* Scroll left one column */
1258 col
= col_limit(col
);
1262 case VIEWER_COLUMN_RIGHT
:
1263 case VIEWER_COLUMN_RIGHT
| BUTTON_REPEAT
:
1264 /* Scroll right one column */
1266 col
= col_limit(col
);
1277 if (rb
->default_event_handler_ex(button
, viewer_exit
, NULL
)
1278 == SYS_USB_CONNECTED
)
1279 return PLUGIN_USB_CONNECTED
;
1282 if (button
!= BUTTON_NONE
)
1283 lastbutton
= button
;