forked from len0rd/rockbox
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25644 a1c6a512-1295-4272-9138-f99709370657
505 lines
16 KiB
C
505 lines
16 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2002 Gilles Roux, 2003 Garrett Derner
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "plugin.h"
|
|
#include "tv_drawtext.h"
|
|
|
|
#define WRAP_TRIM 44 /* Max number of spaces to trim (arbitrary) */
|
|
#define NARROW_MAX_COLUMNS 64 /* Max displayable string len [narrow] (over-estimate) */
|
|
#define WIDE_MAX_COLUMNS 128 /* Max displayable string len [wide] (over-estimate) */
|
|
#define MAX_WIDTH 910 /* Max line length in WIDE mode */
|
|
#define READ_PREV_ZONE (block_size*9/10) /* Arbitrary number less than SMALL_BLOCK_SIZE */
|
|
#define SMALL_BLOCK_SIZE block_size /* Smallest file chunk we will read */
|
|
#define LARGE_BLOCK_SIZE (block_size << 1) /* Preferable size of file chunk to read */
|
|
#define TOP_SECTOR buffer
|
|
#define MID_SECTOR (buffer + SMALL_BLOCK_SIZE)
|
|
#define BOTTOM_SECTOR (buffer + (SMALL_BLOCK_SIZE << 1))
|
|
#undef SCROLLBAR_WIDTH
|
|
#define SCROLLBAR_WIDTH rb->global_settings->scrollbar_width
|
|
#define MAX_PAGE 9999
|
|
|
|
/* Is a scrollbar called for on the current screen? */
|
|
#define NEED_SCROLLBAR() \
|
|
((!(ONE_SCREEN_FITS_ALL())) && (prefs.scrollbar_mode==SB_ON))
|
|
|
|
static void increment_current_line(void)
|
|
{
|
|
if (cline < display_lines)
|
|
cline++;
|
|
else if (cpage < MAX_PAGE)
|
|
{
|
|
cpage++;
|
|
cline = 1;
|
|
}
|
|
}
|
|
|
|
static void decrement_current_line(void)
|
|
{
|
|
if (cline > 1)
|
|
cline--;
|
|
else if (cpage > 1)
|
|
{
|
|
cpage--;
|
|
cline = display_lines;
|
|
}
|
|
}
|
|
|
|
static void viewer_scroll_up(void)
|
|
{
|
|
unsigned char *p;
|
|
|
|
p = find_prev_line(screen_top_ptr);
|
|
if (p == NULL && !BUFFER_BOF()) {
|
|
read_and_synch(-1);
|
|
p = find_prev_line(screen_top_ptr);
|
|
}
|
|
if (p != NULL)
|
|
screen_top_ptr = p;
|
|
|
|
decrement_current_line();
|
|
}
|
|
|
|
static void viewer_scroll_down(bool autoscroll)
|
|
{
|
|
if (cpage == lpage)
|
|
return;
|
|
|
|
if (next_line_ptr != NULL)
|
|
screen_top_ptr = next_line_ptr;
|
|
|
|
if (prefs.scroll_mode == LINE || autoscroll)
|
|
increment_current_line();
|
|
}
|
|
|
|
static void viewer_scroll_to_top_line(void)
|
|
{
|
|
int line;
|
|
|
|
for (line = cline; line > 1; line--)
|
|
viewer_scroll_up();
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
static void viewer_scrollbar(void) {
|
|
int items, min_shown, max_shown, sb_begin_y, sb_height;
|
|
|
|
items = (int) file_size; /* (SH1 int is same as long) */
|
|
min_shown = (int) file_pos + (screen_top_ptr - buffer);
|
|
|
|
if (next_screen_ptr == NULL)
|
|
max_shown = items;
|
|
else
|
|
max_shown = min_shown + (next_screen_ptr - screen_top_ptr);
|
|
|
|
sb_begin_y = header_height;
|
|
sb_height = LCD_HEIGHT - header_height - footer_height;
|
|
|
|
rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, sb_begin_y,
|
|
SCROLLBAR_WIDTH-1, sb_height,
|
|
items, min_shown, max_shown, VERTICAL);
|
|
}
|
|
|
|
static void viewer_show_header(void)
|
|
{
|
|
if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
|
|
rb->gui_syncstatusbar_draw(rb->statusbars, true);
|
|
|
|
if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
|
|
rb->lcd_putsxy(0, header_height - pf->height, file_name);
|
|
}
|
|
|
|
static void viewer_show_footer(void)
|
|
{
|
|
if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
|
|
rb->gui_syncstatusbar_draw(rb->statusbars, true);
|
|
|
|
if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
|
|
{
|
|
unsigned char buf[12];
|
|
|
|
if (cline == 1)
|
|
rb->snprintf(buf, sizeof(buf), "%d", cpage);
|
|
else
|
|
rb->snprintf(buf, sizeof(buf), "%d - %d", cpage, cpage+1);
|
|
|
|
rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void viewer_draw(int col)
|
|
{
|
|
int i, j, k, line_len, line_width, spaces, left_col=0;
|
|
int width, extra_spaces, indent_spaces, spaces_per_word;
|
|
bool multiple_spacing, line_is_short;
|
|
unsigned short ch;
|
|
unsigned char *str, *oldstr;
|
|
unsigned char *line_begin;
|
|
unsigned char *line_end;
|
|
unsigned char c;
|
|
unsigned char scratch_buffer[max_columns + 1];
|
|
unsigned char utf8_buffer[max_columns*4 + 1];
|
|
unsigned char *endptr;
|
|
|
|
/* If col==-1 do all calculations but don't display */
|
|
if (col != -1) {
|
|
#ifdef HAVE_LCD_BITMAP
|
|
left_col = prefs.need_scrollbar? SCROLLBAR_WIDTH:0;
|
|
#else
|
|
left_col = 0;
|
|
#endif
|
|
rb->lcd_clear_display();
|
|
}
|
|
max_line_len = 0;
|
|
line_begin = line_end = screen_top_ptr;
|
|
|
|
for (i = 0; i < display_lines; i++) {
|
|
if (BUFFER_OOB(line_end))
|
|
{
|
|
if (lpage == cpage)
|
|
break; /* Happens after display last line at BUFFER_EOF() */
|
|
|
|
if (lpage == 0 && cline == 1)
|
|
{
|
|
lpage = cpage;
|
|
last_screen_top_ptr = screen_top_ptr;
|
|
last_file_pos = file_pos;
|
|
}
|
|
}
|
|
|
|
get_next_line_position(&line_begin, &line_end, &line_is_short);
|
|
if (line_end == NULL)
|
|
{
|
|
if (BUFFER_OOB(line_begin))
|
|
break;
|
|
line_end = buffer_end + 1;
|
|
}
|
|
|
|
line_len = line_end - line_begin;
|
|
|
|
/* calculate line_len */
|
|
str = oldstr = line_begin;
|
|
j = -1;
|
|
while (str < line_end) {
|
|
oldstr = str;
|
|
str = crop_at_width(str);
|
|
j++;
|
|
}
|
|
line_width = j*draw_columns;
|
|
while (oldstr < line_end) {
|
|
oldstr = get_ucs(oldstr, &ch);
|
|
line_width += glyph_width(ch);
|
|
}
|
|
|
|
if (prefs.line_mode == JOIN) {
|
|
if (line_begin[0] == 0) {
|
|
line_begin++;
|
|
if (prefs.word_mode == CHOP)
|
|
line_end++;
|
|
else
|
|
line_len--;
|
|
}
|
|
for (j=k=spaces=0; j < line_len; j++) {
|
|
if (k == max_columns)
|
|
break;
|
|
|
|
c = line_begin[j];
|
|
switch (c) {
|
|
case ' ':
|
|
spaces++;
|
|
break;
|
|
case 0:
|
|
spaces = 0;
|
|
scratch_buffer[k++] = ' ';
|
|
break;
|
|
default:
|
|
while (spaces) {
|
|
spaces--;
|
|
scratch_buffer[k++] = ' ';
|
|
if (k == max_columns - 1)
|
|
break;
|
|
}
|
|
scratch_buffer[k++] = c;
|
|
break;
|
|
}
|
|
}
|
|
if (col != -1) {
|
|
scratch_buffer[k] = 0;
|
|
endptr = decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
|
|
*endptr = 0;
|
|
}
|
|
}
|
|
else if (prefs.line_mode == REFLOW) {
|
|
if (line_begin[0] == 0) {
|
|
line_begin++;
|
|
if (prefs.word_mode == CHOP)
|
|
line_end++;
|
|
else
|
|
line_len--;
|
|
}
|
|
|
|
indent_spaces = 0;
|
|
if (!line_is_short) {
|
|
multiple_spacing = false;
|
|
width=spaces=0;
|
|
for (str = line_begin; str < line_end; ) {
|
|
str = get_ucs(str, &ch);
|
|
switch (ch) {
|
|
case ' ':
|
|
case 0:
|
|
if ((str == line_begin) && (prefs.word_mode==WRAP))
|
|
/* special case: indent the paragraph,
|
|
* don't count spaces */
|
|
indent_spaces = par_indent_spaces;
|
|
else if (!multiple_spacing)
|
|
spaces++;
|
|
multiple_spacing = true;
|
|
break;
|
|
default:
|
|
multiple_spacing = false;
|
|
width += glyph_width(ch);
|
|
break;
|
|
}
|
|
}
|
|
if (multiple_spacing) spaces--;
|
|
|
|
if (spaces) {
|
|
/* total number of spaces to insert between words */
|
|
extra_spaces = (max_width-width)/glyph_width(' ')
|
|
- indent_spaces;
|
|
/* number of spaces between each word*/
|
|
spaces_per_word = extra_spaces / spaces;
|
|
/* number of words with n+1 spaces (to fill up) */
|
|
extra_spaces = extra_spaces % spaces;
|
|
if (spaces_per_word > 2) { /* too much spacing is awful */
|
|
spaces_per_word = 3;
|
|
extra_spaces = 0;
|
|
}
|
|
} else { /* this doesn't matter much... no spaces anyway */
|
|
spaces_per_word = extra_spaces = 0;
|
|
}
|
|
} else { /* end of a paragraph: don't fill line */
|
|
spaces_per_word = 1;
|
|
extra_spaces = 0;
|
|
}
|
|
|
|
multiple_spacing = false;
|
|
for (j=k=spaces=0; j < line_len; j++) {
|
|
if (k == max_columns)
|
|
break;
|
|
|
|
c = line_begin[j];
|
|
switch (c) {
|
|
case ' ':
|
|
case 0:
|
|
if (j==0 && prefs.word_mode==WRAP) { /* indent paragraph */
|
|
for (j=0; j<par_indent_spaces; j++)
|
|
scratch_buffer[k++] = ' ';
|
|
j=0;
|
|
}
|
|
else if (!multiple_spacing) {
|
|
for (width = spaces<extra_spaces ? -1:0; width < spaces_per_word; width++)
|
|
scratch_buffer[k++] = ' ';
|
|
spaces++;
|
|
}
|
|
multiple_spacing = true;
|
|
break;
|
|
default:
|
|
scratch_buffer[k++] = c;
|
|
multiple_spacing = false;
|
|
break;
|
|
}
|
|
}
|
|
if (col != -1) {
|
|
scratch_buffer[k] = 0;
|
|
endptr = decode2utf8(scratch_buffer, utf8_buffer, col, draw_columns);
|
|
*endptr = 0;
|
|
}
|
|
}
|
|
else { /* prefs.line_mode != JOIN && prefs.line_mode != REFLOW */
|
|
if (col != -1)
|
|
if (line_width > col) {
|
|
str = oldstr = line_begin;
|
|
k = col;
|
|
width = 0;
|
|
while( (width<draw_columns) && (oldstr<line_end) )
|
|
{
|
|
oldstr = get_ucs(oldstr, &ch);
|
|
if (k > 0) {
|
|
k -= glyph_width(ch);
|
|
line_begin = oldstr;
|
|
} else {
|
|
width += glyph_width(ch);
|
|
}
|
|
}
|
|
|
|
if(prefs.view_mode==WIDE)
|
|
endptr = rb->iso_decode(line_begin, utf8_buffer,
|
|
prefs.encoding, oldstr-line_begin);
|
|
else
|
|
endptr = rb->iso_decode(line_begin, utf8_buffer,
|
|
prefs.encoding, line_end-line_begin);
|
|
*endptr = 0;
|
|
}
|
|
}
|
|
if (col != -1 && line_width > col)
|
|
{
|
|
int dpage = (cline+i <= display_lines)?cpage:cpage+1;
|
|
int dline = cline+i - ((cline+i <= display_lines)?0:display_lines);
|
|
bool bflag = (viewer_find_bookmark(dpage, dline) >= 0);
|
|
#ifdef HAVE_LCD_BITMAP
|
|
int dy = i * pf->height + header_height;
|
|
#endif
|
|
if (bflag)
|
|
#ifdef HAVE_LCD_BITMAP
|
|
{
|
|
rb->lcd_set_drawmode(DRMODE_BG|DRMODE_FG);
|
|
rb->lcd_fillrect(left_col, dy, LCD_WIDTH, pf->height);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
|
|
}
|
|
rb->lcd_putsxy(left_col, dy, utf8_buffer);
|
|
rb->lcd_set_drawmode(DRMODE_SOLID);
|
|
#else
|
|
{
|
|
rb->lcd_puts(left_col, i, BOOKMARK_ICON);
|
|
}
|
|
rb->lcd_puts(left_col+1, i, utf8_buffer);
|
|
#endif
|
|
}
|
|
if (line_width > max_line_len)
|
|
max_line_len = line_width;
|
|
|
|
if (i == 0)
|
|
next_line_ptr = line_end;
|
|
}
|
|
next_screen_ptr = line_end;
|
|
if (BUFFER_OOB(next_screen_ptr))
|
|
next_screen_ptr = NULL;
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
next_screen_to_draw_ptr = prefs.page_mode==OVERLAP? line_begin: next_screen_ptr;
|
|
|
|
if (prefs.need_scrollbar)
|
|
viewer_scrollbar();
|
|
#else
|
|
next_screen_to_draw_ptr = next_screen_ptr;
|
|
#endif
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
/* show header */
|
|
viewer_show_header();
|
|
|
|
/* show footer */
|
|
viewer_show_footer();
|
|
#endif
|
|
|
|
if (col != -1)
|
|
rb->lcd_update();
|
|
}
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
static void init_need_scrollbar(void) {
|
|
/* Call viewer_draw in quiet mode to initialize next_screen_ptr,
|
|
and thus ONE_SCREEN_FITS_ALL(), and thus NEED_SCROLLBAR() */
|
|
viewer_draw(-1);
|
|
prefs.need_scrollbar = NEED_SCROLLBAR();
|
|
draw_columns = prefs.need_scrollbar? display_columns-SCROLLBAR_WIDTH : display_columns;
|
|
par_indent_spaces = draw_columns/(5*glyph_width(' '));
|
|
calc_max_width();
|
|
}
|
|
|
|
static void init_header_and_footer(void)
|
|
{
|
|
header_height = 0;
|
|
footer_height = 0;
|
|
if (rb->global_settings->statusbar == STATUSBAR_TOP)
|
|
{
|
|
if (prefs.header_mode == HD_SBAR || prefs.header_mode == HD_BOTH)
|
|
header_height = STATUSBAR_HEIGHT;
|
|
|
|
if (prefs.footer_mode == FT_SBAR)
|
|
prefs.footer_mode = FT_NONE;
|
|
else if (prefs.footer_mode == FT_BOTH)
|
|
prefs.footer_mode = FT_PAGE;
|
|
}
|
|
else if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
|
|
{
|
|
if (prefs.footer_mode == FT_SBAR || prefs.footer_mode == FT_BOTH)
|
|
footer_height = STATUSBAR_HEIGHT;
|
|
|
|
if (prefs.header_mode == HD_SBAR)
|
|
prefs.header_mode = HD_NONE;
|
|
else if (prefs.header_mode == HD_BOTH)
|
|
prefs.header_mode = HD_PATH;
|
|
}
|
|
else /* STATUSBAR_OFF || STATUSBAR_CUSTOM */
|
|
{
|
|
if (prefs.header_mode == HD_SBAR)
|
|
prefs.header_mode = HD_NONE;
|
|
else if (prefs.header_mode == HD_BOTH)
|
|
prefs.header_mode = HD_PATH;
|
|
|
|
if (prefs.footer_mode == FT_SBAR)
|
|
prefs.footer_mode = FT_NONE;
|
|
else if (prefs.footer_mode == FT_BOTH)
|
|
prefs.footer_mode = FT_PAGE;
|
|
}
|
|
|
|
if (prefs.header_mode == HD_NONE || prefs.header_mode == HD_PATH ||
|
|
prefs.footer_mode == FT_NONE || prefs.footer_mode == FT_PAGE)
|
|
rb->gui_syncstatusbar_draw(rb->statusbars, false);
|
|
|
|
if (prefs.header_mode == HD_PATH || prefs.header_mode == HD_BOTH)
|
|
header_height += pf->height;
|
|
if (prefs.footer_mode == FT_PAGE || prefs.footer_mode == FT_BOTH)
|
|
footer_height += pf->height;
|
|
|
|
display_lines = (LCD_HEIGHT - header_height - footer_height) / pf->height;
|
|
|
|
lpage = 0;
|
|
last_file_pos = 0;
|
|
last_screen_top_ptr = NULL;
|
|
}
|
|
|
|
static void change_font(unsigned char *font)
|
|
{
|
|
unsigned char buf[MAX_PATH];
|
|
|
|
if (font == NULL || *font == '\0')
|
|
return;
|
|
|
|
rb->snprintf(buf, MAX_PATH, "%s/%s.fnt", FONT_DIR, font);
|
|
if (rb->font_load(NULL, buf) < 0)
|
|
rb->splash(HZ/2, "font load failed.");
|
|
}
|
|
#endif
|
|
|
|
/* When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8,
|
|
* then file size decreases only BOM_SIZE.
|
|
*/
|
|
static void get_filesize(void)
|
|
{
|
|
file_size = rb->filesize(fd);
|
|
if (file_size == -1)
|
|
return;
|
|
|
|
if (prefs.encoding == UTF_8 && is_bom)
|
|
file_size -= BOM_SIZE;
|
|
}
|