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 #elif CONFIG_KEYPAD == IAUDIO_M3_PAD
237 #define VIEWER_QUIT BUTTON_RC_REC
238 #define VIEWER_PAGE_UP BUTTON_RC_VOL_UP
239 #define VIEWER_PAGE_DOWN BUTTON_RC_VOL_DOWN
240 #define VIEWER_SCREEN_LEFT BUTTON_RC_REW
241 #define VIEWER_SCREEN_RIGHT BUTTON_RC_FF
242 #define VIEWER_MENU BUTTON_RC_MENU
243 #define VIEWER_AUTOSCROLL BUTTON_RC_MODE
245 #define VIEWER_RC_QUIT BUTTON_REC
247 #elif CONFIG_KEYPAD == COWOND2_PAD
248 #define VIEWER_QUIT BUTTON_POWER
249 #define VIEWER_PAGE_UP BUTTON_UP
250 #define VIEWER_PAGE_DOWN BUTTON_DOWN
251 #define VIEWER_SCREEN_LEFT BUTTON_LEFT
252 #define VIEWER_SCREEN_RIGHT BUTTON_RIGHT
253 #define VIEWER_MENU BUTTON_MENU
254 #define VIEWER_AUTOSCROLL BUTTON_SELECT
257 #error No keymap defined!
260 /* stuff for the bookmarking */
261 struct bookmarked_file_info
{
264 char filename
[MAX_PATH
];
267 struct bookmark_file_data
{
268 signed int bookmarked_files_count
;
269 struct bookmarked_file_info bookmarks
[];
282 REFLOW
, /* won't be set on charcell LCD, must be last */
306 } encoding
; /* FIXME: What should default encoding be? */
308 #ifdef HAVE_LCD_BITMAP
319 #endif /* HAVE_LCD_BITMAP */
326 int autoscroll_speed
;
330 struct preferences prefs
;
331 struct preferences old_prefs
;
333 static unsigned char *buffer
;
334 static long buffer_size
;
335 static unsigned char line_break
[] = {0,0x20,9,0xB,0xC,'-'};
336 static int display_columns
; /* number of (pixel) columns on the display */
337 static int display_lines
; /* number of lines on the display */
338 static int draw_columns
; /* number of (pixel) columns available for text */
339 static int par_indent_spaces
; /* number of spaces to indent first paragraph */
341 static char *file_name
;
342 static long file_size
;
343 static long start_position
; /* position in the file after the viewer is started */
344 static bool mac_text
;
345 static long file_pos
; /* Position of the top of the buffer in the file */
346 static unsigned char *buffer_end
; /*Set to BUFFER_END() when file_pos changes*/
347 static int max_line_len
;
348 static unsigned char *screen_top_ptr
;
349 static unsigned char *next_screen_ptr
;
350 static unsigned char *next_screen_to_draw_ptr
;
351 static unsigned char *next_line_ptr
;
352 static struct plugin_api
* rb
;
353 #ifdef HAVE_LCD_BITMAP
354 static struct font
*pf
;
358 int glyph_width(int ch
)
363 #ifdef HAVE_LCD_BITMAP
364 return rb
->font_get_width(pf
, ch
);
370 unsigned char* get_ucs(const unsigned char* str
, unsigned short* ch
)
372 unsigned char utf8_tmp
[6];
375 if (prefs
.encoding
== UTF8
)
376 return (unsigned char*)rb
->utf8decode(str
, ch
);
378 count
= BUFFER_OOB(str
+2)? 1:2;
379 rb
->iso_decode(str
, utf8_tmp
, prefs
.encoding
, count
);
380 rb
->utf8decode(utf8_tmp
, ch
);
382 if ((prefs
.encoding
== SJIS
&& *str
> 0xA0 && *str
< 0xE0) || prefs
.encoding
< SJIS
)
383 return (unsigned char*)str
+1;
385 return (unsigned char*)str
+2;
391 #define ADVANCE_COUNTERS(c) { width += glyph_width(c); k++; }
392 #define LINE_IS_FULL ((k>=MAX_COLUMNS-1) ||( width >= draw_columns))
393 #define LINE_IS_NOT_FULL ((k<MAX_COLUMNS-1) &&( width < draw_columns))
394 static unsigned char* crop_at_width(const unsigned char* p
)
398 const unsigned char *oldp
= p
;
402 while (LINE_IS_NOT_FULL
) {
405 ADVANCE_COUNTERS(ch
);
408 return (unsigned char*)oldp
;
411 static unsigned char* find_first_feed(const unsigned char* p
, int size
)
415 for (i
=0; i
< size
; i
++)
417 return (unsigned char*) p
+i
;
422 static unsigned char* find_last_feed(const unsigned char* p
, int size
)
426 for (i
=size
-1; i
>=0; i
--)
428 return (unsigned char*) p
+i
;
433 static unsigned char* find_last_space(const unsigned char* p
, int size
)
437 k
= (prefs
.line_mode
==JOIN
) || (prefs
.line_mode
==REFLOW
) ? 0:1;
439 if (!BUFFER_OOB(&p
[size
]))
440 for (j
=k
; j
< ((int) sizeof(line_break
)) - 1; j
++)
441 if (p
[size
] == line_break
[j
])
442 return (unsigned char*) p
+size
;
444 for (i
=size
-1; i
>=0; i
--)
445 for (j
=k
; j
< (int) sizeof(line_break
); j
++)
447 if (!((p
[i
] == '-') && (prefs
.word_mode
== WRAP
)))
448 if (p
[i
] == line_break
[j
])
449 return (unsigned char*) p
+i
;
455 static unsigned char* find_next_line(const unsigned char* cur_line
, bool *is_short
)
457 const unsigned char *next_line
= NULL
;
458 int size
, i
, j
, k
, width
, search_len
, spaces
, newlines
;
462 if (is_short
!= NULL
)
465 if BUFFER_OOB(cur_line
)
468 if (prefs
.view_mode
== WIDE
) {
469 search_len
= MAX_WIDTH
;
471 else { /* prefs.view_mode == NARROW */
472 search_len
= crop_at_width(cur_line
) - cur_line
;
475 size
= BUFFER_OOB(cur_line
+search_len
) ? buffer_end
-cur_line
: search_len
;
477 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
)) {
478 /* Need to scan ahead and possibly increase search_len and size,
479 or possibly set next_line at second hard return in a row. */
482 for (j
=k
=width
=spaces
=newlines
=0; ; j
++) {
483 if (BUFFER_OOB(cur_line
+j
))
486 size
= search_len
= j
;
493 if (prefs
.line_mode
== REFLOW
) {
496 next_line
= cur_line
+ size
;
497 return (unsigned char*) next_line
;
499 if (j
==0) /* i=1 is intentional */
500 for (i
=0; i
<par_indent_spaces
; i
++)
501 ADVANCE_COUNTERS(' ');
503 if (!first_chars
) spaces
++;
509 next_line
= cur_line
+ size
- spaces
;
510 if (next_line
!= cur_line
)
511 return (unsigned char*) next_line
;
517 if (BUFFER_OOB(cur_line
+size
) || size
> 2*search_len
)
520 spaces
= first_chars
? 0:1;
524 if (prefs
.line_mode
==JOIN
|| newlines
>0) {
527 ADVANCE_COUNTERS(' ');
529 size
= search_len
= j
;
535 /* REFLOW, multiple spaces between words: count only
536 * one. If more are needed, they will be added
540 ADVANCE_COUNTERS(' ');
542 size
= search_len
= j
;
553 /* find first hard return */
554 next_line
= find_first_feed(cur_line
, size
);
557 if (next_line
== NULL
)
558 if (size
== search_len
) {
559 if (prefs
.word_mode
== WRAP
) /* Find last space */
560 next_line
= find_last_space(cur_line
, size
);
562 if (next_line
== NULL
)
563 next_line
= crop_at_width(cur_line
);
565 if (prefs
.word_mode
== WRAP
)
567 i
<WRAP_TRIM
&& isspace(next_line
[0]) && !BUFFER_OOB(next_line
);
572 if (prefs
.line_mode
== EXPAND
)
573 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
574 if (next_line
[0] == 0)
575 if (next_line
!= cur_line
)
576 return (unsigned char*) next_line
;
578 /* If next_line is pointing to a zero, increment it; i.e.,
579 leave the terminator at the end of cur_line. If pointing
580 to a hyphen, increment only if there is room to display
581 the hyphen on current line (won't apply in WIDE mode,
582 since it's guarenteed there won't be room). */
583 if (!BUFFER_OOB(next_line
)) /* Not Null & not out of bounds */
584 if (next_line
[0] == 0)/* ||
585 (next_line[0] == '-' && next_line-cur_line < draw_columns)) */
588 if (BUFFER_OOB(next_line
))
594 return (unsigned char*) next_line
;
597 static unsigned char* find_prev_line(const unsigned char* cur_line
)
599 const unsigned char *prev_line
= NULL
;
600 const unsigned char *p
;
602 if BUFFER_OOB(cur_line
)
605 /* To wrap consistently at the same places, we must
606 start with a known hard return, then work downwards.
607 We can either search backwards for a hard return,
608 or simply start wrapping downwards from top of buffer.
609 If current line is not near top of buffer, this is
610 a file with long lines (paragraphs). We would need to
611 read earlier sectors before we could decide how to
612 properly wrap the lines above the current line, but
613 it probably is not worth the disk access. Instead,
614 start with top of buffer and wrap down from there.
615 This may result in some lines wrapping at different
616 points from where they wrap when scrolling down.
617 If buffer is at top of file, start at top of buffer. */
619 if ((prefs
.line_mode
== JOIN
) || (prefs
.line_mode
== REFLOW
))
620 prev_line
= p
= NULL
;
622 prev_line
= p
= find_last_feed(buffer
, cur_line
-buffer
-1);
623 /* Null means no line feeds in buffer above current line. */
625 if (prev_line
== NULL
)
626 if (BUFFER_BOF() || cur_line
- buffer
> READ_PREV_ZONE
)
627 prev_line
= p
= buffer
;
628 /* (else return NULL and read previous block) */
630 /* Wrap downwards until too far, then use the one before. */
631 while (p
< cur_line
&& p
!= NULL
) {
633 p
= find_next_line(prev_line
, NULL
);
636 if (BUFFER_OOB(prev_line
))
639 return (unsigned char*) prev_line
;
642 static void fill_buffer(long pos
, unsigned char* buf
, unsigned size
)
644 /* Read from file and preprocess the data */
645 /* To minimize disk access, always read on sector boundaries */
647 bool found_CR
= false;
649 rb
->lseek(fd
, pos
, SEEK_SET
);
650 numread
= rb
->read(fd
, buf
, size
);
651 rb
->button_clear_queue(); /* clear button queue */
653 for(i
= 0; i
< numread
; i
++) {
670 case 0: /* No break between case 0 and default, intentionally */
683 static int read_and_synch(int direction
)
685 /* Read next (or prev) block, and reposition global pointers. */
686 /* direction: 1 for down (i.e., further into file), -1 for up */
687 int move_size
, move_vector
, offset
;
688 unsigned char *fill_buf
;
690 if (direction
== -1) /* up */ {
691 move_size
= SMALL_BLOCK_SIZE
;
693 fill_buf
= TOP_SECTOR
;
694 rb
->memcpy(BOTTOM_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
695 rb
->memcpy(MID_SECTOR
, TOP_SECTOR
, SMALL_BLOCK_SIZE
);
698 if (prefs
.view_mode
== WIDE
) {
699 /* WIDE mode needs more buffer so we have to read smaller blocks */
700 move_size
= SMALL_BLOCK_SIZE
;
701 offset
= LARGE_BLOCK_SIZE
;
702 fill_buf
= BOTTOM_SECTOR
;
703 rb
->memcpy(TOP_SECTOR
, MID_SECTOR
, SMALL_BLOCK_SIZE
);
704 rb
->memcpy(MID_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
707 move_size
= LARGE_BLOCK_SIZE
;
708 offset
= SMALL_BLOCK_SIZE
;
709 fill_buf
= MID_SECTOR
;
710 rb
->memcpy(TOP_SECTOR
, BOTTOM_SECTOR
, SMALL_BLOCK_SIZE
);
713 move_vector
= direction
* move_size
;
714 screen_top_ptr
-= move_vector
;
715 file_pos
+= move_vector
;
716 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
717 fill_buffer(file_pos
+ offset
, fill_buf
, move_size
);
721 static void viewer_scroll_up(void)
725 p
= find_prev_line(screen_top_ptr
);
726 if (p
== NULL
&& !BUFFER_BOF()) {
728 p
= find_prev_line(screen_top_ptr
);
734 static void viewer_scroll_down(void)
736 if (next_screen_ptr
!= NULL
)
737 screen_top_ptr
= next_line_ptr
;
740 #ifdef HAVE_LCD_BITMAP
741 static void viewer_scrollbar(void) {
742 int items
, min_shown
, max_shown
;
744 items
= (int) file_size
; /* (SH1 int is same as long) */
745 min_shown
= (int) file_pos
+ (screen_top_ptr
- buffer
);
747 if (next_screen_ptr
== NULL
)
750 max_shown
= min_shown
+ (next_screen_ptr
- screen_top_ptr
);
752 rb
->gui_scrollbar_draw(rb
->screens
[SCREEN_MAIN
],0, 0, SCROLLBAR_WIDTH
-1,
753 LCD_HEIGHT
, items
, min_shown
, max_shown
, VERTICAL
);
757 static void viewer_draw(int col
)
759 int i
, j
, k
, line_len
, line_width
, resynch_move
, spaces
, left_col
=0;
760 int width
, extra_spaces
, indent_spaces
, spaces_per_word
;
761 bool multiple_spacing
, line_is_short
;
763 unsigned char *str
, *oldstr
;
764 unsigned char *line_begin
;
765 unsigned char *line_end
;
767 unsigned char scratch_buffer
[MAX_COLUMNS
+ 1];
768 unsigned char utf8_buffer
[MAX_COLUMNS
*4 + 1];
769 unsigned char *endptr
;
771 /* If col==-1 do all calculations but don't display */
773 #ifdef HAVE_LCD_BITMAP
774 left_col
= prefs
.need_scrollbar
? SCROLLBAR_WIDTH
:0;
778 rb
->lcd_clear_display();
781 line_begin
= line_end
= screen_top_ptr
;
783 for (i
= 0; i
< display_lines
; i
++) {
784 if (BUFFER_OOB(line_end
))
785 break; /* Happens after display last line at BUFFER_EOF() */
787 line_begin
= line_end
;
788 line_end
= find_next_line(line_begin
, &line_is_short
);
790 if (line_end
== NULL
) {
792 if (i
< display_lines
- 1 && !BUFFER_BOF()) {
794 rb
->lcd_clear_display();
796 for (; i
< display_lines
- 1; i
++)
799 line_begin
= line_end
= screen_top_ptr
;
804 line_end
= buffer_end
;
808 resynch_move
= read_and_synch(1); /* Read block & move ptrs */
809 line_begin
-= resynch_move
;
811 next_line_ptr
-= resynch_move
;
813 line_end
= find_next_line(line_begin
, NULL
);
814 if (line_end
== NULL
) /* Should not really happen */
818 line_len
= line_end
- line_begin
;
820 /* calculate line_len */
821 str
= oldstr
= line_begin
;
823 while (str
< line_end
) {
825 str
= crop_at_width(str
);
828 line_width
= j
*draw_columns
;
829 while (oldstr
< line_end
) {
830 oldstr
= get_ucs(oldstr
, &ch
);
831 line_width
+= glyph_width(ch
);
834 if (prefs
.line_mode
== JOIN
) {
835 if (line_begin
[0] == 0) {
837 if (prefs
.word_mode
== CHOP
)
842 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
843 if (k
== MAX_COLUMNS
)
853 scratch_buffer
[k
++] = ' ';
858 scratch_buffer
[k
++] = ' ';
859 if (k
== MAX_COLUMNS
- 1)
862 scratch_buffer
[k
++] = c
;
867 scratch_buffer
[k
] = 0;
868 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
869 prefs
.encoding
, draw_columns
/glyph_width('i'));
873 else if (prefs
.line_mode
== REFLOW
) {
874 if (line_begin
[0] == 0) {
876 if (prefs
.word_mode
== CHOP
)
883 if (!line_is_short
) {
884 multiple_spacing
= false;
886 for (str
= line_begin
; str
< line_end
; ) {
887 str
= get_ucs(str
, &ch
);
891 if ((str
== line_begin
) && (prefs
.word_mode
==WRAP
))
892 /* special case: indent the paragraph,
893 * don't count spaces */
894 indent_spaces
= par_indent_spaces
;
895 else if (!multiple_spacing
)
897 multiple_spacing
= true;
900 multiple_spacing
= false;
901 width
+= glyph_width(ch
);
905 if (multiple_spacing
) spaces
--;
908 /* total number of spaces to insert between words */
909 extra_spaces
= (draw_columns
-width
)/glyph_width(' ')
911 /* number of spaces between each word*/
912 spaces_per_word
= extra_spaces
/ spaces
;
913 /* number of words with n+1 spaces (to fill up) */
914 extra_spaces
= extra_spaces
% spaces
;
915 if (spaces_per_word
> 2) { /* too much spacing is awful */
919 } else { /* this doesn't matter much... no spaces anyway */
920 spaces_per_word
= extra_spaces
= 0;
922 } else { /* end of a paragraph: don't fill line */
927 multiple_spacing
= false;
928 for (j
=k
=spaces
=0; j
< line_len
; j
++) {
929 if (k
== MAX_COLUMNS
)
936 if (j
==0 && prefs
.word_mode
==WRAP
) { /* indent paragraph */
937 for (j
=0; j
<par_indent_spaces
; j
++)
938 scratch_buffer
[k
++] = ' ';
941 else if (!multiple_spacing
) {
942 for (width
= spaces
<extra_spaces
? -1:0; width
< spaces_per_word
; width
++)
943 scratch_buffer
[k
++] = ' ';
946 multiple_spacing
= true;
949 scratch_buffer
[k
++] = c
;
950 multiple_spacing
= false;
956 scratch_buffer
[k
] = 0;
957 endptr
= rb
->iso_decode(scratch_buffer
+ col
, utf8_buffer
,
958 prefs
.encoding
, k
-col
);
962 else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
964 if (line_width
> col
) {
965 str
= oldstr
= line_begin
;
968 while( (width
<draw_columns
) && (oldstr
<line_end
) )
970 oldstr
= get_ucs(oldstr
, &ch
);
972 k
-= glyph_width(ch
);
975 width
+= glyph_width(ch
);
979 if(prefs
.view_mode
==WIDE
)
980 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
981 prefs
.encoding
, oldstr
-line_begin
);
983 endptr
= rb
->iso_decode(line_begin
, utf8_buffer
,
984 prefs
.encoding
, line_end
-line_begin
);
988 if (col
!= -1 && line_width
> col
)
989 #ifdef HAVE_LCD_BITMAP
990 rb
->lcd_putsxy(left_col
, i
*pf
->height
, utf8_buffer
);
992 rb
->lcd_puts(left_col
, i
, utf8_buffer
);
994 if (line_width
> max_line_len
)
995 max_line_len
= line_width
;
998 next_line_ptr
= line_end
;
1000 next_screen_ptr
= line_end
;
1001 if (BUFFER_OOB(next_screen_ptr
))
1002 next_screen_ptr
= NULL
;
1004 #ifdef HAVE_LCD_BITMAP
1005 next_screen_to_draw_ptr
= prefs
.page_mode
==OVERLAP
? line_begin
: next_screen_ptr
;
1007 if (prefs
.need_scrollbar
)
1010 next_screen_to_draw_ptr
= next_screen_ptr
;
1017 static void viewer_top(void)
1019 /* Read top of file into buffer
1020 and point screen pointer to top */
1024 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1025 fill_buffer(0, buffer
, buffer_size
);
1028 screen_top_ptr
= buffer
;
1031 static void viewer_bottom(void)
1033 /* Read bottom of file into buffer
1034 and point screen pointer to bottom */
1037 if (file_size
> buffer_size
) {
1038 /* Find last buffer in file, round up to next sector boundary */
1039 last_sectors
= file_size
- buffer_size
+ SMALL_BLOCK_SIZE
;
1040 last_sectors
/= SMALL_BLOCK_SIZE
;
1041 last_sectors
*= SMALL_BLOCK_SIZE
;
1047 if (file_pos
!= last_sectors
)
1049 file_pos
= last_sectors
;
1050 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1051 fill_buffer(last_sectors
, buffer
, buffer_size
);
1054 screen_top_ptr
= buffer_end
-1;
1057 #ifdef HAVE_LCD_BITMAP
1058 static void init_need_scrollbar(void) {
1059 /* Call viewer_draw in quiet mode to initialize next_screen_ptr,
1060 and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
1062 prefs
.need_scrollbar
= NEED_SCROLLBAR();
1063 draw_columns
= prefs
.need_scrollbar
? display_columns
-SCROLLBAR_WIDTH
: display_columns
;
1064 par_indent_spaces
= draw_columns
/(5*glyph_width(' '));
1067 #define init_need_scrollbar()
1070 static bool viewer_init(void)
1072 #ifdef HAVE_LCD_BITMAP
1074 pf
= rb
->font_get(FONT_UI
);
1076 display_lines
= LCD_HEIGHT
/ pf
->height
;
1077 draw_columns
= display_columns
= LCD_WIDTH
;
1079 /* REAL fixed pitch :) all chars use up 1 cell */
1081 draw_columns
= display_columns
= 11;
1082 par_indent_spaces
= 2;
1085 fd
= rb
->open(file_name
, O_RDONLY
);
1089 file_size
= rb
->filesize(fd
);
1093 /* Init mac_text value used in processing buffer */
1099 static void viewer_default_settings(void)
1101 prefs
.word_mode
= WRAP
;
1102 prefs
.line_mode
= NORMAL
;
1103 prefs
.view_mode
= NARROW
;
1104 prefs
.scroll_mode
= PAGE
;
1105 #ifdef HAVE_LCD_BITMAP
1106 prefs
.page_mode
= NO_OVERLAP
;
1107 prefs
.scrollbar_mode
= SB_OFF
;
1109 prefs
.autoscroll_speed
= 1;
1110 /* Set codepage to system default */
1111 prefs
.encoding
= rb
->global_settings
->default_codepage
;
1114 static void viewer_load_settings(void) /* same name as global, but not the same file.. */
1117 struct bookmark_file_data
*data
;
1118 struct bookmarked_file_info this_bookmark
;
1120 /* read settings file */
1121 settings_fd
=rb
->open(SETTINGS_FILE
, O_RDONLY
);
1122 if ((settings_fd
>= 0) && (rb
->filesize(settings_fd
) == sizeof(struct preferences
)))
1124 rb
->read(settings_fd
, &prefs
, sizeof(struct preferences
));
1125 rb
->close(settings_fd
);
1129 /* load default settings if there is no settings file */
1130 viewer_default_settings();
1133 rb
->memcpy(&old_prefs
, &prefs
, sizeof(struct preferences
));
1135 data
= (struct bookmark_file_data
*)buffer
; /* grab the text buffer */
1136 data
->bookmarked_files_count
= 0;
1138 /* read bookmarks if file exists */
1139 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_RDONLY
);
1140 if (settings_fd
>= 0)
1142 /* figure out how many items to read */
1143 rb
->read(settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1144 if (data
->bookmarked_files_count
> MAX_BOOKMARKED_FILES
)
1145 data
->bookmarked_files_count
= MAX_BOOKMARKED_FILES
;
1146 rb
->read(settings_fd
, data
->bookmarks
,
1147 sizeof(struct bookmarked_file_info
) * data
->bookmarked_files_count
);
1148 rb
->close(settings_fd
);
1152 screen_top_ptr
= buffer
;
1154 /* check if current file is in list */
1155 for (i
=0; i
< data
->bookmarked_files_count
; i
++)
1157 if (!rb
->strcmp(file_name
, data
->bookmarks
[i
].filename
))
1159 int screen_pos
= data
->bookmarks
[i
].file_position
+ data
->bookmarks
[i
].top_ptr_pos
;
1160 int screen_top
= screen_pos
% buffer_size
;
1161 file_pos
= screen_pos
- screen_top
;
1162 screen_top_ptr
= buffer
+ screen_top
;
1167 this_bookmark
.file_position
= file_pos
;
1168 this_bookmark
.top_ptr_pos
= screen_top_ptr
- buffer
;
1170 rb
->memset(&this_bookmark
.filename
[0],0,MAX_PATH
);
1171 rb
->strcpy(this_bookmark
.filename
,file_name
);
1173 /* prevent potential slot overflow */
1174 if (i
>= data
->bookmarked_files_count
)
1176 if (i
< MAX_BOOKMARKED_FILES
)
1177 data
->bookmarked_files_count
++;
1179 i
= MAX_BOOKMARKED_FILES
-1;
1182 /* write bookmark file with spare slot in first position
1183 to be filled in by viewer_save_settings */
1184 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_WRONLY
|O_CREAT
);
1185 if (settings_fd
>=0 )
1188 rb
->write (settings_fd
, &data
->bookmarked_files_count
, sizeof(signed int));
1190 /* write the current bookmark */
1191 rb
->write (settings_fd
, &this_bookmark
, sizeof(struct bookmarked_file_info
));
1193 /* write everything that was before this bookmark */
1194 rb
->write (settings_fd
, data
->bookmarks
, sizeof(struct bookmarked_file_info
)*i
);
1196 rb
->close(settings_fd
);
1199 buffer_end
= BUFFER_END(); /* Update whenever file_pos changes */
1201 if (BUFFER_OOB(screen_top_ptr
))
1203 screen_top_ptr
= buffer
;
1206 fill_buffer(file_pos
, buffer
, buffer_size
);
1208 /* remember the current position */
1209 start_position
= file_pos
+ screen_top_ptr
- buffer
;
1211 init_need_scrollbar();
1214 static void viewer_save_settings(void)/* same name as global, but not the same file.. */
1218 /* save the viewer settings if they have been changed */
1219 if (rb
->memcmp(&prefs
, &old_prefs
, sizeof(struct preferences
)))
1221 settings_fd
= rb
->creat(SETTINGS_FILE
); /* create the settings file */
1223 if (settings_fd
>= 0 )
1225 rb
->write (settings_fd
, &prefs
, sizeof(struct preferences
));
1226 rb
->close(settings_fd
);
1230 /* save the bookmark if the position has changed */
1231 if (file_pos
+ screen_top_ptr
- buffer
!= start_position
)
1233 settings_fd
= rb
->open(BOOKMARKS_FILE
, O_WRONLY
|O_CREAT
);
1235 if (settings_fd
>= 0 )
1237 struct bookmarked_file_info b
;
1238 b
.file_position
= file_pos
+ screen_top_ptr
- buffer
;
1239 b
.top_ptr_pos
= 0; /* this is only kept for legassy reasons */
1240 rb
->memset(&b
.filename
[0],0,MAX_PATH
);
1241 rb
->strcpy(b
.filename
,file_name
);
1242 rb
->PREFIX(lseek
)(settings_fd
,sizeof(signed int),SEEK_SET
);
1243 rb
->write (settings_fd
, &b
, sizeof(struct bookmarked_file_info
));
1244 rb
->close(settings_fd
);
1249 static void viewer_exit(void *parameter
)
1253 viewer_save_settings();
1257 static int col_limit(int col
)
1262 if (col
> max_line_len
- 2*glyph_width('o'))
1263 col
= max_line_len
- 2*glyph_width('o');
1268 /* settings helper functions */
1270 static bool encoding_setting(void)
1272 static const struct opt_items names
[] = {
1277 {"ISO-8859-11", -1},
1289 return rb
->set_option("Encoding", &prefs
.encoding
, INT
, names
,
1290 sizeof(names
) / sizeof(names
[0]), NULL
);
1293 static bool word_wrap_setting(void)
1295 static const struct opt_items names
[] = {
1297 {"Off (Chop Words)", -1},
1300 return rb
->set_option("Word Wrap", &prefs
.word_mode
, INT
,
1304 static bool line_mode_setting(void)
1306 static const struct opt_items names
[] = {
1309 {"Expand Lines", -1},
1310 #ifdef HAVE_LCD_BITMAP
1311 {"Reflow Lines", -1},
1315 return rb
->set_option("Line Mode", &prefs
.line_mode
, INT
, names
,
1316 sizeof(names
) / sizeof(names
[0]), NULL
);
1319 static bool view_mode_setting(void)
1321 static const struct opt_items names
[] = {
1322 {"No (Narrow)", -1},
1326 ret
= rb
->set_option("Wide View", &prefs
.view_mode
, INT
,
1328 if (prefs
.view_mode
== NARROW
)
1333 static bool scroll_mode_setting(void)
1335 static const struct opt_items names
[] = {
1336 {"Scroll by Page", -1},
1337 {"Scroll by Line", -1},
1340 return rb
->set_option("Scroll Mode", &prefs
.scroll_mode
, INT
,
1344 #ifdef HAVE_LCD_BITMAP
1345 static bool page_mode_setting(void)
1347 static const struct opt_items names
[] = {
1352 return rb
->set_option("Overlap Pages", &prefs
.page_mode
, INT
,
1356 static bool scrollbar_setting(void)
1358 static const struct opt_items names
[] = {
1363 return rb
->set_option("Show Scrollbar", &prefs
.scrollbar_mode
, INT
,
1368 static bool autoscroll_speed_setting(void)
1370 return rb
->set_int("Auto-scroll Speed", "", UNIT_INT
,
1371 &prefs
.autoscroll_speed
, NULL
, 1, 1, 10, NULL
);
1374 static bool viewer_options_menu(void)
1379 static const struct menu_item items
[] = {
1380 {"Encoding", encoding_setting
},
1381 {"Word Wrap", word_wrap_setting
},
1382 {"Line Mode", line_mode_setting
},
1383 {"Wide View", view_mode_setting
},
1384 #ifdef HAVE_LCD_BITMAP
1385 {"Show Scrollbar", scrollbar_setting
},
1386 {"Overlap Pages", page_mode_setting
},
1388 {"Scroll Mode", scroll_mode_setting
},
1389 {"Auto-Scroll Speed", autoscroll_speed_setting
},
1391 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
),
1392 NULL
, NULL
, NULL
, NULL
);
1394 result
= menu_run(m
);
1396 #ifdef HAVE_LCD_BITMAP
1397 rb
->lcd_setmargins(0,0);
1399 /* Show-scrollbar mode for current view-width mode */
1400 init_need_scrollbar();
1405 static void viewer_menu(void)
1409 static const struct menu_item items
[] = {
1411 {"Viewer Options", NULL
},
1412 {"Show Playback Menu", NULL
},
1416 m
= menu_init(rb
, items
, sizeof(items
) / sizeof(*items
), NULL
, NULL
, NULL
, NULL
);
1417 result
=menu_show(m
);
1425 case 1: /* change settings */
1426 done
= viewer_options_menu();
1428 case 2: /* playback control */
1429 playback_control(rb
, NULL
);
1431 case 3: /* return */
1435 #ifdef HAVE_LCD_BITMAP
1436 rb
->lcd_setmargins(0,0);
1441 enum plugin_status
plugin_start(struct plugin_api
* api
, void* file
)
1444 int lastbutton
= BUTTON_NONE
;
1445 bool autoscroll
= false;
1449 old_tick
= *rb
->current_tick
;
1451 /* get the plugin buffer */
1452 buffer
= rb
->plugin_get_buffer((size_t *)&buffer_size
);
1455 return PLUGIN_ERROR
;
1460 rb
->splash(HZ
, "Error opening file.");
1461 return PLUGIN_ERROR
;
1464 viewer_load_settings(); /* load the preferences and bookmark */
1467 rb
->lcd_set_backdrop(NULL
);
1476 if(old_tick
<= *rb
->current_tick
- (110-prefs
.autoscroll_speed
*10))
1478 viewer_scroll_down();
1480 old_tick
= *rb
->current_tick
;
1484 button
= rb
->button_get_w_tmo(HZ
/10);
1490 case VIEWER_AUTOSCROLL
:
1491 #ifdef VIEWER_AUTOSCROLL_PRE
1492 if (lastbutton
!= VIEWER_AUTOSCROLL_PRE
)
1495 autoscroll
= !autoscroll
;
1498 case VIEWER_PAGE_UP
:
1499 case VIEWER_PAGE_UP
| BUTTON_REPEAT
:
1500 if (prefs
.scroll_mode
== PAGE
)
1503 #ifdef HAVE_LCD_BITMAP
1504 for (i
= prefs
.page_mode
==OVERLAP
? 1:0; i
< display_lines
; i
++)
1506 for (i
= 0; i
< display_lines
; i
++)
1512 old_tick
= *rb
->current_tick
;
1516 case VIEWER_PAGE_DOWN
:
1517 case VIEWER_PAGE_DOWN
| BUTTON_REPEAT
:
1518 if (prefs
.scroll_mode
== PAGE
)
1521 if (next_screen_ptr
!= NULL
)
1522 screen_top_ptr
= next_screen_to_draw_ptr
;
1525 viewer_scroll_down();
1526 old_tick
= *rb
->current_tick
;
1530 case VIEWER_SCREEN_LEFT
:
1531 case VIEWER_SCREEN_LEFT
| BUTTON_REPEAT
:
1532 if (prefs
.view_mode
== WIDE
) {
1534 col
-= draw_columns
;
1535 col
= col_limit(col
);
1537 else { /* prefs.view_mode == NARROW */
1545 case VIEWER_SCREEN_RIGHT
:
1546 case VIEWER_SCREEN_RIGHT
| BUTTON_REPEAT
:
1547 if (prefs
.view_mode
== WIDE
) {
1549 col
+= draw_columns
;
1550 col
= col_limit(col
);
1552 else { /* prefs.view_mode == NARROW */
1553 /* Bottom of file */
1560 #ifdef VIEWER_LINE_UP
1561 case VIEWER_LINE_UP
:
1562 case VIEWER_LINE_UP
| BUTTON_REPEAT
:
1563 /* Scroll up one line */
1565 old_tick
= *rb
->current_tick
;
1569 case VIEWER_LINE_DOWN
:
1570 case VIEWER_LINE_DOWN
| BUTTON_REPEAT
:
1571 /* Scroll down one line */
1572 if (next_screen_ptr
!= NULL
)
1573 screen_top_ptr
= next_line_ptr
;
1574 old_tick
= *rb
->current_tick
;
1578 #ifdef VIEWER_COLUMN_LEFT
1579 case VIEWER_COLUMN_LEFT
:
1580 case VIEWER_COLUMN_LEFT
| BUTTON_REPEAT
:
1581 if (prefs
.view_mode
== WIDE
) {
1582 /* Scroll left one column */
1583 col
-= glyph_width('o');
1584 col
= col_limit(col
);
1589 case VIEWER_COLUMN_RIGHT
:
1590 case VIEWER_COLUMN_RIGHT
| BUTTON_REPEAT
:
1591 if (prefs
.view_mode
== WIDE
) {
1592 /* Scroll right one column */
1593 col
+= glyph_width('o');
1594 col
= col_limit(col
);
1600 #ifdef VIEWER_RC_QUIT
1601 case VIEWER_RC_QUIT
:
1609 if (rb
->default_event_handler_ex(button
, viewer_exit
, NULL
)
1610 == SYS_USB_CONNECTED
)
1611 return PLUGIN_USB_CONNECTED
;
1614 if (button
!= BUTTON_NONE
)
1616 lastbutton
= button
;