summaryrefslogtreecommitdiffstats
path: root/apps/plugins
diff options
context:
space:
mode:
authorYoshihisa Uchida <uchida@rockbox.org>2010-06-05 10:30:08 +0000
committerYoshihisa Uchida <uchida@rockbox.org>2010-06-05 10:30:08 +0000
commitfdba8404503af0448586615330a7b27f2ced531c (patch)
treebb15677a7a720675ac2666f11e62042f3b2639ad /apps/plugins
parent991e92fd3dc15f1e365761264c26305559ddb0a4 (diff)
downloadrockbox-fdba8404503af0448586615330a7b27f2ced531c.tar.gz
rockbox-fdba8404503af0448586615330a7b27f2ced531c.zip
reworks text viewer plugin. (FS#11209)
new text viewer plugin: text_viewer.rock. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26571 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SUBDIRS1
-rw-r--r--apps/plugins/text_viewer/SOURCES12
-rw-r--r--apps/plugins/text_viewer/readme.txt62
-rw-r--r--apps/plugins/text_viewer/text_viewer.c210
-rw-r--r--apps/plugins/text_viewer/text_viewer.make20
-rw-r--r--apps/plugins/text_viewer/tv_action.c186
-rw-r--r--apps/plugins/text_viewer/tv_action.h119
-rw-r--r--apps/plugins/text_viewer/tv_bookmark.c302
-rw-r--r--apps/plugins/text_viewer/tv_bookmark.h85
-rw-r--r--apps/plugins/text_viewer/tv_button.h424
-rw-r--r--apps/plugins/text_viewer/tv_menu.c349
-rw-r--r--apps/plugins/text_viewer/tv_menu.h43
-rw-r--r--apps/plugins/text_viewer/tv_pager.c337
-rw-r--r--apps/plugins/text_viewer/tv_pager.h104
-rw-r--r--apps/plugins/text_viewer/tv_preferences.c119
-rw-r--r--apps/plugins/text_viewer/tv_preferences.h129
-rw-r--r--apps/plugins/text_viewer/tv_reader.c191
-rw-r--r--apps/plugins/text_viewer/tv_reader.h102
-rw-r--r--apps/plugins/text_viewer/tv_screen_pos.c41
-rw-r--r--apps/plugins/text_viewer/tv_screen_pos.h56
-rw-r--r--apps/plugins/text_viewer/tv_settings.c519
-rw-r--r--apps/plugins/text_viewer/tv_settings.h73
-rw-r--r--apps/plugins/text_viewer/tv_text_processor.c576
-rw-r--r--apps/plugins/text_viewer/tv_text_processor.h80
-rw-r--r--apps/plugins/text_viewer/tv_text_reader.c91
-rw-r--r--apps/plugins/text_viewer/tv_text_reader.h101
-rw-r--r--apps/plugins/text_viewer/tv_window.c394
-rw-r--r--apps/plugins/text_viewer/tv_window.h69
-rw-r--r--apps/plugins/viewers.config4
30 files changed, 4798 insertions, 2 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index f797c4038d..437988bd22 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -111,6 +111,7 @@ test_touchscreen,apps
test_viewports,apps
test_greylib_bitmap_scale,viewers
text_editor,apps
+text_viewer,viewers
theme_remove,viewers
vbrfix,viewers
video,viewers
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index a6b5cda363..cf1e9d2b80 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -1,5 +1,6 @@
/* For all targets */
shortcuts
+text_viewer
/* For various targets... */
diff --git a/apps/plugins/text_viewer/SOURCES b/apps/plugins/text_viewer/SOURCES
new file mode 100644
index 0000000000..7e45d1fb68
--- /dev/null
+++ b/apps/plugins/text_viewer/SOURCES
@@ -0,0 +1,12 @@
+text_viewer.c
+tv_action.c
+tv_bookmark.c
+tv_menu.c
+tv_pager.c
+tv_preferences.c
+tv_reader.c
+tv_screen_pos.c
+tv_settings.c
+tv_text_processor.c
+tv_text_reader.c
+tv_window.c
diff --git a/apps/plugins/text_viewer/readme.txt b/apps/plugins/text_viewer/readme.txt
new file mode 100644
index 0000000000..f124370bdc
--- /dev/null
+++ b/apps/plugins/text_viewer/readme.txt
@@ -0,0 +1,62 @@
+about the text viewer plugin.
+
+Limitation
+ for the target which PLUGIN_BUFFER_SIZE < 0x13000 (i.e., archos series),
+ can only be read up to 999 pages.
+
+
+Difference between viewer.rock
+ [settings file]
+ - the global setting, 'tv_global.dat' is stored.
+ - Settings and bookmarks for each file, 'tv_file.dat' is stored.
+
+ Note: when viewer.dat(viewer_file.dat) exists, tv_global.dat(tv_file.dat) is created by
+ using viewer.dat(viewer_file.dat).
+
+ [wod wrap]
+ - add the following characters which can be split the line.
+ '!', ',', '.', ':', ';', '?',
+ U+00b7, U+2010, U+3000, U+3001, U+3002, U+30fb, U+30fc,
+ U+ff01, U+ff0c, U+ff0d, U+ff0e, U+ff1a, U+ff1b, U+ff1f.
+
+ - when the line split, if the line length is short ( < 0.75 * display width),
+ split the line in display width. (thus, maybe split a word)
+
+ [line mode]
+ [join]
+ - break line condition has changed.
+ - If the next line is a blank line or spaces only line, this line breaks.
+
+ [reflow]
+ - indent changes two spaces.
+ - supports the player which does not define HAVE_LCD_BITMAP.
+
+ [alignment]
+ - the right alignment supports the player which does not define HAVE_LCD_BITMAP.
+
+ [bookmark]
+ - increased to 16 the number of bookmarks that can be registered.
+
+
+TODO list
+ - for the target which PLUGIN_BUFFER_SIZE < 0x13000 (i.e., archos series),
+ supports more than 999 pages of text.
+
+ - add History feature.
+
+ - when the WIDE screen, allow to specify the number of screens.
+
+ - when the line_mode is reflow, allow to specify indent spaces.
+
+ - for the horizontal scroll, allow the select scroll by screen/scroll by column for the settings menu.
+
+ - can display the horizontal scroll bar.
+
+ - draw images that are linked to the text. (<img src="...">)
+
+ - play audios that are linked to the text. (<audio src="...">)
+
+ - more treatments of line breaking, word wrappings.
+ (for example, period does not appear the top of line.)
+
+ - whether scroll to prev/next page or scroll to top page/bottom page can be select the settings menu.
diff --git a/apps/plugins/text_viewer/text_viewer.c b/apps/plugins/text_viewer/text_viewer.c
new file mode 100644
index 0000000000..7150498b56
--- /dev/null
+++ b/apps/plugins/text_viewer/text_viewer.c
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_action.h"
+#include "tv_button.h"
+#include "tv_preferences.h"
+
+PLUGIN_HEADER
+
+enum plugin_status plugin_start(const void* file)
+{
+ int button;
+ int lastbutton = BUTTON_NONE;
+ bool autoscroll = false;
+ long old_tick;
+ bool done = false;
+ bool display_update = true;
+ const struct tv_preferences *prefs = tv_get_preferences();
+
+ old_tick = *rb->current_tick;
+
+ if (!file)
+ return PLUGIN_ERROR;
+
+ if (!tv_init(file)) {
+ rb->splash(HZ, "Error opening file");
+ return PLUGIN_ERROR;
+ }
+
+#if LCD_DEPTH > 1
+ rb->lcd_set_backdrop(NULL);
+#endif
+
+ while (!done) {
+
+ if (display_update)
+ tv_draw();
+
+ display_update = true;
+
+ button = rb->button_get_w_tmo(HZ/10);
+
+ switch (button) {
+ case TV_MENU:
+#ifdef TV_MENU2
+ case TV_MENU2:
+#endif
+ {
+ enum tv_menu_result res = tv_menu();
+
+ if (res != TV_MENU_RESULT_EXIT_MENU)
+ {
+ tv_exit(NULL);
+ done = true;
+ if (res == TV_MENU_RESULT_ATTACHED_USB)
+ return PLUGIN_USB_CONNECTED;
+ }
+ }
+ break;
+
+ case TV_AUTOSCROLL:
+#ifdef TV_AUTOSCROLL_PRE
+ if (lastbutton != TV_AUTOSCROLL_PRE)
+ break;
+#endif
+ autoscroll = !autoscroll;
+ break;
+
+ case TV_SCROLL_UP:
+ case TV_SCROLL_UP | BUTTON_REPEAT:
+#ifdef TV_SCROLL_UP2
+ case TV_SCROLL_UP2:
+ case TV_SCROLL_UP2 | BUTTON_REPEAT:
+#endif
+ tv_scroll_up(TV_VERTICAL_SCROLL_PREFS);
+ old_tick = *rb->current_tick;
+ break;
+
+ case TV_SCROLL_DOWN:
+ case TV_SCROLL_DOWN | BUTTON_REPEAT:
+#ifdef TV_PAGE_DOWN2
+ case TV_SCROLL_DOWN2:
+ case TV_SCROLL_DOWN2 | BUTTON_REPEAT:
+#endif
+ tv_scroll_down(TV_VERTICAL_SCROLL_PREFS);
+ old_tick = *rb->current_tick;
+ break;
+
+ case TV_SCREEN_LEFT:
+ case TV_SCREEN_LEFT | BUTTON_REPEAT:
+ if (prefs->view_mode == WIDE)
+ {
+ /* Screen left */
+ tv_scroll_left(TV_HORIZONTAL_SCROLL_WINDOW);
+ }
+ else { /* prefs->view_mode == NARROW */
+ /* scroll to previous page */
+ tv_scroll_up(TV_VERTICAL_SCROLL_PAGE);
+#if 0
+ /* Top of file */
+ tv_top();
+#endif
+ }
+ break;
+
+ case TV_SCREEN_RIGHT:
+ case TV_SCREEN_RIGHT | BUTTON_REPEAT:
+ if (prefs->view_mode == WIDE)
+ {
+ /* Screen right */
+ tv_scroll_right(TV_HORIZONTAL_SCROLL_WINDOW);
+ }
+ else { /* prefs->view_mode == NARROW */
+ /* scroll to next page */
+ tv_scroll_down(TV_VERTICAL_SCROLL_PAGE);
+#if 0
+ /* Bottom of file */
+ tv_bottom();
+#endif
+ }
+ break;
+
+#ifdef TV_LINE_UP
+ case TV_LINE_UP:
+ case TV_LINE_UP | BUTTON_REPEAT:
+ /* Scroll up one line */
+ tv_scroll_up(TV_VERTICAL_SCROLL_LINE);
+ old_tick = *rb->current_tick;
+ break;
+
+ case TV_LINE_DOWN:
+ case TV_LINE_DOWN | BUTTON_REPEAT:
+ /* Scroll down one line */
+ tv_scroll_down(TV_VERTICAL_SCROLL_LINE);
+ old_tick = *rb->current_tick;
+ break;
+#endif
+#ifdef TV_COLUMN_LEFT
+ case TV_COLUMN_LEFT:
+ case TV_COLUMN_LEFT | BUTTON_REPEAT:
+ /* Scroll left one column */
+ tv_scroll_left(TV_HORIZONTAL_SCROLL_COLUMN);
+ break;
+
+ case TV_COLUMN_RIGHT:
+ case TV_COLUMN_RIGHT | BUTTON_REPEAT:
+ /* Scroll right one column */
+ tv_scroll_right(TV_HORIZONTAL_SCROLL_COLUMN);
+ break;
+#endif
+
+#ifdef TV_RC_QUIT
+ case TV_RC_QUIT:
+#endif
+ case TV_QUIT:
+#ifdef TV_QUIT2
+ case TV_QUIT2:
+#endif
+ tv_exit(NULL);
+ done = true;
+ break;
+
+ case TV_BOOKMARK:
+ tv_add_or_remove_bookmark();
+ break;
+
+ default:
+ if (rb->default_event_handler_ex(button, tv_exit, NULL)
+ == SYS_USB_CONNECTED)
+ return PLUGIN_USB_CONNECTED;
+ display_update = false;
+ break;
+ }
+ if (button != BUTTON_NONE)
+ {
+ lastbutton = button;
+ rb->yield();
+ }
+ if (autoscroll)
+ {
+ if(old_tick <= *rb->current_tick - (110 - prefs->autoscroll_speed * 10))
+ {
+ tv_scroll_down(TV_VERTICAL_SCROLL_PREFS);
+ old_tick = *rb->current_tick;
+ display_update = true;
+ }
+ }
+ }
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/text_viewer/text_viewer.make b/apps/plugins/text_viewer/text_viewer.make
new file mode 100644
index 0000000000..8cb2963faf
--- /dev/null
+++ b/apps/plugins/text_viewer/text_viewer.make
@@ -0,0 +1,20 @@
+# __________ __ ___.
+# Open \______ \ ____ ____ | | _\_ |__ _______ ___
+# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+# \/ \/ \/ \/ \/
+# $Id$
+#
+
+TEXT_VIEWER_SRCDIR := $(APPSDIR)/plugins/text_viewer
+TEXT_VIEWER_BUILDDIR := $(BUILDDIR)/apps/plugins/text_viewer
+
+TEXT_VIEWER_SRC := $(call preprocess, $(TEXT_VIEWER_SRCDIR)/SOURCES)
+TEXT_VIEWER_OBJ := $(call c2obj, $(TEXT_VIEWER_SRC))
+
+OTHER_SRC += $(TEXT_VIEWER_SRC)
+
+ROCKS += $(TEXT_VIEWER_BUILDDIR)/text_viewer.rock
+
+$(TEXT_VIEWER_BUILDDIR)/text_viewer.rock: $(TEXT_VIEWER_OBJ)
diff --git a/apps/plugins/text_viewer/tv_action.c b/apps/plugins/text_viewer/tv_action.c
new file mode 100644
index 0000000000..fb7fdb40e6
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_action.c
@@ -0,0 +1,186 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_action.h"
+#include "tv_bookmark.h"
+#include "tv_pager.h"
+#include "tv_screen_pos.h"
+#include "tv_settings.h"
+#include "tv_window.h"
+
+static const struct tv_preferences *prefs;
+
+bool tv_init(const unsigned char *file)
+{
+ size_t req_size = 0;
+ size_t size;
+ size_t used_size;
+ unsigned char *buffer;
+
+ /* get the plugin buffer */
+ buffer = rb->plugin_get_buffer(&req_size);
+ size = req_size;
+ if (buffer == NULL || size == 0)
+ return false;
+
+ prefs = tv_get_preferences();
+
+ tv_init_bookmark();
+
+ /* initialize modules */
+ if (!tv_init_window(buffer, size, &used_size))
+ return false;
+
+ /* load the preferences and bookmark */
+ tv_load_settings(file);
+
+ /* select to read the page */
+ tv_select_bookmark();
+ return true;
+}
+
+void tv_exit(void *parameter)
+{
+ (void)parameter;
+
+ /* save preference and bookmarks */
+ if (!tv_save_settings())
+ rb->splash(HZ, "Can't save preferences and bookmarks");
+
+ /* finalize modules */
+ tv_finalize_window();
+}
+
+void tv_draw(void)
+{
+ struct tv_screen_pos pos;
+
+ tv_copy_screen_pos(&pos);
+ tv_draw_window();
+ if (pos.line == 0)
+ tv_new_page();
+
+ tv_move_screen(pos.page, pos.line, SEEK_SET);
+}
+
+void tv_scroll_up(enum tv_vertical_scroll_mode mode)
+{
+ int offset_page = 0;
+ int offset_line = -1;
+
+ if ((mode == TV_VERTICAL_SCROLL_PAGE) ||
+ (mode == TV_VERTICAL_SCROLL_PREFS && prefs->scroll_mode == PAGE))
+ {
+ offset_page--;
+#ifdef HAVE_LCD_BITMAP
+ offset_line = (prefs->page_mode == OVERLAP)? 1:0;
+#endif
+ }
+ tv_move_screen(offset_page, offset_line, SEEK_CUR);
+}
+
+void tv_scroll_down(enum tv_vertical_scroll_mode mode)
+{
+ int offset_page = 0;
+ int offset_line = 1;
+
+ if ((mode == TV_VERTICAL_SCROLL_PAGE) ||
+ (mode == TV_VERTICAL_SCROLL_PREFS && prefs->scroll_mode == PAGE))
+ {
+ offset_page++;
+#ifdef HAVE_LCD_BITMAP
+ offset_line = (prefs->page_mode == OVERLAP)? -1:0;
+#endif
+ }
+ tv_move_screen(offset_page, offset_line, SEEK_CUR);
+}
+
+void tv_scroll_left(enum tv_horizontal_scroll_mode mode)
+{
+ int offset_window = 0;
+ int offset_column = 0;
+
+ if (mode == TV_HORIZONTAL_SCROLL_COLUMN)
+ {
+ /* Scroll left one column */
+ offset_column--;
+ }
+ else
+ {
+ /* Scroll left one window */
+ offset_window--;
+ }
+ tv_move_window(offset_window, offset_column);
+}
+
+void tv_scroll_right(enum tv_horizontal_scroll_mode mode)
+{
+ int offset_window = 0;
+ int offset_column = 0;
+
+ if (mode == TV_HORIZONTAL_SCROLL_COLUMN)
+ {
+ /* Scroll right one column */
+ offset_column++;
+ }
+ else
+ {
+ /* Scroll right one window */
+ offset_window++;
+ }
+ tv_move_window(offset_window, offset_column);
+}
+
+void tv_top(void)
+{
+ tv_move_screen(0, 0, SEEK_SET);
+}
+
+void tv_bottom(void)
+{
+ tv_move_screen(0, 0, SEEK_END);
+ if (prefs->scroll_mode == PAGE)
+ tv_move_screen(0, -tv_get_screen_pos()->line, SEEK_CUR);
+}
+
+enum tv_menu_result tv_menu(void)
+{
+ enum tv_menu_result res;
+ struct tv_screen_pos cur_pos;
+ off_t cur_file_pos = tv_get_screen_pos()->file_pos;
+
+ res = tv_display_menu();
+
+ tv_convert_fpos(cur_file_pos, &cur_pos);
+ if (prefs->scroll_mode == PAGE)
+ cur_pos.line = 0;
+
+ tv_move_screen(cur_pos.page, cur_pos.line, SEEK_SET);
+
+ return res;
+}
+
+void tv_add_or_remove_bookmark(void)
+{
+ tv_toggle_bookmark();
+}
diff --git a/apps/plugins/text_viewer/tv_action.h b/apps/plugins/text_viewer/tv_action.h
new file mode 100644
index 0000000000..c5f7bd0e2f
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_action.h
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_ACTION_H
+#define PLUGIN_TEXT_VIEWER_ACTION_H
+
+#include "tv_menu.h"
+
+/* horizontal scroll mode */
+enum tv_horizontal_scroll_mode
+{
+ TV_HORIZONTAL_SCROLL_COLUMN, /* left/right one column */
+ TV_HORIZONTAL_SCROLL_WINDOW, /* left/right one window */
+ TV_HORIZONTAL_SCROLL_PREFS, /* left/right follows the settings */
+};
+
+/*vertical scroll mode */
+enum tv_vertical_scroll_mode
+{
+ TV_VERTICAL_SCROLL_LINE, /* up/down one line */
+ TV_VERTICAL_SCROLL_PAGE, /* up/down one page */
+ TV_VERTICAL_SCROLL_PREFS, /* up/down follows the settings */
+};
+
+/*
+ * initialize modules
+ *
+ * [In] file
+ * read file name
+ *
+ * return
+ * true initialize success
+ * false initialize failure
+ */
+bool tv_init(const unsigned char *file);
+
+/*
+ * finalize modules
+ *
+ * [In] parameter
+ * this argument does not use
+ */
+void tv_exit(void *parameter);
+
+/* draw the current page */
+void tv_draw(void);
+
+/*
+ * scroll up
+ *
+ * [In] mode
+ * scroll mode
+ */
+void tv_scroll_up(enum tv_vertical_scroll_mode mode);
+
+/*
+ * scroll down
+ *
+ * [In] mode
+ * scroll mode
+ */
+void tv_scroll_down(enum tv_vertical_scroll_mode mode);
+
+/*
+ * scroll left
+ *
+ * [In] mode
+ * scroll mode
+ */
+void tv_scroll_left(enum tv_horizontal_scroll_mode mode);
+
+/*
+ * scroll right
+ *
+ * [In] mode
+ * scroll mode
+ */
+void tv_scroll_right(enum tv_horizontal_scroll_mode mode);
+
+/* jump to the top */
+void tv_top(void);
+
+/* jump to the bottom */
+void tv_bottom(void);
+
+/*
+ * display menu
+ *
+ * return
+ * the following value returns
+ * TV_MENU_RESULT_EXIT_MENU menu exit and continue this plugin
+ * TV_MENU_RESULT_EXIT_PLUGIN request to exit this plugin
+ * TV_MENU_RESULT_ATTACHED_USB connect USB cable
+ */
+enum tv_menu_result tv_menu(void);
+
+/* add or remove the bookmark to the current position */
+void tv_add_or_remove_bookmark(void);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_bookmark.c b/apps/plugins/text_viewer/tv_bookmark.c
new file mode 100644
index 0000000000..61df02705e
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_bookmark.c
@@ -0,0 +1,302 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_bookmark.h"
+#include "tv_pager.h"
+#include "tv_preferences.h"
+
+/* text viewer bookmark functions */
+
+enum {
+ TV_BOOKMARK_SYSTEM = 1,
+ TV_BOOKMARK_USER = 2,
+};
+
+#define SERIALIZE_BOOKMARK_SIZE 8
+
+struct tv_bookmark_info {
+ struct tv_screen_pos pos;
+ unsigned char flag;
+};
+
+/* bookmark stored array */
+struct tv_bookmark_info bookmarks[TV_MAX_BOOKMARKS + 1];
+static unsigned char bookmark_count;
+
+static int tv_compare_screen_pos(const struct tv_screen_pos *p1, const struct tv_screen_pos *p2)
+{
+ if (p1->page != p2->page)
+ return p1->page - p2->page;
+
+ return p1->line - p2->line;
+}
+
+static int bm_comp(const void *a, const void *b)
+{
+ struct tv_bookmark_info *pa;
+ struct tv_bookmark_info *pb;
+
+ pa = (struct tv_bookmark_info*)a;
+ pb = (struct tv_bookmark_info*)b;
+
+ return tv_compare_screen_pos(&pa->pos, &pb->pos);
+}
+
+static int tv_add_bookmark(const struct tv_screen_pos *pos)
+{
+ if (bookmark_count >= TV_MAX_BOOKMARKS)
+ return -1;
+
+ bookmarks[bookmark_count].pos = *pos;
+ bookmarks[bookmark_count].flag = TV_BOOKMARK_USER;
+
+ return bookmark_count++;
+}
+
+static void tv_remove_bookmark(int idx)
+{
+ int k;
+
+ if (idx >= 0 && idx < bookmark_count)
+ {
+ for (k = idx + 1; k < bookmark_count; k++)
+ bookmarks[k-1] = bookmarks[k];
+
+ bookmark_count--;
+ }
+}
+
+static int tv_find_bookmark(const struct tv_screen_pos *pos)
+{
+ int i;
+
+ for (i = 0; i < bookmark_count; i++)
+ {
+ if (tv_compare_screen_pos(&bookmarks[i].pos, pos) == 0)
+ return i;
+ }
+ return -1;
+}
+
+static void tv_change_preferences(const struct tv_preferences *oldp)
+{
+ int i;
+
+ if (oldp == NULL)
+ return;
+
+ for (i = 0; i < bookmark_count; i++)
+ tv_convert_fpos(bookmarks[i].pos.file_pos, &bookmarks[i].pos);
+}
+
+void tv_init_bookmark(void)
+{
+ tv_add_preferences_change_listner(tv_change_preferences);
+}
+
+int tv_get_bookmark_positions(struct tv_screen_pos *pos_array)
+{
+ int i;
+
+ for(i = 0; i < bookmark_count; i++)
+ *pos_array++ = bookmarks[i].pos;
+
+ return bookmark_count;
+}
+
+void tv_toggle_bookmark(void)
+{
+ const struct tv_screen_pos *pos = tv_get_screen_pos();
+ int idx = tv_find_bookmark(pos);
+
+ if (idx < 0)
+ {
+ if (tv_add_bookmark(pos) >= 0)
+ rb->splash(HZ/2, "Bookmark add");
+ else
+ rb->splash(HZ/2, "No more add bookmark");
+ return;
+ }
+ tv_remove_bookmark(idx);
+ rb->splash(HZ/2, "Bookmark remove");
+}
+
+void tv_create_system_bookmark(void)
+{
+ const struct tv_screen_pos *pos = tv_get_screen_pos();
+ int idx = tv_find_bookmark(pos);
+
+ if (idx >= 0)
+ bookmarks[idx].flag |= TV_BOOKMARK_SYSTEM;
+ else
+ {
+ bookmarks[bookmark_count].pos = *pos;
+ bookmarks[bookmark_count].flag = TV_BOOKMARK_SYSTEM;
+ bookmark_count++;
+ }
+}
+
+void tv_select_bookmark(void)
+{
+ int i;
+ struct tv_screen_pos select_pos;
+
+ for (i = 0; i < bookmark_count; i++)
+ {
+ if (bookmarks[i].flag & TV_BOOKMARK_SYSTEM)
+ break;
+ }
+
+ /* if does not find the system bookmark, add the system bookmark. */
+ if (i >= bookmark_count)
+ tv_create_system_bookmark();
+
+ if (bookmark_count == 1)
+ select_pos = bookmarks[0].pos;
+ else
+ {
+ int selected = -1;
+ struct opt_items items[bookmark_count];
+ unsigned char names[bookmark_count][24];
+
+ rb->qsort(bookmarks, bookmark_count, sizeof(struct tv_bookmark_info), bm_comp);
+
+ for (i = 0; i < bookmark_count; i++)
+ {
+ rb->snprintf(names[i], sizeof(names[0]),
+#if CONFIG_KEYPAD != PLAYER_PAD
+ "%cPage: %d Line: %d",
+#else
+ "%cP:%d L:%d",
+#endif
+ (bookmarks[i].flag & TV_BOOKMARK_SYSTEM)? '*' : ' ',
+ bookmarks[i].pos.page + 1,
+ bookmarks[i].pos.line + 1);
+ items[i].string = names[i];
+ items[i].voice_id = -1;
+ }
+
+ rb->set_option("Select bookmark", &selected, INT, items,
+ bookmark_count, NULL);
+
+ if (selected >= 0 && selected < bookmark_count)
+ select_pos = bookmarks[selected].pos;
+ else
+ {
+ /* when does not select any bookmarks, move to the current page */
+ tv_copy_screen_pos(&select_pos);
+
+ if (select_pos.file_pos == 0)
+ rb->splash(HZ, "Start the first page");
+ else
+ rb->splash(HZ, "Return to the current page");
+ }
+ }
+
+ /* deletes the system bookmark */
+ for (i = 0; i < bookmark_count; i++)
+ {
+ if ((bookmarks[i].flag &= TV_BOOKMARK_USER) == 0)
+ {
+ tv_remove_bookmark(i);
+ break;
+ }
+ }
+
+ /* move to the select position */
+ if (tv_get_preferences()->scroll_mode == PAGE)
+ select_pos.line = 0;
+
+ tv_move_screen(select_pos.page, select_pos.line, SEEK_SET);
+}
+
+/* serialize or deserialize of the bookmark array */
+static bool tv_read_bookmark_info(int fd, struct tv_bookmark_info *b)
+{
+ unsigned char buf[SERIALIZE_BOOKMARK_SIZE];
+
+ if (rb->read(fd, buf, sizeof(buf)) < 0)
+ return false;
+
+ b->pos.file_pos = (buf[0] << 24)|(buf[1] << 16)|(buf[2] << 8)|buf[3];
+ b->pos.page = (buf[4] << 8)|buf[5];
+ b->pos.line = buf[6];
+ b->flag = buf[7];
+
+ return true;
+}
+
+bool tv_deserialize_bookmarks(int fd)
+{
+ int i;
+ bool res = true;
+
+ if (rb->read(fd, &bookmark_count, 1) < 0)
+ return false;
+
+ for (i = 0; i < bookmark_count; i++)
+ {
+ if (!tv_read_bookmark_info(fd, &bookmarks[i]))
+ {
+ res = false;
+ break;
+ }
+ }
+
+ bookmark_count = i;
+ return res;
+}
+
+static bool tv_write_bookmark_info(int fd, const struct tv_bookmark_info *b)
+{
+ unsigned char buf[SERIALIZE_BOOKMARK_SIZE];
+ unsigned char *p = buf;
+
+ *p++ = b->pos.file_pos >> 24;
+ *p++ = b->pos.file_pos >> 16;
+ *p++ = b->pos.file_pos >> 8;
+ *p++ = b->pos.file_pos;
+
+ *p++ = b->pos.page >> 8;
+ *p++ = b->pos.page;
+
+ *p++ = b->pos.line;
+ *p = b->flag;
+
+ return (rb->write(fd, buf, SERIALIZE_BOOKMARK_SIZE) >= 0);
+}
+
+int tv_serialize_bookmarks(int fd)
+{
+ int i;
+
+ if (rb->write(fd, &bookmark_count, 1) < 0)
+ return 0;
+
+ for (i = 0; i < bookmark_count; i++)
+ {
+ if (!tv_write_bookmark_info(fd, &bookmarks[i]))
+ break;
+ }
+ return i * SERIALIZE_BOOKMARK_SIZE + 1;
+}
diff --git a/apps/plugins/text_viewer/tv_bookmark.h b/apps/plugins/text_viewer/tv_bookmark.h
new file mode 100644
index 0000000000..d61af41620
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_bookmark.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_BOOKMARK_H
+#define PLUGIN_TEXT_VIEWER_BOOKMARK_H
+
+#include "tv_screen_pos.h"
+
+/* stuff for the bookmarking */
+
+/* Maximum amount of register possible bookmarks */
+#define TV_MAX_BOOKMARKS 16
+
+/* initialize the bookmark module */
+void tv_init_bookmark(void);
+
+/*
+ * get the positions which registered bookmarks
+ *
+ * [Out] pos_array
+ * the array which store positions of all bookmarks
+ *
+ * return
+ * bookmark count
+ */
+int tv_get_bookmark_positions(struct tv_screen_pos *pos_array);
+
+/*
+ * the function that a bookmark add when there is not a bookmark in the given position
+ * or the bookmark remove when there exist a bookmark in the given position.
+ */
+void tv_toggle_bookmark(void);
+
+/*
+ * The menu that can select registered bookmarks is displayed, one is selected from
+ * among them, and moves to the page which selected bookmarks.
+ */
+void tv_select_bookmark(void);
+
+/* creates system bookmark */
+void tv_create_system_bookmark(void);
+
+/*
+ * serialize the bookmark array
+ *
+ * [In] fd
+ * the file descripter which is stored the result.
+ *
+ * Return
+ * the size of the result
+ */
+int tv_serialize_bookmarks(int fd);
+
+/*
+ * deserialize the bookmark array
+ *
+ * [In] fd
+ * the file descripter which is stored the serialization of the bookmark array.
+ *
+ * Return
+ * true success
+ * false failure
+ */
+bool tv_deserialize_bookmarks(int fd);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_button.h b/apps/plugins/text_viewer/tv_button.h
new file mode 100644
index 0000000000..53114c6169
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_button.h
@@ -0,0 +1,424 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_BUTTON_H
+#define PLUGIN_TEXT_VIEWER_BUTTON_H
+
+/* variable button definitions */
+
+/*
+ * [required]
+ * TV_QUIT exit txit viewer
+ * TV_SCROLL_UP scroll up
+ * TV_SCROLL_DOWN scroll down
+ * TV_SCREEN_LEFT scroll left (when wide mode)/scroll previous page (when narrow mode)
+ * TV_SCREEN_RIGHT scroll right (when wide mode)/scroll next page (when narrow mode)
+ * TV_MENU enter menu
+ * TV_AUTOSCROLL toggle autoscroll
+ * TV_BOOKMARK set/reset bookmark
+ *
+ * [optional]
+ * TV_RC_QUIT exit text plugin (remote key only)
+ * TV_LINE_UP one line up
+ * TV_LINE_DOWN one line down
+ * TV_COLUMN_LEFT one column left
+ * TV_COLUMN_RIGHT one column right
+ */
+
+/* Recorder keys */
+#if CONFIG_KEYPAD == RECORDER_PAD
+#define TV_QUIT BUTTON_OFF
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_F1
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_LINE_UP (BUTTON_ON | BUTTON_UP)
+#define TV_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
+#define TV_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
+#define TV_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
+#define TV_BOOKMARK BUTTON_F2
+
+/* Archos AV300 keys */
+#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
+#define TV_QUIT BUTTON_OFF
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_F1
+#define TV_AUTOSCROLL BUTTON_SELECT
+#define TV_LINE_UP (BUTTON_ON | BUTTON_UP)
+#define TV_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
+#define TV_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
+#define TV_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
+#define TV_BOOKMARK BUTTON_F2
+
+/* Ondio keys */
+#elif CONFIG_KEYPAD == ONDIO_PAD
+#define TV_QUIT BUTTON_OFF
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU (BUTTON_MENU|BUTTON_REPEAT)
+#define TV_AUTOSCROLL_PRE BUTTON_MENU
+#define TV_AUTOSCROLL (BUTTON_MENU|BUTTON_REL)
+#define TV_BOOKMARK (BUTTON_MENU|BUTTON_OFF)
+
+/* Player keys */
+#elif CONFIG_KEYPAD == PLAYER_PAD
+#define TV_QUIT BUTTON_STOP
+#define TV_SCROLL_UP BUTTON_LEFT
+#define TV_SCROLL_DOWN BUTTON_RIGHT
+#define TV_SCREEN_LEFT (BUTTON_ON|BUTTON_LEFT)
+#define TV_SCREEN_RIGHT (BUTTON_ON|BUTTON_RIGHT)
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_BOOKMARK BUTTON_ON
+
+/* iRiver H1x0 && H3x0 keys */
+#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \
+ (CONFIG_KEYPAD == IRIVER_H300_PAD)
+#define TV_QUIT BUTTON_OFF
+#define TV_RC_QUIT BUTTON_RC_STOP
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MODE
+#define TV_AUTOSCROLL BUTTON_SELECT
+#define TV_LINE_UP (BUTTON_ON | BUTTON_UP)
+#define TV_LINE_DOWN (BUTTON_ON | BUTTON_DOWN)
+#define TV_COLUMN_LEFT (BUTTON_ON | BUTTON_LEFT)
+#define TV_COLUMN_RIGHT (BUTTON_ON | BUTTON_RIGHT)
+#define TV_BOOKMARK (BUTTON_ON | BUTTON_SELECT)
+
+/* iPods */
+#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_3G_PAD) || \
+ (CONFIG_KEYPAD == IPOD_1G2G_PAD)
+#define TV_QUIT_PRE BUTTON_SELECT
+#define TV_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define TV_SCROLL_UP BUTTON_SCROLL_BACK
+#define TV_SCROLL_DOWN BUTTON_SCROLL_FWD
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_BOOKMARK BUTTON_SELECT
+
+/* iFP7xx keys */
+#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
+#define TV_QUIT BUTTON_PLAY
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MODE
+#define TV_AUTOSCROLL BUTTON_SELECT
+#define TV_BOOKMARK (BUTTON_LEFT|BUTTON_SELECT)
+
+/* iAudio X5 keys */
+#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_SELECT
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_BOOKMARK BUTTON_REC
+
+/* GIGABEAT keys */
+#elif CONFIG_KEYPAD == GIGABEAT_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_A
+#define TV_BOOKMARK BUTTON_SELECT
+
+/* Sansa E200 keys */
+#elif CONFIG_KEYPAD == SANSA_E200_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_SELECT
+#define TV_AUTOSCROLL BUTTON_REC
+#define TV_LINE_UP BUTTON_SCROLL_BACK
+#define TV_LINE_DOWN BUTTON_SCROLL_FWD
+#define TV_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
+
+/* Sansa Fuze keys */
+#elif CONFIG_KEYPAD == SANSA_FUZE_PAD
+#define TV_QUIT (BUTTON_HOME|BUTTON_REPEAT)
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_SELECT|BUTTON_REPEAT
+#define TV_AUTOSCROLL BUTTON_SELECT|BUTTON_DOWN
+#define TV_LINE_UP BUTTON_SCROLL_BACK
+#define TV_LINE_DOWN BUTTON_SCROLL_FWD
+#define TV_BOOKMARK BUTTON_SELECT
+
+/* Sansa C200 keys */
+#elif CONFIG_KEYPAD == SANSA_C200_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_VOL_UP
+#define TV_SCROLL_DOWN BUTTON_VOL_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_SELECT
+#define TV_AUTOSCROLL BUTTON_REC
+#define TV_LINE_UP BUTTON_UP
+#define TV_LINE_DOWN BUTTON_DOWN
+#define TV_BOOKMARK (BUTTON_DOWN | BUTTON_SELECT)
+
+/* Sansa Clip keys */
+#elif CONFIG_KEYPAD == SANSA_CLIP_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_VOL_UP
+#define TV_SCROLL_DOWN BUTTON_VOL_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_SELECT
+#define TV_AUTOSCROLL BUTTON_HOME
+#define TV_LINE_UP BUTTON_UP
+#define TV_LINE_DOWN BUTTON_DOWN
+#define TV_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
+
+/* Sansa M200 keys */
+#elif CONFIG_KEYPAD == SANSA_M200_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_VOL_UP
+#define TV_SCROLL_DOWN BUTTON_VOL_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU (BUTTON_SELECT | BUTTON_UP)
+#define TV_AUTOSCROLL (BUTTON_SELECT | BUTTON_REL)
+#define TV_LINE_UP BUTTON_UP
+#define TV_LINE_DOWN BUTTON_DOWN
+#define TV_BOOKMARK (BUTTON_DOWN|BUTTON_SELECT)
+
+/* iriver H10 keys */
+#elif CONFIG_KEYPAD == IRIVER_H10_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_SCROLL_UP
+#define TV_SCROLL_DOWN BUTTON_SCROLL_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_REW
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_BOOKMARK BUTTON_FF
+
+/*M-Robe 500 keys */
+#elif CONFIG_KEYPAD == MROBE500_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_RC_PLAY
+#define TV_SCROLL_DOWN BUTTON_RC_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_RC_HEART
+#define TV_AUTOSCROLL BUTTON_RC_MODE
+#define TV_BOOKMARK BUTTON_CENTER
+
+/*Gigabeat S keys */
+#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
+#define TV_QUIT BUTTON_BACK
+#define TV_SCROLL_UP BUTTON_PREV
+#define TV_SCROLL_DOWN BUTTON_NEXT
+#define TV_SCREEN_LEFT (BUTTON_PLAY | BUTTON_LEFT)
+#define TV_SCREEN_RIGHT (BUTTON_PLAY | BUTTON_RIGHT)
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL_PRE BUTTON_PLAY
+#define TV_AUTOSCROLL (BUTTON_PLAY|BUTTON_REL)
+#define TV_LINE_UP BUTTON_UP
+#define TV_LINE_DOWN BUTTON_DOWN
+#define TV_COLUMN_LEFT BUTTON_LEFT
+#define TV_COLUMN_RIGHT BUTTON_RIGHT
+#define TV_BOOKMARK BUTTON_SELECT
+
+/*M-Robe 100 keys */
+#elif CONFIG_KEYPAD == MROBE100_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_DISPLAY
+#define TV_BOOKMARK BUTTON_SELECT
+
+/* iAUdio M3 keys */
+#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
+#define TV_QUIT BUTTON_REC
+#define TV_RC_QUIT BUTTON_RC_REC
+#define TV_SCROLL_UP BUTTON_RC_VOL_UP
+#define TV_SCROLL_DOWN BUTTON_RC_VOL_DOWN
+#define TV_SCREEN_LEFT BUTTON_RC_REW
+#define TV_SCREEN_RIGHT BUTTON_RC_FF
+#define TV_MENU BUTTON_RC_MENU
+#define TV_AUTOSCROLL BUTTON_RC_MODE
+#define TV_BOOKMARK BUTTON_RC_PLAY
+
+/* Cowon D2 keys */
+#elif CONFIG_KEYPAD == COWON_D2_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_MENU BUTTON_MENU
+#define TV_SCROLL_UP BUTTON_MINUS
+#define TV_SCROLL_DOWN BUTTON_PLUS
+#define TV_BOOKMARK (BUTTON_MENU|BUTTON_PLUS)
+
+#elif CONFIG_KEYPAD == IAUDIO67_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_VOLUP
+#define TV_SCROLL_DOWN BUTTON_VOLDOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_RC_QUIT BUTTON_STOP
+#define TV_BOOKMARK (BUTTON_LEFT|BUTTON_PLAY)
+
+/* Creative Zen Vision:M keys */
+#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
+#define TV_QUIT BUTTON_BACK
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_SELECT
+#define TV_BOOKMARK BUTTON_PLAY
+
+/* Philips HDD1630 keys */
+#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_VIEW
+#define TV_BOOKMARK BUTTON_SELECT
+
+/* Philips SA9200 keys */
+#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_PREV
+#define TV_SCREEN_RIGHT BUTTON_NEXT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_BOOKMARK BUTTON_RIGHT
+
+/* Onda VX747 keys */
+#elif CONFIG_KEYPAD == ONDAVX747_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_MENU BUTTON_MENU
+#define TV_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER)
+
+/* Onda VX777 keys */
+#elif CONFIG_KEYPAD == ONDAVX777_PAD
+#define TV_QUIT BUTTON_POWER
+#define TV_BOOKMARK (BUTTON_RIGHT|BUTTON_POWER)
+
+/* SAMSUNG YH-820 / YH-920 / YH-925 keys */
+#elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
+#define TV_QUIT BUTTON_REC
+#define TV_SCROLL_UP BUTTON_UP
+#define TV_SCROLL_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_LEFT
+#define TV_SCREEN_RIGHT BUTTON_RIGHT
+#define TV_MENU BUTTON_PLAY
+#define TV_AUTOSCROLL BUTTON_REW
+#define TV_BOOKMARK BUTTON_FFWD
+
+/* Packard Bell Vibe 500 keys */
+#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
+#define TV_QUIT BUTTON_REC
+#define TV_SCROLL_UP BUTTON_OK
+#define TV_SCROLL_DOWN BUTTON_CANCEL
+#define TV_LINE_UP BUTTON_UP
+#define TV_LINE_DOWN BUTTON_DOWN
+#define TV_SCREEN_LEFT BUTTON_PREV
+#define TV_SCREEN_RIGHT BUTTON_NEXT
+#define TV_MENU BUTTON_MENU
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_BOOKMARK BUTTON_POWER
+
+/* MPIO HD200 keys */
+#elif CONFIG_KEYPAD == MPIO_HD200_PAD
+#define TV_QUIT (BUTTON_REC | BUTTON_PLAY)
+#define TV_SCROLL_UP BUTTON_PREV
+#define TV_SCROLL_DOWN BUTTON_NEXT
+#define TV_SCREEN_LEFT BUTTON_VOL_DOWN
+#define TV_SCREEN_RIGHT BUTTON_VOL_UP
+#define TV_MENU BUTTON_SELECT
+#define TV_AUTOSCROLL BUTTON_PLAY
+#define TV_BOOKMARK BUTTON_REC
+
+#else
+#error No keymap defined!
+#endif
+
+#ifdef HAVE_TOUCHSCREEN
+#ifdef TV_QUIT
+#define TV_QUIT2 BUTTON_TOPLEFT
+#else
+#define TV_QUIT BUTTON_TOPLEFT
+#endif
+#ifdef TV_SCROLL_UP
+#define TV_SCROLL_UP2 BUTTON_TOPMIDDLE
+#else
+#define TV_SCROLL_UP BUTTON_TOPMIDDLE
+#endif
+#ifdef TV_SCROLL_DOWN
+#define TV_SCROLL_DOWN2 BUTTON_BOTTOMMIDDLE
+#else
+#define TV_SCROLL_DOWN BUTTON_BOTTOMMIDDLE
+#endif
+#ifndef TV_SCREEN_LEFT
+#define TV_SCREEN_LEFT BUTTON_MIDLEFT
+#endif
+#ifndef TV_SCREEN_RIGHT
+#define TV_SCREEN_RIGHT BUTTON_MIDRIGHT
+#endif
+#ifdef TV_MENU
+#define TV_MENU2 BUTTON_TOPRIGHT
+#else
+#define TV_MENU BUTTON_TOPRIGHT
+#endif
+#ifndef TV_AUTOSCROLL
+#define TV_AUTOSCROLL BUTTON_CENTER
+#endif
+#endif
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_menu.c b/apps/plugins/text_viewer/tv_menu.c
new file mode 100644
index 0000000000..f40afb8848
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_menu.c
@@ -0,0 +1,349 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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 "lib/playback_control.h"
+#include "tv_bookmark.h"
+#include "tv_menu.h"
+#include "tv_settings.h"
+
+/* settings helper functions */
+
+static struct tv_preferences new_prefs;
+
+static bool tv_encoding_setting(void)
+{
+ static struct opt_items names[NUM_CODEPAGES];
+ int idx;
+
+ for (idx = 0; idx < NUM_CODEPAGES; idx++)
+ {
+ names[idx].string = rb->get_codepage_name(idx);
+ names[idx].voice_id = -1;
+ }
+
+ return rb->set_option("Encoding", &new_prefs.encoding, INT, names,
+ sizeof(names) / sizeof(names[0]), NULL);
+}
+
+static bool tv_word_wrap_setting(void)
+{
+ static const struct opt_items names[] = {
+ {"On", -1},
+ {"Off (Chop Words)", -1},
+ };
+
+ return rb->set_option("Word Wrap", &new_prefs.word_mode, INT,
+ names, 2, NULL);
+}
+
+static bool tv_line_mode_setting(void)
+{
+ static const struct opt_items names[] = {
+ {"Normal", -1},
+ {"Join Lines", -1},
+ {"Expand Lines", -1},
+ {"Reflow Lines", -1},
+ };
+
+ return rb->set_option("Line Mode", &new_prefs.line_mode, INT, names,
+ sizeof(names) / sizeof(names[0]), NULL);
+}
+
+static bool tv_view_mode_setting(void)
+{
+ static const struct opt_items names[] = {
+ {"No (Narrow)", -1},
+ {"Yes", -1},
+ };
+
+ return rb->set_option("Wide View", &new_prefs.view_mode, INT,
+ names , 2, NULL);
+}
+
+static bool tv_scroll_mode_setting(void)
+{
+ static const struct opt_items names[] = {
+ {"Scroll by Page", -1},
+ {"Scroll by Line", -1},
+ };
+
+ return rb->set_option("Scroll Mode", &new_prefs.scroll_mode, INT,
+ names, 2, NULL);
+}
+
+static bool tv_alignment_setting(void)
+{
+ static const struct opt_items names[] = {
+ {"Left", -1},
+ {"Right", -1},
+ };
+
+ return rb->set_option("Alignment", &new_prefs.alignment, INT,
+ names , 2, NULL);
+}
+
+#ifdef HAVE_LCD_BITMAP
+static bool tv_page_mode_setting(void)
+{
+ static const struct opt_items names[] = {
+ {"No", -1},
+ {"Yes", -1},
+ };
+
+ return rb->set_option("Overlap Pages", &new_prefs.page_mode, INT,
+ names, 2, NULL);
+}
+
+static bool tv_scrollbar_setting(void)
+{
+ static const struct opt_items names[] = {
+ {"Off", -1},
+ {"On", -1}
+ };
+
+ return rb->set_option("Show Scrollbar", &new_prefs.scrollbar_mode, INT,
+ names, 2, NULL);
+}
+
+static bool tv_header_setting(void)
+{
+ int len = (rb->global_settings->statusbar == STATUSBAR_TOP)? 4 : 2;
+ struct opt_items names[len];
+
+ names[0].string = "None";
+ names[0].voice_id = -1;
+ names[1].string = "File path";
+ names[1].voice_id = -1;
+
+ if (rb->global_settings->statusbar == STATUSBAR_TOP)
+ {
+ names[2].string = "Status bar";
+ names[2].voice_id = -1;
+ names[3].string = "Both";
+ names[3].voice_id = -1;
+ }
+
+ return rb->set_option("Show Header", &new_prefs.header_mode, INT,
+ names, len, NULL);
+}
+
+static bool tv_footer_setting(void)
+{
+ int len = (rb->global_settings->statusbar == STATUSBAR_BOTTOM)? 4 : 2;
+ struct opt_items names[len];
+
+ names[0].string = "None";
+ names[0].voice_id = -1;
+ names[1].string = "Page Num";
+ names[1].voice_id = -1;
+
+ if (rb->global_settings->statusbar == STATUSBAR_BOTTOM)
+ {
+ names[2].string = "Status bar";
+ names[2].voice_id = -1;
+ names[3].string = "Both";
+ names[3].voice_id = -1;
+ }
+
+ return rb->set_option("Show Footer", &new_prefs.footer_mode, INT,
+ names, len, NULL);
+}
+
+static int tv_font_comp(const void *a, const void *b)
+{
+ struct opt_items *pa;
+ struct opt_items *pb;
+
+ pa = (struct opt_items *)a;
+ pb = (struct opt_items *)b;
+
+ return rb->strcmp(pa->string, pb->string);
+}
+
+static bool tv_font_setting(void)
+{
+ int count = 0;
+ DIR *dir;
+ struct dirent *entry;
+ int i = 0;
+ int len;
+ int new_font = 0;
+ int old_font;
+ bool res;
+ int size = 0;
+
+ dir = rb->opendir(FONT_DIR);
+ if (!dir)
+ {
+ rb->splash(HZ/2, "font dir does not access");
+ return false;
+ }
+
+ while ((entry = rb->readdir(dir)) != NULL)
+ {
+ len = rb->strlen(entry->d_name);
+ if (len < 4 || rb->strcmp(entry->d_name + len - 4, ".fnt"))
+ continue;
+ size += len - 3;
+ count++;
+ }
+ rb->closedir(dir);
+
+ struct opt_items names[count];
+ unsigned char font_names[size];
+ unsigned char *p = font_names;
+
+ dir = rb->opendir(FONT_DIR);
+ if (!dir)
+ {
+ rb->splash(HZ/2, "font dir does not access");
+ return false;
+ }
+
+ while ((entry = rb->readdir(dir)) != NULL)
+ {
+ len = rb->strlen(entry->d_name);
+ if (len < 4 || rb->strcmp(entry->d_name + len - 4, ".fnt"))
+ continue;
+
+ rb->strlcpy(p, entry->d_name, len - 3);
+ names[i].string = p;
+ names[i].voice_id = -1;
+ p += len - 3;
+ if (++i >= count)
+ break;
+ }
+ rb->closedir(dir);
+
+ rb->qsort(names, count, sizeof(struct opt_items), tv_font_comp);
+
+ for (i = 0; i < count; i++)
+ {
+ if (!rb->strcmp(names[i].string, new_prefs.font_name))
+ {
+ new_font = i;
+ break;
+ }
+ }
+ old_font = new_font;
+
+ res = rb->set_option("Select Font", &new_font, INT,
+ names, count, NULL);
+
+ if (new_font != old_font)
+ {
+ rb->memset(new_prefs.font_name, 0, MAX_PATH);
+ rb->strlcpy(new_prefs.font_name, names[new_font].string, MAX_PATH);
+ }
+
+ return res;
+}
+#endif
+
+static bool tv_autoscroll_speed_setting(void)
+{
+ return rb->set_int("Auto-scroll Speed", "", UNIT_INT,
+ &new_prefs.autoscroll_speed, NULL, 1, 1, 10, NULL);
+}
+
+MENUITEM_FUNCTION(encoding_item, 0, "Encoding", tv_encoding_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(word_wrap_item, 0, "Word Wrap", tv_word_wrap_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(line_mode_item, 0, "Line Mode", tv_line_mode_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(view_mode_item, 0, "Wide View", tv_view_mode_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(alignment_item, 0, "Alignment", tv_alignment_setting,
+ NULL, NULL, Icon_NOICON);
+#ifdef HAVE_LCD_BITMAP
+MENUITEM_FUNCTION(scrollbar_item, 0, "Show Scrollbar", tv_scrollbar_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(page_mode_item, 0, "Overlap Pages", tv_page_mode_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(header_item, 0, "Show Header", tv_header_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(footer_item, 0, "Show Footer", tv_footer_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(font_item, 0, "Font", tv_font_setting,
+ NULL, NULL, Icon_NOICON);
+#endif
+MENUITEM_FUNCTION(scroll_mode_item, 0, "Scroll Mode", tv_scroll_mode_setting,
+ NULL, NULL, Icon_NOICON);
+MENUITEM_FUNCTION(autoscroll_speed_item, 0, "Auto-Scroll Speed",
+ tv_autoscroll_speed_setting, NULL, NULL, Icon_NOICON);
+MAKE_MENU(option_menu, "Viewer Options", NULL, Icon_NOICON,
+ &encoding_item, &word_wrap_item, &line_mode_item, &view_mode_item,
+ &alignment_item,
+#ifdef HAVE_LCD_BITMAP
+ &scrollbar_item, &page_mode_item, &header_item, &footer_item, &font_item,
+#endif
+ &scroll_mode_item, &autoscroll_speed_item);
+
+static enum tv_menu_result tv_options_menu(void)
+{
+ bool result = TV_MENU_RESULT_EXIT_MENU;
+
+ if (rb->do_menu(&option_menu, NULL, NULL, false) == MENU_ATTACHED_USB)
+ result = TV_MENU_RESULT_ATTACHED_USB;
+
+ return result;
+}
+
+enum tv_menu_result tv_display_menu(void)
+{
+ int result = TV_MENU_RESULT_EXIT_MENU;
+
+ MENUITEM_STRINGLIST(menu, "Viewer Menu", NULL,
+ "Return", "Viewer Options",
+ "Show Playback Menu", "Select Bookmark",
+ "Global Settings", "Quit");
+
+ switch (rb->do_menu(&menu, NULL, NULL, false))
+ {
+ case 0: /* return */
+ break;
+ case 1: /* change settings */
+ tv_copy_preferences(&new_prefs);
+ result = tv_options_menu();
+ tv_set_preferences(&new_prefs);
+ break;
+ case 2: /* playback control */
+ playback_control(NULL);
+ break;
+ case 3: /* select bookmark */
+ tv_select_bookmark();
+ break;
+ case 4: /* change global settings */
+ if (!tv_load_global_settings(&new_prefs))
+ tv_set_default_preferences(&new_prefs);
+
+ result = tv_options_menu();
+ tv_save_global_settings(&new_prefs);
+ break;
+ case 5: /* quit */
+ result = TV_MENU_RESULT_EXIT_PLUGIN;
+ break;
+ }
+ return result;
+}
diff --git a/apps/plugins/text_viewer/tv_menu.h b/apps/plugins/text_viewer/tv_menu.h
new file mode 100644
index 0000000000..35886d3c10
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_menu.h
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_MENU_H
+#define PLUGIN_TEXT_VIEWER_MENU_H
+
+enum tv_menu_result {
+ TV_MENU_RESULT_EXIT_MENU,
+ TV_MENU_RESULT_EXIT_PLUGIN,
+ TV_MENU_RESULT_ATTACHED_USB,
+};
+
+/*
+ * display the setting menu
+ *
+ * return
+ * the following value returns
+ * TV_MENU_RESULT_EXIT_MENU menu exit and continue this plugin
+ * TV_MENU_RESULT_EXIT_PLUGIN request to exit this plugin
+ * TV_MENU_RESULT_ATTACHED_USB connect USB cable
+ */
+enum tv_menu_result tv_display_menu(void);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_pager.c b/apps/plugins/text_viewer/tv_pager.c
new file mode 100644
index 0000000000..d98fe4627d
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_pager.c
@@ -0,0 +1,337 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_pager.h"
+#include "tv_preferences.h"
+#include "tv_reader.h"
+#include "tv_window.h"
+
+#if PLUGIN_BUFFER_SIZE < 0x13000
+#define TV_MAX_PAGE 999
+#else
+#define TV_MAX_PAGE 9999
+#endif
+
+#define TV_PAGER_MEMSIZE (4 * TV_MAX_PAGE)
+
+static unsigned char *pager_buffer;
+
+static struct tv_screen_pos cur_pos;
+
+static int parse_page;
+static int last_page;
+static int max_page;
+
+static int lines_per_page;
+
+static int line_pos[LCD_HEIGHT / 2];
+static int parse_page;
+static int parse_top_line;
+static int parse_lines;
+
+static void set_uint32(unsigned char *p, unsigned int val)
+{
+ *p++ = val & 0xff;
+ *p++ = (val >> 8) & 0xff;
+ *p++ = (val >> 16) & 0xff;
+ *p = (val >> 24) & 0xff;
+}
+
+static unsigned int get_uint32(const unsigned char *p)
+{
+ return (((((p[3] << 8) | p[2]) << 8) | p[1]) << 8) | p[0];
+}
+
+static void tv_set_fpos(int page, off_t pos)
+{
+ if (page >= 0 && page <= max_page)
+ set_uint32(pager_buffer + page * 4, pos);
+}
+
+static off_t tv_get_fpos(int page)
+{
+ if (page >= 0 && page <= max_page)
+ return get_uint32(pager_buffer + page * 4);
+ return 0;
+}
+
+static void tv_change_preferences(const struct tv_preferences *oldp)
+{
+ (void)oldp;
+
+ cur_pos.page = 0;
+ cur_pos.line = 0;
+ last_page = 0;
+ max_page = TV_MAX_PAGE - 1;
+ tv_set_fpos(cur_pos.page, 0);
+ tv_seek(0, SEEK_SET);
+}
+
+bool tv_init_pager(unsigned char *buf, size_t bufsize, size_t *used_size)
+{
+ if (bufsize < TV_PAGER_MEMSIZE)
+ return false;
+
+ pager_buffer = buf;
+ tv_set_screen_pos(&cur_pos);
+ tv_add_preferences_change_listner(tv_change_preferences);
+
+ /* valid page: 0, ..., max_page. */
+ max_page = TV_MAX_PAGE - 1;
+
+ line_pos[0] = 0;
+
+ buf += TV_PAGER_MEMSIZE;
+ bufsize -= TV_PAGER_MEMSIZE;
+ if (!tv_init_reader(buf, bufsize, used_size))
+ return false;
+
+ *used_size += TV_PAGER_MEMSIZE;
+
+ return true;
+}
+
+void tv_finalize_pager(void)
+{
+ tv_finalize_reader();
+}
+
+void tv_reset_line_positions(void)
+{
+ parse_page = cur_pos.page;
+ parse_top_line = cur_pos.line;
+ parse_lines = 0;
+}
+
+void tv_move_next_line(int size)
+{
+ if (!tv_is_eof())
+ {
+ tv_seek(size, SEEK_CUR);
+ cur_pos.file_pos = tv_get_current_file_pos();
+ cur_pos.line++;
+ parse_lines++;
+ line_pos[cur_pos.line] = line_pos[cur_pos.line - 1] + size;
+ }
+}
+
+int tv_get_line_positions(int offset)
+{
+ int line = cur_pos.line + offset;
+
+ if (line < 0)
+ line = 0;
+ else if (line > parse_lines)
+ line = parse_lines;
+
+ return line_pos[parse_top_line + line] - line_pos[parse_top_line];
+}
+
+void tv_new_page(void)
+{
+ parse_page = cur_pos.page;
+ if (cur_pos.page == last_page && last_page < max_page)
+ {
+ if (!tv_is_eof())
+ tv_set_fpos(++last_page, tv_get_current_file_pos());
+ else
+ max_page = last_page;
+ }
+
+ if (++cur_pos.page > max_page)
+ cur_pos.page = max_page;
+
+ lines_per_page = cur_pos.line;
+ cur_pos.line = 0;
+}
+
+static void tv_seek_page(int offset, int whence)
+{
+ int new_page = offset;
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ new_page += cur_pos.page;
+ break;
+ case SEEK_SET:
+ break;
+ case SEEK_END:
+ new_page += last_page;
+ whence = SEEK_SET;
+ break;
+ default:
+ return;
+ break;
+ }
+ if (new_page < 0)
+ new_page = 0;
+ else if (new_page >= last_page)
+ new_page = last_page;
+
+ cur_pos.page = new_page;
+ cur_pos.line = 0;
+ cur_pos.file_pos = tv_get_fpos(new_page);
+ tv_seek(cur_pos.file_pos, SEEK_SET);
+}
+
+static bool tv_create_line_positions(void)
+{
+ bool res;
+
+ if (tv_is_eof())
+ return false;
+
+ cur_pos.line = 0;
+ tv_reset_line_positions();
+ res = tv_traverse_lines();
+ lines_per_page = cur_pos.line;
+ tv_new_page();
+
+ return res;
+}
+
+void tv_convert_fpos(off_t fpos, struct tv_screen_pos *pos)
+{
+ int i;
+
+ for (i = 0; i < last_page; i++)
+ {
+ if (tv_get_fpos(i) <= fpos && tv_get_fpos(i + 1) > fpos)
+ break;
+ }
+
+ pos->page = i;
+ pos->line = 0;
+ pos->file_pos = fpos;
+
+ if (tv_get_fpos(i) == fpos)
+ return;
+
+ tv_seek_page(i, SEEK_SET);
+ while (tv_create_line_positions() && cur_pos.file_pos < fpos)
+ rb->splashf(0, "converting %ld%%...", 100 * cur_pos.file_pos / fpos);
+
+ if (cur_pos.page < max_page)
+ cur_pos.page--;
+ tv_seek_page(cur_pos.page, SEEK_SET);
+ for (i = 0; i < lines_per_page; i++)
+ {
+ if (cur_pos.file_pos + tv_get_line_positions(i) >= fpos)
+ break;
+ }
+
+ pos->page = cur_pos.page;
+ pos->line = i;
+}
+
+static void tv_seek_to_bottom_line(void)
+{
+ off_t total_size = tv_get_file_size();
+
+ tv_seek_page(0, SEEK_END);
+ while (tv_create_line_positions())
+ rb->splashf(0, "loading %ld%%...", 100 * cur_pos.file_pos / total_size);
+
+ cur_pos.line = lines_per_page - 1;
+}
+
+void tv_move_screen(int page_offset, int line_offset, int whence)
+{
+ struct tv_screen_pos new_pos;
+ int i;
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ cur_pos.page += page_offset;
+ cur_pos.line += line_offset;
+ break;
+ case SEEK_SET:
+ cur_pos.page = page_offset;
+ cur_pos.line = line_offset;
+ break;
+ case SEEK_END:
+ tv_seek_to_bottom_line();
+ cur_pos.page += page_offset;
+ cur_pos.line += line_offset;
+ break;
+ default:
+ return;
+ break;
+ }
+
+ if (cur_pos.page < 0 || (cur_pos.page == 0 && cur_pos.line < 0))
+ {
+ tv_seek_page(0, SEEK_SET);
+ return;
+ }
+ else if (cur_pos.page > max_page)
+ {
+ tv_seek_page(max_page, SEEK_SET);
+ return;
+ }
+
+ new_pos = cur_pos;
+ if (cur_pos.line < 0)
+ new_pos.page--;
+
+ tv_seek_page(new_pos.page, SEEK_SET);
+ while (cur_pos.page < new_pos.page && tv_create_line_positions())
+ rb->splashf(0, "loading %d%%...", 100 * cur_pos.page / new_pos.page);
+
+ if (new_pos.line == 0)
+ return;
+
+ if (parse_page == cur_pos.page)
+ {
+ if (cur_pos.page < max_page && new_pos.line == lines_per_page)
+ {
+ tv_seek(line_pos[lines_per_page], SEEK_CUR);
+ for (i = 0; i < parse_lines; i++)
+ line_pos[i] = line_pos[i + lines_per_page] - line_pos[lines_per_page];
+
+ cur_pos.page++;
+ cur_pos.line = 0;
+ parse_top_line = 0;
+ new_pos.line = 0;
+ }
+ }
+ else
+ {
+ tv_create_line_positions();
+ tv_seek_page(new_pos.page, SEEK_SET);
+ }
+
+ cur_pos.line = new_pos.line;
+ if (cur_pos.line >= lines_per_page)
+ cur_pos.line = lines_per_page - 1;
+ else if (cur_pos.line < 0)
+ {
+ cur_pos.line += lines_per_page;
+ if (cur_pos.line < 0)
+ cur_pos.line = 0;
+ }
+ tv_seek(line_pos[cur_pos.line], SEEK_CUR);
+ cur_pos.file_pos += line_pos[cur_pos.line];
+}
diff --git a/apps/plugins/text_viewer/tv_pager.h b/apps/plugins/text_viewer/tv_pager.h
new file mode 100644
index 0000000000..798d694ce5
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_pager.h
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_PAGER_H
+#define PLUGIN_TEXT_VIEWER_PAGER_H
+
+#include "tv_screen_pos.h"
+
+/* stuff for the paging */
+
+/*
+ * initialize the pager module
+ *
+ * [In] buf
+ * the start pointer of the buffer
+ *
+ * [In] size
+ * enabled buffer size
+ *
+ * [Out] used_size
+ * the size of the buffer which the pager uses
+ *
+ * return
+ * true initialize success
+ * false initialize failure
+ */
+bool tv_init_pager(unsigned char *buf, size_t bufsize, size_t *used_size);
+
+/* finalize the pager module */
+void tv_finalize_pager(void);
+
+/* reset the stored line positions */
+void tv_reset_line_positions(void);
+
+/*
+ * move to the next line
+ *
+ * [In] size
+ * the current line size
+ */
+void tv_move_next_line(int size);
+
+/*
+ * return the distance from the top of the current page
+ *
+ * [In] offset
+ * the difference between the current line
+ *
+ * return
+ * difference between the current line + offset from the top of the current page
+ */
+int tv_get_line_positions(int offset);
+
+/* change the new page */
+void tv_new_page(void);
+
+/*
+ * convert the given file position to the nearest the position (page, line)
+ *
+ * [In] fpos
+ * the file position which want to convert
+ *
+ * [Out] pos
+ * result
+ */
+void tv_convert_fpos(off_t fpos, struct tv_screen_pos *pos);
+
+/*
+ * move to the given page and line
+ *
+ * [In] page_offset
+ * page offset
+ *
+ * [In] line_offset
+ * line offset
+ *
+ * [In] whence
+ * SEEK_CUR seek to the current page + offset page, the current line + offset line.
+ * SEEK_SET seek to the offset page, offset line.
+ * SEEK_END seek to the reading last page + offset page,
+ * the last line of the reading last page + offset line.
+ */
+void tv_move_screen(int page_offset, int line_offset, int whence);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_preferences.c b/apps/plugins/text_viewer/tv_preferences.c
new file mode 100644
index 0000000000..cd3560db93
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_preferences.c
@@ -0,0 +1,119 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_preferences.h"
+
+static struct tv_preferences prefs;
+static bool is_initialized = false;
+static int listner_count = 0;
+
+#define TV_MAX_LISTNERS 4
+static void (*listners[TV_MAX_LISTNERS])(const struct tv_preferences *oldp);
+
+static void tv_notify_change_preferences(const struct tv_preferences *oldp,
+ const struct tv_preferences *newp)
+{
+ int i;
+
+ /*
+ * the following items do not check.
+ * - alignment
+ * - scroll_mode
+ * - page_mode
+ * - font
+ * - autoscroll_speed
+ */
+ if ((oldp == NULL) ||
+ (oldp->word_mode != newp->word_mode) ||
+ (oldp->line_mode != newp->line_mode) ||
+ (oldp->view_mode != newp->view_mode) ||
+ (oldp->scrollbar_mode != newp->scrollbar_mode) ||
+ (oldp->encoding != newp->encoding) ||
+#ifdef HAVE_LCD_BITMAP
+ (oldp->header_mode != newp->header_mode) ||
+ (oldp->footer_mode != newp->footer_mode) ||
+ (rb->strcmp(oldp->font_name, newp->font_name)) ||
+#endif
+ (rb->strcmp(oldp->file_name, newp->file_name)))
+ {
+ for (i = listner_count - 1; i >= 0; i--)
+ listners[i](oldp);
+ }
+}
+
+const struct tv_preferences *tv_get_preferences(void)
+{
+ return &prefs;
+}
+
+void tv_set_preferences(const struct tv_preferences *new_prefs)
+{
+ struct tv_preferences *oldp = NULL;
+ struct tv_preferences old_prefs;
+
+ if (!is_initialized)
+ is_initialized = true;
+ else
+ {
+ old_prefs = prefs;
+ oldp = &old_prefs;
+ }
+ rb->memcpy(&prefs, new_prefs, sizeof(struct tv_preferences));
+ tv_notify_change_preferences(oldp, &prefs);
+}
+
+void tv_copy_preferences(struct tv_preferences *copy_prefs)
+{
+ rb->memcpy(copy_prefs, &prefs, sizeof(struct tv_preferences));
+}
+
+void tv_set_default_preferences(struct tv_preferences *p)
+{
+ p->word_mode = WRAP;
+ p->line_mode = NORMAL;
+ p->view_mode = NARROW;
+ p->alignment = LEFT;
+ p->scroll_mode = PAGE;
+ p->page_mode = NO_OVERLAP;
+ p->scrollbar_mode = SB_OFF;
+ rb->memset(p->font_name, 0, MAX_PATH);
+#ifdef HAVE_LCD_BITMAP
+ p->header_mode = HD_BOTH;
+ p->footer_mode = FT_BOTH;
+ rb->strlcpy(p->font_name, rb->global_settings->font_file, MAX_PATH);
+ p->font = rb->font_get(FONT_UI);
+#else
+ p->header_mode = HD_NONE;
+ p->footer_mode = FT_NONE;
+#endif
+ p->autoscroll_speed = 1;
+ /* Set codepage to system default */
+ p->encoding = rb->global_settings->default_codepage;
+ p->file_name[0] = '\0';
+}
+
+void tv_add_preferences_change_listner(void (*listner)(const struct tv_preferences *oldp))
+{
+ if (listner_count < TV_MAX_LISTNERS)
+ listners[listner_count++] = listner;
+}
diff --git a/apps/plugins/text_viewer/tv_preferences.h b/apps/plugins/text_viewer/tv_preferences.h
new file mode 100644
index 0000000000..64ab0d102f
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_preferences.h
@@ -0,0 +1,129 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_PREFERENCES_H
+#define PLUGIN_TEXT_VIEWER_PREFERENCES_H
+
+struct tv_preferences {
+ enum {
+ WRAP = 0,
+ CHOP,
+ } word_mode;
+
+ enum {
+ NORMAL = 0,
+ JOIN,
+ EXPAND,
+ REFLOW,
+ } line_mode;
+
+ enum {
+ NARROW = 0,
+ WIDE,
+ } view_mode;
+
+ enum {
+ LEFT = 0,
+ RIGHT,
+ } alignment;
+
+ enum codepages encoding;
+
+ enum {
+ SB_OFF = 0,
+ SB_ON,
+ } scrollbar_mode;
+
+ enum {
+ NO_OVERLAP = 0,
+ OVERLAP,
+ } page_mode;
+
+ enum {
+ HD_NONE = 0,
+ HD_PATH,
+ HD_SBAR,
+ HD_BOTH,
+ } header_mode;
+
+ enum {
+ FT_NONE = 0,
+ FT_PAGE,
+ FT_SBAR,
+ FT_BOTH,
+ } footer_mode;
+
+ enum {
+ PAGE=0,
+ LINE,
+ } scroll_mode;
+
+ int autoscroll_speed;
+
+ unsigned char font_name[MAX_PATH];
+#ifdef HAVE_LCD_BITMAP
+ struct font *font;
+#endif
+ unsigned char file_name[MAX_PATH];
+};
+
+/*
+ * return the preferences
+ *
+ * return
+ * the pointer the preferences
+ */
+const struct tv_preferences *tv_get_preferences(void);
+
+/*
+ * change the preferences
+ *
+ * [In] new_prefs
+ * new preferences
+ */
+void tv_set_preferences(const struct tv_preferences *new_prefs);
+
+/*
+ * copy the preferences
+ *
+ * [Out] copy_prefs
+ * the preferences in copy destination
+ */
+void tv_copy_preferences(struct tv_preferences *copy_prefs);
+
+/*
+ * set the default settings
+ *
+ * [Out] p
+ * the preferences which store the default settings
+ */
+void tv_set_default_preferences(struct tv_preferences *p);
+
+/*
+ * register the function to be executed when the current preferences is changed
+ *
+ * [In] listner
+ * the function to be executed when the current preferences is changed
+ */
+void tv_add_preferences_change_listner(void (*listner)(const struct tv_preferences *oldp));
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_reader.c b/apps/plugins/text_viewer/tv_reader.c
new file mode 100644
index 0000000000..6dc66ef567
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_reader.c
@@ -0,0 +1,191 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_preferences.h"
+#include "tv_reader.h"
+
+#if PLUGIN_BUFFER_SIZE < 0x10000
+#define TV_MIN_BLOCK_SIZE 0x800
+#else
+#define TV_MIN_BLOCK_SIZE 0x1000
+#endif
+
+/* UTF-8 BOM */
+#define BOM "\xef\xbb\xbf"
+#define BOM_SIZE 3
+
+static int fd = -1;
+
+static off_t file_pos;
+static off_t start_file_pos;
+
+static off_t file_size;
+
+static unsigned char *reader_buffer;
+static ssize_t buffer_size;
+static ssize_t block_size;
+
+static ssize_t buf_pos;
+static ssize_t read_size;
+
+off_t tv_get_file_size(void)
+{
+ return file_size;
+}
+
+bool tv_is_eof(void)
+{
+ return (file_pos + buf_pos >= file_size);
+}
+
+off_t tv_get_current_file_pos(void)
+{
+ return file_pos + buf_pos;
+}
+
+const unsigned char *tv_get_buffer(ssize_t *bufsize)
+{
+ *bufsize = read_size - buf_pos;
+ return reader_buffer + buf_pos;
+}
+
+static ssize_t tv_read(unsigned char *buf, ssize_t reqsize)
+{
+ if (buf - reader_buffer + reqsize > buffer_size)
+ reqsize = buffer_size - (buf - reader_buffer);
+
+ return rb->read(fd, buf, reqsize);
+}
+
+void tv_seek(off_t offset, int whence)
+{
+ ssize_t size;
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ if (offset >= file_pos && offset < file_pos + read_size)
+ {
+ buf_pos = offset - file_pos;
+ return;
+ }
+ file_pos = offset;
+ break;
+
+ case SEEK_CUR:
+ buf_pos += offset;
+ if (buf_pos >= 0 && buf_pos < read_size)
+ {
+ if (buf_pos > block_size)
+ {
+ buf_pos -= block_size;
+ file_pos += block_size;
+ size = read_size - block_size;
+ rb->memcpy(reader_buffer, reader_buffer + block_size, size);
+ read_size = tv_read(reader_buffer + block_size, block_size);
+ if (read_size < 0)
+ read_size = 0;
+
+ read_size += size;
+ }
+ return;
+ }
+ file_pos += buf_pos;
+ whence = SEEK_SET;
+ break;
+
+ default:
+ return;
+ break;
+ }
+
+ if (whence == SEEK_SET)
+ {
+ if (file_pos < 0)
+ file_pos = 0;
+ else if (file_pos > file_size)
+ file_pos = file_size;
+
+ rb->lseek(fd, file_pos + start_file_pos, SEEK_SET);
+ buf_pos = 0;
+ read_size = tv_read(reader_buffer, buffer_size);
+ }
+}
+
+static void tv_change_preferences(const struct tv_preferences *oldp)
+{
+ unsigned char bom[BOM_SIZE];
+ const struct tv_preferences *prefs = tv_get_preferences();
+ int cur_start_file_pos = start_file_pos;
+ off_t cur_file_pos = file_pos + buf_pos;
+
+ file_pos = 0;
+ buf_pos = 0;
+ read_size = 0;
+ start_file_pos = 0;
+
+ /* open the new file */
+ if (oldp == NULL || rb->strcmp(oldp->file_name, prefs->file_name))
+ {
+ if (fd >= 0)
+ rb->close(fd);
+
+ fd = rb->open(prefs->file_name, O_RDONLY);
+ if (fd < 0)
+ return;
+ }
+
+ /*
+ * When a file is UTF-8 file with BOM, if prefs.encoding is UTF-8,
+ * then file size decreases only BOM_SIZE.
+ */
+ if (prefs->encoding == UTF_8)
+ {
+ rb->lseek(fd, 0, SEEK_SET);
+ rb->read(fd, bom, BOM_SIZE);
+ if (rb->memcmp(bom, BOM, BOM_SIZE) == 0)
+ start_file_pos = BOM_SIZE;
+ }
+
+ file_size = rb->filesize(fd) - start_file_pos;
+ tv_seek(cur_file_pos + cur_start_file_pos - start_file_pos, SEEK_SET);
+}
+
+bool tv_init_reader(unsigned char *buf, size_t bufsize, size_t *used_size)
+{
+ if (bufsize < 2 * TV_MIN_BLOCK_SIZE)
+ return false;
+
+ reader_buffer = buf;
+ block_size = bufsize / 2;
+ buffer_size = 2 * block_size;
+ *used_size = buffer_size;
+ tv_add_preferences_change_listner(tv_change_preferences);
+ return true;
+}
+
+void tv_finalize_reader(void)
+{
+ if (fd >= 0)
+ rb->close(fd);
+}
diff --git a/apps/plugins/text_viewer/tv_reader.h b/apps/plugins/text_viewer/tv_reader.h
new file mode 100644
index 0000000000..464af1027a
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_reader.h
@@ -0,0 +1,102 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_READER_H
+#define PLUGIN_TEXT_VIEWER_READER_H
+
+/* stuff for the reading file */
+
+/*
+ * initialize the reader module
+ *
+ * [In] buf
+ * the start pointer of the buffer
+ *
+ * [In] size
+ * enabled buffer size
+ *
+ * [Out] used_size
+ * the size of the buffer which the pager uses
+ *
+ * return
+ * true initialize success
+ * false initialize failure
+ */
+bool tv_init_reader(unsigned char *buf, size_t bufsize, size_t *used_size);
+
+/* finalize the reader module */
+void tv_finalize_reader(void);
+
+/*
+ * return the file size
+ *
+ * return
+ * file size
+ *
+ * Note: when the file is UTF-8 file with BOM, if the encoding of the text viewer is UTF-8,
+ * then file size decreases only BOM size.
+ */
+off_t tv_get_file_size(void);
+
+/*
+ * return the whether is the end of file or not
+ *
+ * return
+ * true EOF
+ * false not EOF
+ */
+bool tv_is_eof(void);
+
+/*
+ * return the current file position
+ *
+ * return
+ * the current file position
+ */
+off_t tv_get_current_file_pos(void);
+
+/*
+ * return the bufer which store text data
+ *
+ * [Out] bufsize
+ * buffer size
+ *
+ * return
+ * the pointer of the buffer
+ */
+const unsigned char *tv_get_buffer(ssize_t *bufsize);
+
+/*
+ * seek to the given offset
+ *
+ * [In] offset
+ * offset size
+ *
+ * [In] whence
+ * SEEK_CUR seek to the current position + offset.
+ * SEEK_SET seek to the offset.
+ *
+ * Note: whence supports SEEK_CUR and SEEK_SET only.
+ */
+void tv_seek(off_t offset, int whence);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_screen_pos.c b/apps/plugins/text_viewer/tv_screen_pos.c
new file mode 100644
index 0000000000..001bd478de
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_screen_pos.c
@@ -0,0 +1,41 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_screen_pos.h"
+
+static const struct tv_screen_pos *screen_pos;
+
+const struct tv_screen_pos *tv_get_screen_pos(void)
+{
+ return screen_pos;
+}
+
+void tv_set_screen_pos(const struct tv_screen_pos *pos)
+{
+ screen_pos = pos;
+}
+
+void tv_copy_screen_pos(struct tv_screen_pos *pos)
+{
+ *pos = *screen_pos;
+}
diff --git a/apps/plugins/text_viewer/tv_screen_pos.h b/apps/plugins/text_viewer/tv_screen_pos.h
new file mode 100644
index 0000000000..3ee8c10d89
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_screen_pos.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_SCREEN_POS_H
+#define PLUGIN_TEXT_VIEWER_SCREEN_POS_H
+
+struct tv_screen_pos {
+ off_t file_pos;
+ int page;
+ int line;
+};
+
+/*
+ * return the current position
+ *
+ * return
+ * the pointer which is stored the current position
+ */
+const struct tv_screen_pos *tv_get_screen_pos(void);
+
+/*
+ * set the current position
+ *
+ * [In] pos
+ * the pointer which is stored the current position
+ */
+void tv_set_screen_pos(const struct tv_screen_pos *pos);
+
+/*
+ * the current position set to the given structure
+ *
+ * [Out] pos
+ * the pointer in order to store the current position
+ */
+void tv_copy_screen_pos(struct tv_screen_pos *pos);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_settings.c b/apps/plugins/text_viewer/tv_settings.c
new file mode 100644
index 0000000000..bb4ead7b5d
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_settings.c
@@ -0,0 +1,519 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_bookmark.h"
+#include "tv_reader.h"
+#include "tv_settings.h"
+
+/* global settings file
+ * binary file, so dont use .cfg
+ *
+ * setting file format
+ *
+ * part byte count
+ * --------------------------------
+ * 'TVGS' 4
+ * version 1
+ * word_mode 1
+ * line_mode 1
+ * view_mode 1
+ * alignment 1
+ * encoding 1
+ * scrollbar_mode 1
+ * (unused) 1 (for compatibility)
+ * page_mode 1
+ * page_number_mode 1
+ * title_mode 1
+ * scroll_mode 1
+ * autoscroll_speed 1
+ * font name MAX_PATH
+ */
+
+#define VIEWER_GLOBAL_SETTINGS_FILE VIEWERS_DIR "/viewer.dat"
+#define TV_GLOBAL_SETTINGS_FILE VIEWERS_DIR "/tv_global.dat"
+
+#define TV_GLOBAL_SETTINGS_HEADER "\x54\x56\x47\x53" /* "TVGS" */
+#define TV_GLOBAL_SETTINGS_VERSION 0x32
+#define TV_GLOBAL_SETTINGS_HEADER_SIZE 5
+#define TV_GLOBAL_SETTINGS_FIRST_VERSION 0x31
+
+/* preferences and bookmarks at each file
+ * binary file, so dont use .cfg
+ *
+ * setting file format
+ *
+ * part byte count
+ * --------------------------------
+ * 'TVS' 3
+ * version 1
+ * file count 2
+ * [1st file]
+ * file path MAX_PATH
+ * next file pos 2 (prefences size + bookmark count * bookmark size + 1)
+ * [preferences]
+ * word_mode 1
+ * line_mode 1
+ * view_mode 1
+ * alignment 1
+ * encoding 1
+ * scrollbar_mode 1
+ * (unused) 1 (for compatibility)
+ * page_mode 1
+ * header_mode 1
+ * footer_mode 1
+ * scroll_mode 1
+ * autoscroll_speed 1
+ * font name MAX_PATH
+ * bookmark count 1
+ * [1st bookmark]
+ * file_position 4
+ * page 2
+ * line 1
+ * flag 1
+ * [2nd bookmark]
+ * ...
+ * [last bookmark]
+ * [2nd file]
+ * ...
+ * [last file]
+ */
+#define VIEWER_SETTINGS_FILE VIEWERS_DIR "/viewer_file.dat"
+#define TV_SETTINGS_FILE VIEWERS_DIR "/tv_file.dat"
+
+/* temporary file */
+#define TV_SETTINGS_TMP_FILE VIEWERS_DIR "/tv_file.tmp"
+
+#define TV_SETTINGS_HEADER "\x54\x56\x53" /* "TVS" */
+#define TV_SETTINGS_VERSION 0x33
+#define TV_SETTINGS_HEADER_SIZE 4
+#define TV_SETTINGS_FIRST_VERSION 0x32
+
+#define TV_PREFERENCES_SIZE (12 + MAX_PATH)
+
+/* ----------------------------------------------------------------------------
+ * read/write the preferences
+ * ----------------------------------------------------------------------------
+ */
+
+static bool tv_read_preferences(int pfd, int version, struct tv_preferences *prefs)
+{
+ unsigned char buf[TV_PREFERENCES_SIZE];
+ const unsigned char *p = buf;
+ int read_size = TV_PREFERENCES_SIZE;
+
+ if (version == 0)
+ read_size--;
+
+ if (rb->read(pfd, buf, read_size) < 0)
+ return false;
+
+ prefs->word_mode = *p++;
+ prefs->line_mode = *p++;
+ prefs->view_mode = *p++;
+ if (version > 0)
+ prefs->alignment = *p++;
+ else
+ prefs->alignment = LEFT;
+ prefs->encoding = *p++;
+ prefs->scrollbar_mode = *p++;
+ /* skip need_scrollbar */
+ p++;
+ prefs->page_mode = *p++;
+ prefs->header_mode = *p++;
+ prefs->footer_mode = *p++;
+ prefs->scroll_mode = *p++;
+ prefs->autoscroll_speed = *p++;
+ rb->memcpy(prefs->font_name, p, MAX_PATH);
+#ifdef HAVE_LCD_BITMAP
+ prefs->font = rb->font_get(FONT_UI);
+#endif
+
+ return true;
+}
+
+static bool tv_write_preferences(int pfd, const struct tv_preferences *prefs)
+{
+ unsigned char buf[TV_PREFERENCES_SIZE];
+ unsigned char *p = buf;
+
+ *p++ = prefs->word_mode;
+ *p++ = prefs->line_mode;
+ *p++ = prefs->view_mode;
+ *p++ = prefs->alignment;
+ *p++ = prefs->encoding;
+ *p++ = prefs->scrollbar_mode;
+ /* skip need_scrollbar */
+ p++;
+ *p++ = prefs->page_mode;
+ *p++ = prefs->header_mode;
+ *p++ = prefs->footer_mode;
+ *p++ = prefs->scroll_mode;
+ *p++ = prefs->autoscroll_speed;
+ rb->memcpy(p, prefs->font_name, MAX_PATH);
+
+ return (rb->write(pfd, buf, TV_PREFERENCES_SIZE) >= 0);
+}
+
+/* ----------------------------------------------------------------------------
+ * convert vewer.rock's settings file to text_viewer.rock's settings file
+ * ----------------------------------------------------------------------------
+ */
+
+static bool tv_convert_settings(int sfd, int dfd, int old_ver)
+{
+ struct tv_preferences new_prefs;
+ off_t old_pos;
+ off_t new_pos;
+ unsigned char buf[MAX_PATH + 2];
+ int settings_size;
+
+ rb->read(sfd, buf, MAX_PATH + 2);
+ rb->write(dfd, buf, MAX_PATH + 2);
+
+ settings_size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
+
+ old_pos = rb->lseek(sfd, 0, SEEK_CUR);
+ new_pos = rb->lseek(dfd, 0, SEEK_CUR);
+
+ /*
+ * when the settings size != preferences size + bookmarks size,
+ * settings data are considered to be old version.
+ */
+ if (old_ver > 0 && ((settings_size - TV_PREFERENCES_SIZE) % 8) == 0)
+ old_ver = 0;
+
+ if (!tv_read_preferences(sfd, old_ver, &new_prefs))
+ return false;
+
+ if (!tv_write_preferences(dfd, &new_prefs))
+ return false;
+
+ settings_size -= (rb->lseek(sfd, 0, SEEK_CUR) - old_pos);
+
+ if (settings_size > 0)
+ {
+ rb->read(sfd, buf, settings_size);
+ rb->write(dfd, buf, settings_size);
+ }
+
+ settings_size = rb->lseek(dfd, 0, SEEK_CUR) - new_pos;
+ buf[0] = settings_size >> 8;
+ buf[1] = settings_size;
+ rb->lseek(dfd, new_pos - 2, SEEK_SET);
+ rb->write(dfd, buf, 2);
+ rb->lseek(dfd, settings_size, SEEK_CUR);
+ return true;
+}
+
+static void tv_convert_settings_file(void)
+{
+ unsigned char buf[TV_SETTINGS_HEADER_SIZE + 2];
+ int sfd;
+ int tfd;
+ int i;
+ int fcount;
+ int version;
+ bool res = false;
+
+ if ((sfd = rb->open(VIEWER_SETTINGS_FILE, O_RDONLY)) < 0)
+ return;
+
+ if ((tfd = rb->open(TV_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ {
+ rb->close(sfd);
+ return;
+ }
+
+ if (rb->read(sfd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0)
+ {
+ version = buf[TV_SETTINGS_HEADER_SIZE - 1] - TV_SETTINGS_FIRST_VERSION;
+ fcount = (buf[TV_SETTINGS_HEADER_SIZE] << 8) | buf[TV_SETTINGS_HEADER_SIZE + 1];
+ buf[TV_SETTINGS_HEADER_SIZE - 1] = TV_SETTINGS_VERSION;
+
+ if (rb->write(tfd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0)
+ {
+ res = true;
+ for (i = 0; i < fcount; i++)
+ {
+ if (!tv_convert_settings(sfd, tfd, version))
+ {
+ res = false;
+ break;
+ }
+ }
+ }
+ }
+
+ rb->close(sfd);
+ rb->close(tfd);
+
+ if (res)
+ rb->rename(TV_SETTINGS_TMP_FILE, TV_SETTINGS_FILE);
+ else
+ rb->remove(TV_SETTINGS_TMP_FILE);
+
+ return;
+}
+
+/* ----------------------------------------------------------------------------
+ * load/save the global settings
+ * ----------------------------------------------------------------------------
+ */
+
+bool tv_load_global_settings(struct tv_preferences *prefs)
+{
+ unsigned char buf[TV_GLOBAL_SETTINGS_HEADER_SIZE];
+ int fd;
+ int version;
+ bool res = false;
+
+ /*
+ * the viewer.rock's setting file read when the text_viewer.rock's setting file
+ * does not read.
+ */
+ if ((fd = rb->open(TV_GLOBAL_SETTINGS_FILE, O_RDONLY)) < 0)
+ fd = rb->open(VIEWER_GLOBAL_SETTINGS_FILE, O_RDONLY);
+
+ if (fd >= 0)
+ {
+ if ((rb->read(fd, buf, TV_GLOBAL_SETTINGS_HEADER_SIZE) > 0) &&
+ (rb->memcmp(buf, TV_GLOBAL_SETTINGS_HEADER, TV_GLOBAL_SETTINGS_HEADER_SIZE - 1) == 0))
+ {
+ version = buf[TV_GLOBAL_SETTINGS_HEADER_SIZE - 1] - TV_GLOBAL_SETTINGS_FIRST_VERSION;
+ res = tv_read_preferences(fd, version, prefs);
+ }
+ rb->close(fd);
+ }
+ return res;
+}
+
+bool tv_save_global_settings(const struct tv_preferences *prefs)
+{
+ unsigned char buf[TV_GLOBAL_SETTINGS_HEADER_SIZE];
+ int fd;
+ bool res;
+
+ if ((fd = rb->open(TV_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ return false;
+
+ rb->memcpy(buf, TV_GLOBAL_SETTINGS_HEADER, TV_GLOBAL_SETTINGS_HEADER_SIZE - 1);
+ buf[TV_GLOBAL_SETTINGS_HEADER_SIZE - 1] = TV_GLOBAL_SETTINGS_VERSION;
+
+ res = (rb->write(fd, buf, TV_GLOBAL_SETTINGS_HEADER_SIZE) >= 0) &&
+ (tv_write_preferences(fd, prefs));
+ rb->close(fd);
+
+ if (res)
+ {
+ rb->remove(TV_GLOBAL_SETTINGS_FILE);
+ rb->rename(TV_SETTINGS_TMP_FILE, TV_GLOBAL_SETTINGS_FILE);
+ }
+ else
+ rb->remove(TV_SETTINGS_TMP_FILE);
+
+ return res;
+}
+
+/* ----------------------------------------------------------------------------
+ * load/save the settings
+ * ----------------------------------------------------------------------------
+ */
+
+void tv_load_settings(const unsigned char *file_name)
+{
+ unsigned char buf[MAX_PATH+2];
+ unsigned int fcount;
+ unsigned int i;
+ bool res = false;
+ int fd;
+ int version;
+ unsigned int size;
+ struct tv_preferences prefs;
+
+ if (!rb->file_exists(TV_SETTINGS_FILE))
+ tv_convert_settings_file();
+
+ if ((fd = rb->open(TV_SETTINGS_FILE, O_RDONLY)) >= 0)
+ {
+ if ((rb->read(fd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0) &&
+ (rb->memcmp(buf, TV_SETTINGS_HEADER, TV_SETTINGS_HEADER_SIZE - 1) == 0))
+ {
+ version = buf[TV_SETTINGS_HEADER_SIZE - 1] - TV_SETTINGS_FIRST_VERSION;
+ fcount = (buf[TV_SETTINGS_HEADER_SIZE] << 8) | buf[TV_SETTINGS_HEADER_SIZE+1];
+
+ for (i = 0; i < fcount; i++)
+ {
+ if (rb->read(fd, buf, MAX_PATH+2) >= 0)
+ {
+ size = (buf[MAX_PATH] << 8) | buf[MAX_PATH+1];
+ if (rb->strcmp(buf, file_name) == 0)
+ {
+ if (tv_read_preferences(fd, version, &prefs))
+ res = tv_deserialize_bookmarks(fd);
+
+ break;
+ }
+ rb->lseek(fd, size, SEEK_CUR);
+ }
+ }
+ rb->close(fd);
+ }
+ else
+ {
+ /* when the settings file is illegal, removes it */
+ rb->close(fd);
+ rb->remove(TV_SETTINGS_FILE);
+ }
+ }
+ if (!res)
+ {
+ /* specifications are acquired from the global settings */
+ if (!tv_load_global_settings(&prefs))
+ tv_set_default_preferences(&prefs);
+ }
+ rb->strlcpy(prefs.file_name, file_name, MAX_PATH);
+ tv_set_preferences(&prefs);
+}
+
+static bool tv_copy_settings(int sfd, int dfd, int size)
+{
+ unsigned char buf[MAX_PATH];
+ int i = size / MAX_PATH;
+
+ size %= MAX_PATH;
+
+ while (i--)
+ {
+ if ((rb->read(sfd, buf, MAX_PATH) < 0) || (rb->write(dfd, buf, MAX_PATH) < 0))
+ return false;
+ }
+
+ return ((rb->read(sfd, buf, size) >= 0) && (rb->write(dfd, buf, size) >= 0));
+}
+
+bool tv_save_settings(void)
+{
+ const struct tv_preferences *prefs = tv_get_preferences();
+ unsigned char buf[MAX_PATH+2];
+ unsigned int fcount = 0;
+ unsigned int i;
+ int ofd = -1;
+ int tfd;
+ off_t size;
+ bool res = true;
+
+ /* add reading page to bookmarks */
+ tv_create_system_bookmark();
+
+ if (!rb->file_exists(TV_SETTINGS_FILE))
+ tv_convert_settings_file();
+
+ /* create header for the temporary file */
+ rb->memcpy(buf, TV_SETTINGS_HEADER, TV_SETTINGS_HEADER_SIZE - 1);
+ buf[TV_SETTINGS_HEADER_SIZE - 1] = TV_SETTINGS_VERSION;
+
+ if ((tfd = rb->open(TV_SETTINGS_TMP_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0)
+ return false;
+
+ if (rb->write(tfd, buf, TV_SETTINGS_HEADER_SIZE + 2) < 0)
+ {
+ rb->close(tfd);
+ return false;
+ }
+
+ if ((ofd = rb->open(TV_SETTINGS_FILE, O_RDONLY)) >= 0)
+ {
+ res = ((rb->read(ofd, buf, TV_SETTINGS_HEADER_SIZE + 2) >= 0) &&
+ (rb->memcmp(buf, TV_SETTINGS_HEADER, TV_SETTINGS_HEADER_SIZE - 1) == 0));
+
+ if (res)
+ {
+ fcount = (buf[TV_SETTINGS_HEADER_SIZE] << 8) | buf[TV_SETTINGS_HEADER_SIZE + 1];
+ for (i = 0; i < fcount; i++)
+ {
+ if (rb->read(ofd, buf, MAX_PATH + 2) < 0)
+ {
+ res = false;
+ break;
+ }
+
+ size = (buf[MAX_PATH] << 8) | buf[MAX_PATH + 1];
+ if (rb->strcmp(buf, prefs->file_name) == 0)
+ rb->lseek(ofd, size, SEEK_CUR);
+ else
+ {
+ if ((rb->write(tfd, buf, MAX_PATH + 2) < 0) ||
+ (!tv_copy_settings(ofd, tfd, size)))
+ {
+ res = false;
+ break;
+ }
+ }
+ }
+ }
+ rb->close(ofd);
+ }
+
+ if (res)
+ {
+ /* save to current read file's preferences and bookmarks */
+ res = false;
+ rb->memset(buf, 0, MAX_PATH);
+ rb->strlcpy(buf, prefs->file_name, MAX_PATH);
+
+ if (rb->write(tfd, buf, MAX_PATH + 2) >= 0)
+ {
+ if (tv_write_preferences(tfd, prefs))
+ {
+ size = tv_serialize_bookmarks(tfd);
+ if (size > 0)
+ {
+ size += TV_PREFERENCES_SIZE;
+ rb->lseek(tfd, -size - 2, SEEK_CUR);
+ buf[0] = size >> 8;
+ buf[1] = size;
+ if (rb->write(tfd, buf, 2) >= 0)
+ {
+ rb->lseek(tfd, TV_SETTINGS_HEADER_SIZE, SEEK_SET);
+
+ fcount++;
+ buf[0] = fcount >> 8;
+ buf[1] = fcount;
+ res = (rb->write(tfd, buf, 2) >= 0);
+ }
+ }
+ }
+ }
+ }
+ rb->close(tfd);
+
+ if (res)
+ {
+ rb->remove(TV_SETTINGS_FILE);
+ rb->rename(TV_SETTINGS_TMP_FILE, TV_SETTINGS_FILE);
+ }
+ else
+ rb->remove(TV_SETTINGS_TMP_FILE);
+
+ return res;
+}
diff --git a/apps/plugins/text_viewer/tv_settings.h b/apps/plugins/text_viewer/tv_settings.h
new file mode 100644
index 0000000000..c2218e7d9f
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_settings.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_SETTINGS_H
+#define PLUGIN_TEXT_VIEWER_SETTINGS_H
+
+#include "tv_preferences.h"
+
+/*
+ * load from the global settings file
+ *
+ * [Out] prefs
+ * the structure which read settings is stored
+ *
+ * return
+ * true success
+ * false failure
+ */
+bool tv_load_global_settings(struct tv_preferences *prefs);
+
+/*
+ * save to the global settings file
+ *
+ * [In] prefs
+ * the structure with settings to save
+ *
+ * return
+ * true success
+ * false failure
+ */
+bool tv_save_global_settings(const struct tv_preferences *prefs);
+
+/*
+ * load the settings at each file
+ *
+ * [In] file_name
+ * the file name of file that wants to read settings
+ *
+ * return
+ * true success
+ * false failure
+ */
+void tv_load_settings(const unsigned char *file_name);
+
+/*
+ * save the settings at each file
+ *
+ * return
+ * true success
+ * false failure
+ */
+bool tv_save_settings(void);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_text_processor.c b/apps/plugins/text_viewer/tv_text_processor.c
new file mode 100644
index 0000000000..8cdd78df15
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_text_processor.c
@@ -0,0 +1,576 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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 "ctype.h"
+#include "tv_preferences.h"
+#include "tv_text_processor.h"
+
+enum tv_text_type {
+ TV_TEXT_UNKNOWN,
+ TV_TEXT_MAC,
+ TV_TEXT_UNIX,
+ TV_TEXT_WIN,
+};
+
+/* the max characters of each blocks */
+#ifdef HAVE_LCD_BITMAP
+#define TV_MAX_CHARS_PER_BLOCK (LCD_WIDTH / 2 + 1)
+#else
+#define TV_MAX_CHARS_PER_BLOCK (LCD_WIDTH + 1)
+#endif
+
+#define TV_MAX_BLOCKS 2
+
+/*
+ * number of spaces to indent first paragraph
+ * (this value uses the line mode is REFLOW only)
+ */
+#define TV_INDENT_SPACES 2
+
+static const struct tv_preferences *prefs;
+static enum tv_text_type text_type = TV_TEXT_UNKNOWN;
+
+static const unsigned char *end_ptr;
+
+static unsigned short *ucsbuf[TV_MAX_BLOCKS];
+static unsigned char *utf8buf;
+static unsigned char *outbuf;
+
+static int block_count;
+static int block_width;
+
+/* if this value is true, then tv_create_line_text returns a blank line. */
+static bool expand_extra_line = false;
+
+/* when a line is divided, this value sets true. */
+static bool is_break_line = false;
+
+static unsigned short break_chars[] =
+ {
+ 0,
+ /* halfwidth characters */
+ '\t', '\n', 0x0b, 0x0c, ' ', '!', ',', '-', '.', ':', ';', '?', 0xb7,
+ /* fullwidth characters */
+ 0x2010, /* hyphen */
+ 0x3000, /* fullwidth space */
+ 0x3001, /* ideographic comma */
+ 0x3002, /* ideographic full stop */
+ 0x30fb, /* katakana middle dot */
+ 0x30fc, /* katakana-hiragana prolonged sound mark */
+ 0xff01, /* fullwidth exclamation mark */
+ 0xff0c, /* fullwidth comma */
+ 0xff0d, /* fullwidth hyphen-minus */
+ 0xff0e, /* fullwidth full stop */
+ 0xff1a, /* fullwidth colon */
+ 0xff1b, /* fullwidth semicolon */
+ 0xff1f, /* fullwidth question mark */
+ };
+
+/* the characters which is not judged as space with isspace() */
+static unsigned short extra_spaces[] = { 0, 0x3000 };
+
+static int tv_glyph_width(int ch)
+{
+ if (ch == '\n')
+ return 0;
+
+ if (ch == 0)
+ ch = ' ';
+
+#ifdef HAVE_LCD_BITMAP
+ /* the width of the diacritics charcter is 0 */
+ if (rb->is_diacritic(ch, NULL))
+ return 0;
+
+ return rb->font_get_width(prefs->font, ch);
+#else
+ return 1;
+#endif
+}
+
+static unsigned char *tv_get_ucs(const unsigned char *str, unsigned short *ch)
+{
+ int count = 1;
+ unsigned char utf8_tmp[3];
+
+ /* distinguish the text_type */
+ if (*str == '\r')
+ {
+ if (text_type == TV_TEXT_WIN || text_type == TV_TEXT_UNKNOWN)
+ {
+ if (str + 1 < end_ptr && *(str+1) == '\n')
+ {
+ if (text_type == TV_TEXT_UNKNOWN)
+ text_type = TV_TEXT_WIN;
+
+ *ch = '\n';
+ return (unsigned char *)str + 2;
+ }
+
+ if (text_type == TV_TEXT_UNKNOWN)
+ text_type = TV_TEXT_MAC;
+ }
+ *ch = (text_type == TV_TEXT_MAC)? '\n' : ' ';
+ return (unsigned char *)str + 1;
+ }
+ else if (*str == '\n')
+ {
+ if (text_type == TV_TEXT_UNKNOWN)
+ text_type = TV_TEXT_UNIX;
+
+ *ch = (text_type == TV_TEXT_UNIX)? '\n' : ' ';
+ return (unsigned char *)str + 1;
+ }
+
+ if (prefs->encoding == UTF_8)
+ return (unsigned char*)rb->utf8decode(str, ch);
+
+#ifdef HAVE_LCD_BITMAP
+ if ((*str >= 0x80) &&
+ ((prefs->encoding > SJIS) ||
+ (prefs->encoding == SJIS && (*str <= 0xa0 || *str >= 0xe0))))
+ {
+ if (str + 1 >= end_ptr)
+ {
+ end_ptr = str;
+ *ch = 0;
+ return (unsigned char *)str;
+ }
+ count = 2;
+ }
+#endif
+ rb->iso_decode(str, utf8_tmp, prefs->encoding, count);
+ rb->utf8decode(utf8_tmp, ch);
+ return (unsigned char *)str + count;
+}
+
+static void tv_decode2utf8(const unsigned short *ucs, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++)
+ outbuf = rb->utf8encode(ucs[i], outbuf);
+
+ *outbuf = '\0';
+}
+
+static bool tv_is_line_break_char(unsigned short ch)
+{
+ size_t i;
+
+ /* when the word mode is CHOP, all characters does not break line. */
+ if (prefs->word_mode == CHOP)
+ return false;
+
+ for (i = 0; i < sizeof(break_chars); i++)
+ {
+ if (break_chars[i] == ch)
+ return true;
+ }
+ return false;
+}
+
+static bool tv_isspace(unsigned short ch)
+{
+ size_t i;
+
+ if (ch < 128 && isspace(ch))
+ return true;
+
+ for (i = 0; i < sizeof(extra_spaces); i++)
+ {
+ if (extra_spaces[i] == ch)
+ return true;
+ }
+ return false;
+}
+
+static bool tv_is_break_line_join_mode(const unsigned char *next_str)
+{
+ unsigned short ch;
+
+ tv_get_ucs(next_str, &ch);
+ return tv_isspace(ch);
+}
+
+static int tv_form_reflow_line(unsigned short *ucs, int chars)
+{
+ unsigned short new_ucs[TV_MAX_CHARS_PER_BLOCK];
+ unsigned short *p = new_ucs;
+ unsigned short ch;
+ int i;
+ int k;
+ int expand_spaces;
+ int indent_chars = 0;
+ int nonspace_chars = 0;
+ int nonspace_width = 0;
+ int remain_spaces;
+ int spaces = 0;
+ int words_spaces;
+
+ if (prefs->alignment == LEFT)
+ {
+ while (chars > 0 && ucs[chars-1] == ' ')
+ chars--;
+ }
+
+ if (chars == 0)
+ return 0;
+
+ while (ucs[indent_chars] == ' ')
+ indent_chars++;
+
+ for (i = indent_chars; i < chars; i++)
+ {
+ ch = ucs[i];
+ if (ch == ' ')
+ spaces++;
+ else
+ {
+ nonspace_chars++;
+ nonspace_width += tv_glyph_width(ch);
+ }
+ }
+
+ if (spaces == 0)
+ return chars;
+
+ expand_spaces = (block_width - nonspace_width) / tv_glyph_width(' ') - indent_chars;
+ if (indent_chars + nonspace_chars + expand_spaces > TV_MAX_CHARS_PER_BLOCK)
+ expand_spaces = TV_MAX_CHARS_PER_BLOCK - indent_chars - nonspace_chars;
+
+ words_spaces = expand_spaces / spaces;
+ remain_spaces = expand_spaces - words_spaces * spaces;
+
+ for (i = 0; i < indent_chars; i++)
+ *p++ = ' ';
+
+ for ( ; i < chars; i++)
+ {
+ ch = ucs[i];
+ *p++ = ch;
+ if (ch == ' ')
+ {
+ for (k = ((remain_spaces > 0)? 0 : 1); k < words_spaces; k++)
+ *p++ = ch;
+
+ remain_spaces--;
+ }
+ }
+
+ rb->memcpy(ucs, new_ucs, sizeof(unsigned short) * TV_MAX_CHARS_PER_BLOCK);
+ return indent_chars + nonspace_chars + expand_spaces;
+}
+
+static void tv_align_right(int *block_chars)
+{
+ unsigned short *cur_text;
+ unsigned short *prev_text;
+ unsigned short ch;
+ int cur_block = block_count - 1;
+ int prev_block;
+ int cur_chars;
+ int prev_chars;
+ int idx;
+ int break_pos;
+ int break_width = 0;
+ int append_width;
+ int width;
+
+ while (cur_block > 0)
+ {
+ cur_text = ucsbuf[cur_block];
+ cur_chars = block_chars[cur_block];
+ idx = cur_chars;
+ width = 0;
+ while(--idx >= 0)
+ width += tv_glyph_width(cur_text[idx]);
+
+ width = block_width - width;
+ prev_block = cur_block - 1;
+
+ do {
+ prev_text = ucsbuf[prev_block];
+ prev_chars = block_chars[prev_block];
+
+ idx = prev_chars;
+ append_width = 0;
+ break_pos = prev_chars;
+ while (append_width < width && idx > 0)
+ {
+ ch = prev_text[--idx];
+ if (tv_is_line_break_char(ch))
+ {
+ break_pos = idx + 1;
+ break_width = append_width;
+ }
+ append_width += tv_glyph_width(ch);
+ }
+ if (append_width > width)
+ idx++;
+
+ if (idx == 0)
+ {
+ break_pos = 0;
+ break_width = append_width;
+ }
+
+ if (break_pos < prev_chars)
+ append_width = break_width;
+ /* the case of
+ * (1) when the first character of the cur_text concatenates
+ * the last character of the prev_text.
+ * (2) the length of ucsbuf[block] is short (< 0.75 * block width)
+ */
+ else if (((!tv_isspace(*cur_text) && !tv_isspace(prev_text[prev_chars - 1])) ||
+ (4 * width >= 3 * block_width)))
+ {
+ break_pos = idx;
+ }
+
+ if (break_pos < prev_chars)
+ {
+ rb->memmove(cur_text + prev_chars - break_pos,
+ cur_text, block_chars[cur_block] * sizeof(unsigned short));
+ rb->memcpy(cur_text, prev_text + break_pos,
+ (prev_chars - break_pos) * sizeof(unsigned short));
+
+ block_chars[prev_block] = break_pos;
+ block_chars[cur_block ] += prev_chars - break_pos;
+ }
+ } while ((width -= append_width) > 0 && --prev_block >= 0);
+ cur_block--;
+ }
+}
+
+static int tv_parse_text(const unsigned char *src, unsigned short *ucs,
+ int *ucs_chars, bool is_indent)
+{
+ const unsigned char *cur = src;
+ const unsigned char *next = src;
+ const unsigned char *line_break_ptr = NULL;
+ const unsigned char *line_end_ptr = NULL;
+ unsigned short ch = 0;
+ unsigned short prev_ch;
+ int chars = 0;
+ int gw;
+ int i;
+ int line_break_width = 0;
+ int line_end_chars = 0;
+ int width = 0;
+ bool is_space = false;
+
+ while (true) {
+ cur = next;
+ if (cur >= end_ptr)
+ {
+ line_end_ptr = cur;
+ line_end_chars = chars;
+ is_break_line = true;
+ break;
+ }
+
+ prev_ch = ch;
+ next = tv_get_ucs(cur, &ch);
+ if (ch == '\n')
+ {
+ if (prefs->line_mode != JOIN || tv_is_break_line_join_mode(next))
+ {
+ line_end_ptr = next;
+ line_end_chars = chars;
+ is_break_line = false;
+ break;
+ }
+
+ if (prefs->word_mode == CHOP || tv_isspace(prev_ch))
+ continue;
+
+ /*
+ * when the line mode is JOIN and the word mode is WRAP,
+ * the next character does not concatenate with the
+ * previous character.
+ */
+ ch = ' ';
+ }
+ else if ((is_space = tv_isspace(ch)) == true)
+ {
+ /*
+ * when the line mode is REFLOW:
+ * (1) spacelike character convert to ' '
+ * (2) plural spaces are collected to one
+ */
+ if (prefs->line_mode == REFLOW)
+ {
+ ch = ' ';
+ if (prev_ch == ch)
+ continue;
+ }
+
+ /* when the alignment is RIGHT, ignores indent spaces. */
+ if (prefs->alignment == RIGHT && is_indent)
+ continue;
+ }
+ else
+ is_indent = false;
+
+ if (prefs->line_mode == REFLOW && is_indent)
+ gw = tv_glyph_width(ch) * TV_INDENT_SPACES;
+ else
+ gw = tv_glyph_width(ch);
+
+ width += gw;
+ if (width > block_width)
+ {
+ width -= gw;
+ if (is_space)
+ {
+ line_end_ptr = cur;
+ line_end_chars = chars;
+ }
+ is_break_line = true;
+ break;
+ }
+
+ if (prefs->line_mode == REFLOW && is_indent)
+ {
+ for (i = 1; i < TV_INDENT_SPACES; i++)
+ ucs[chars++] = ch;
+ }
+ ucs[chars++] = ch;
+
+ if (tv_is_line_break_char(ch))
+ {
+ line_break_ptr = next;
+ line_break_width = width;
+ line_end_chars = chars;
+ }
+ if (chars >= TV_MAX_CHARS_PER_BLOCK)
+ {
+ is_break_line = true;
+ break;
+ }
+ }
+
+ /* set the end position and character count */
+ if (line_end_ptr == NULL)
+ {
+ /*
+ * when the last line break position is too short (line length < 0.75 * block width),
+ * the line is cut off at the position where it is closest to the displayed width.
+ */
+ if ((prefs->line_mode == REFLOW && line_break_ptr == NULL) ||
+ (4 * line_break_width < 3 * block_width))
+ {
+ line_end_ptr = cur;
+ line_end_chars = chars;
+ }
+ else
+ line_end_ptr = line_break_ptr;
+ }
+
+ *ucs_chars = line_end_chars;
+ return line_end_ptr - src;
+}
+
+int tv_create_formed_text(const unsigned char *src, ssize_t bufsize,
+ int block, bool is_multi, const unsigned char **dst)
+{
+ unsigned short ch;
+ int chars[block_count];
+ int i;
+ int size = 0;
+ bool is_indent;
+
+ outbuf = utf8buf;
+ *outbuf = '\0';
+
+ for (i = 0; i < block_count; i++)
+ chars[i] = 0;
+
+ if (dst != NULL)
+ *dst = utf8buf;
+
+ if (prefs->line_mode == EXPAND && (expand_extra_line = !expand_extra_line) == true)
+ return 0;
+
+ end_ptr = src + bufsize;
+
+ tv_get_ucs(src, &ch);
+ is_indent = (tv_isspace(ch) && !is_break_line);
+
+ for (i = 0; i < block_count; i++)
+ {
+ size += tv_parse_text(src + size, ucsbuf[i], &chars[i], is_indent);
+ if (!is_break_line)
+ break;
+
+ is_indent = false;
+ }
+
+ if (dst != NULL)
+ {
+ if (prefs->alignment == RIGHT)
+ tv_align_right(chars);
+
+ for (i = 0; i < block_count; i++)
+ {
+ if (i == block || (is_multi && i == block + 1))
+ {
+ if (is_break_line && prefs->line_mode == REFLOW)
+ chars[i] = tv_form_reflow_line(ucsbuf[i], chars[i]);
+
+ tv_decode2utf8(ucsbuf[i], chars[i]);
+ }
+ }
+ }
+
+ return size;
+}
+
+bool tv_init_text_processor(unsigned char *buf, size_t bufsize, size_t *used_size)
+{
+ int i;
+
+ *used_size = TV_MAX_CHARS_PER_BLOCK * (2 * 3 + TV_MAX_BLOCKS * sizeof(unsigned short));
+ if (bufsize < *used_size)
+ return false;
+
+ prefs = tv_get_preferences();
+ text_type = TV_TEXT_UNKNOWN;
+ expand_extra_line = false;
+ is_break_line = false;
+
+ ucsbuf[0] = (unsigned short*)buf;
+ for (i = 1; i < TV_MAX_BLOCKS; i++)
+ ucsbuf[i] = ucsbuf[i - 1] + TV_MAX_CHARS_PER_BLOCK;
+
+ utf8buf = buf + TV_MAX_CHARS_PER_BLOCK * TV_MAX_BLOCKS * sizeof(unsigned short);
+
+ return true;
+}
+
+void tv_set_creation_conditions(int blocks, int width)
+{
+ block_count = blocks;
+ block_width = width;
+}
diff --git a/apps/plugins/text_viewer/tv_text_processor.h b/apps/plugins/text_viewer/tv_text_processor.h
new file mode 100644
index 0000000000..3cb2a96448
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_text_processor.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_TEXT_PROCESSOR_H
+#define PLUGIN_TEXT_VIEWER_TEXT_PROCESSOR_H
+
+/*
+ * initialize the text processor module
+ *
+ * [In] buf
+ * the start pointer of the buffer
+ *
+ * [In] size
+ * enabled buffer size
+ *
+ * [Out] used_size
+ * the size of the buffer which the pager uses
+ *
+ * return
+ * true initialize success
+ * false initialize failure
+ */
+bool tv_init_text_processor(unsigned char *buf, size_t bufsize, size_t *used_size);
+
+/*
+ * set the processing conditions
+ *
+ * [In] blocks
+ * total block count
+ *
+ * [In] width
+ * the block width
+ */
+void tv_set_creation_conditions(int blocks, int width);
+
+/*
+ * create the displayed text
+ *
+ * [In] src
+ * the start pointer of the buffer
+ *
+ * [In] bufsize
+ * buffer size
+ *
+ * [In] block
+ * the index of block to read text
+ *
+ * [In] is_multi
+ * true read 2 blocks
+ * false read 1 block
+ *
+ * [Out] dst
+ * the pointer of the pointer which store the text
+ *
+ * return
+ * the size of the text
+ */
+int tv_create_formed_text(const unsigned char *src, ssize_t bufsize,
+ int block, bool is_multi, const unsigned char **dst);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_text_reader.c b/apps/plugins/text_viewer/tv_text_reader.c
new file mode 100644
index 0000000000..28cfa565c3
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_text_reader.c
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_pager.h"
+#include "tv_reader.h"
+#include "tv_text_processor.h"
+#include "tv_text_reader.h"
+
+static int get_block;
+static bool get_double_blocks;
+
+bool tv_init_text_reader(unsigned char *buf, size_t bufsize, size_t *used_size)
+{
+ size_t size;
+
+ if (!tv_init_text_processor(buf, bufsize, used_size))
+ return false;
+
+ size = *used_size;
+ if (!tv_init_pager(buf + size, bufsize - size, used_size))
+ return false;
+
+ *used_size += size;
+ return true;
+}
+
+void tv_finalize_text_reader(void)
+{
+ tv_finalize_pager();
+}
+
+void tv_set_read_conditions(int blocks, int width)
+{
+ tv_set_creation_conditions(blocks, width);
+}
+
+off_t tv_get_total_text_size(void)
+{
+ return tv_get_file_size();
+}
+
+bool tv_get_next_line(const unsigned char **buf)
+{
+ ssize_t bufsize;
+ const unsigned char *buffer = tv_get_buffer(&bufsize);
+
+ if (bufsize > 0)
+ {
+ tv_move_next_line(
+ tv_create_formed_text(buffer, bufsize, get_block, get_double_blocks, buf));
+ return true;
+ }
+ return false;
+}
+
+void tv_read_start(int block, bool is_multi)
+{
+ get_block = block;
+ get_double_blocks = is_multi;
+ tv_reset_line_positions();
+}
+
+int tv_read_end(void)
+{
+ return tv_get_line_positions(0);
+}
+
+void tv_seek_top(void)
+{
+ tv_move_screen(0, 0, SEEK_SET);
+}
diff --git a/apps/plugins/text_viewer/tv_text_reader.h b/apps/plugins/text_viewer/tv_text_reader.h
new file mode 100644
index 0000000000..e8c712c76f
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_text_reader.h
@@ -0,0 +1,101 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_TEXT_READER_H
+#define PLUGIN_TEXT_VIEWER_TEXT_READER_H
+
+/*
+ * initialize the text reader module
+ *
+ * [In] buf
+ * the start pointer of the buffer
+ *
+ * [In] size
+ * enabled buffer size
+ *
+ * [Out] used_size
+ * the size of the buffer which the pager uses
+ *
+ * return
+ * true initialize success
+ * false initialize failure
+ */
+bool tv_init_text_reader(unsigned char *buf, size_t bufsize, size_t *used_size);
+
+/* finalize the text reader module */
+void tv_finalize_text_reader(void);
+
+/*
+ * set the read conditions
+ *
+ * [In] blocks
+ * block count
+ *
+ * [In] width
+ * block width
+ */
+void tv_set_read_conditions(int blocks, int width);
+
+/*
+ * return the total text size
+ *
+ * return
+ * the total text size
+ */
+off_t tv_get_total_text_size(void);
+
+/*
+ * get the text of the next line
+ *
+ * [Out] buf
+ * the pointer of the pointer which store the text
+ *
+ * return
+ * true next line exists
+ * false next line does not exist
+ */
+bool tv_get_next_line(const unsigned char **buf);
+
+/*
+ * start to read lines
+ *
+ * [In] block
+ * the index of block to read text
+ *
+ * [In] is_multi
+ * true read 2 blocks
+ * false read 1 block
+ */
+void tv_read_start(int block, bool is_multi);
+
+/*
+ * end to read lines
+ *
+ * return
+ * read text size
+ */
+int tv_read_end(void);
+
+/* seek to the head of the file */
+void tv_seek_top(void);
+
+#endif
diff --git a/apps/plugins/text_viewer/tv_window.c b/apps/plugins/text_viewer/tv_window.c
new file mode 100644
index 0000000000..43a24ca4bc
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_window.c
@@ -0,0 +1,394 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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_bookmark.h"
+#include "tv_preferences.h"
+#include "tv_screen_pos.h"
+#include "tv_text_reader.h"
+#include "tv_window.h"
+
+#define TV_WINDOWS_PER_SCREEN 2
+
+#define TV_SCROLLBAR_WIDTH rb->global_settings->scrollbar_width
+
+#ifndef HAVE_LCD_BITMAP
+#define TV_BOOKMARK_ICON 0xe101
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+static int header_height;
+static int footer_height;
+static bool need_scrollbar = false;
+#endif
+
+static int start_width;
+static int display_lines;
+
+static int window_width;
+static int window_columns;
+static int col_width;
+
+static int max_windows;
+
+static int cur_window;
+static int cur_column;
+
+static const struct tv_preferences *prefs = NULL;
+
+#ifdef HAVE_LCD_BITMAP
+static bool tv_set_font(const unsigned char *font)
+{
+ unsigned char path[MAX_PATH];
+
+ if (font != NULL && *font != '\0')
+ {
+ rb->snprintf(path, MAX_PATH, "%s/%s.fnt", FONT_DIR, font);
+ if (rb->font_load(NULL, path) < 0)
+ {
+ rb->splash(HZ/2, "font load failed");
+ return false;
+ }
+ }
+ return true;
+}
+
+static void tv_check_header_and_footer(void)
+{
+ struct tv_preferences new_prefs;
+
+ tv_copy_preferences(&new_prefs);
+
+ if (rb->global_settings->statusbar != STATUSBAR_TOP)
+ {
+ if (new_prefs.header_mode == HD_SBAR)
+ new_prefs.header_mode = HD_NONE;
+ else if (new_prefs.header_mode == HD_BOTH)
+ new_prefs.header_mode = HD_PATH;
+ }
+ if (rb->global_settings->statusbar != STATUSBAR_BOTTOM)
+ {
+ if (new_prefs.footer_mode == FT_SBAR)
+ new_prefs.footer_mode = FT_NONE;
+ else if (new_prefs.footer_mode == FT_BOTH)
+ new_prefs.footer_mode = FT_PAGE;
+ }
+ tv_set_preferences(&new_prefs);
+}
+
+static void tv_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 - prefs->font->height, prefs->file_name);
+}
+
+static void tv_show_footer(const struct tv_screen_pos *pos)
+{
+ unsigned char buf[12];
+
+ 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)
+ {
+ if (pos->line == 0)
+ rb->snprintf(buf, sizeof(buf), "%d", pos->page + 1);
+ else
+ rb->snprintf(buf, sizeof(buf), "%d - %d", pos->page + 1, pos->page + 2);
+
+ rb->lcd_putsxy(0, LCD_HEIGHT - footer_height, buf);
+ }
+}
+
+static void tv_show_scrollbar(off_t cur_pos, int size)
+{
+ int items;
+ int min_shown;
+ int max_shown;
+ int sb_begin_y;
+ int sb_height;
+
+ if (!need_scrollbar)
+ return;
+
+ items = (int) tv_get_total_text_size();
+ min_shown = (int) cur_pos;
+
+ max_shown = min_shown + size;
+
+ 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,
+ TV_SCROLLBAR_WIDTH-1, sb_height,
+ items, min_shown, max_shown, VERTICAL);
+}
+
+static int tv_calc_display_lines(void)
+{
+ header_height = (prefs->header_mode == HD_SBAR || prefs->header_mode == HD_BOTH)?
+ STATUSBAR_HEIGHT : 0;
+
+ footer_height = (prefs->footer_mode == FT_SBAR || prefs->footer_mode == FT_BOTH)?
+ STATUSBAR_HEIGHT : 0;
+
+ 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 += prefs->font->height;
+
+ if (prefs->footer_mode == FT_PAGE || prefs->footer_mode == FT_BOTH)
+ footer_height += prefs->font->height;
+
+ return (LCD_HEIGHT - header_height - footer_height) / prefs->font->height;
+}
+#endif
+
+static void tv_show_bookmarks(const struct tv_screen_pos *top_pos)
+{
+ struct tv_screen_pos bookmarks[TV_MAX_BOOKMARKS];
+ int count = tv_get_bookmark_positions(bookmarks);
+ int line;
+
+#ifdef HAVE_LCD_BITMAP
+ rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
+#endif
+ while (count--)
+ {
+ line = (bookmarks[count].page - top_pos->page) * display_lines
+ + (bookmarks[count].line - top_pos->line);
+ if (line >= 0 && line < display_lines)
+ {
+#ifdef HAVE_LCD_BITMAP
+ rb->lcd_fillrect(start_width, header_height + line * prefs->font->height,
+ window_width, prefs->font->height);
+#else
+ rb->lcd_putc(start_width - 1, line, TV_BOOKMARK_ICON);
+#endif
+ }
+ }
+#ifdef HAVE_LCD_BITMAP
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#endif
+}
+
+void tv_draw_window(void)
+{
+ struct tv_screen_pos pos;
+ const unsigned char *line_buf;
+ int line;
+ int offset = cur_column * col_width;
+ int size = 0;
+ int line_width;
+ int draw_width = (max_windows - cur_window) * LCD_WIDTH - offset;
+ int dx = start_width - offset;
+
+ tv_copy_screen_pos(&pos);
+ rb->lcd_clear_display();
+
+ if (prefs->alignment == LEFT)
+ tv_read_start(cur_window, (cur_column > 0));
+ else
+ tv_read_start(0, prefs->view_mode == WIDE);
+
+ for (line = 0; line < display_lines; line++)
+ {
+ if (!tv_get_next_line(&line_buf))
+ break;
+
+ if (prefs->alignment == RIGHT)
+ {
+ rb->lcd_getstringsize(line_buf, &line_width, NULL);
+ dx = draw_width - line_width;
+ }
+
+#ifdef HAVE_LCD_BITMAP
+ rb->lcd_putsxy(dx, header_height + line * prefs->font->height, line_buf);
+#else
+ rb->lcd_puts(dx, line, line_buf);
+#endif
+ }
+
+ size = tv_read_end();
+
+ tv_show_bookmarks(&pos);
+
+#ifdef HAVE_LCD_BITMAP
+ tv_show_scrollbar(pos.file_pos, size);
+ tv_show_header();
+ tv_show_footer(&pos);
+#endif
+
+ rb->lcd_update();
+}
+
+bool tv_traverse_lines(void)
+{
+ int i;
+ bool res = true;
+
+ tv_read_start(0, false);
+ for (i = 0; i < display_lines; i++)
+ {
+ if (!tv_get_next_line(NULL))
+ {
+ res = false;
+ break;
+ }
+ }
+ tv_read_end();
+ return res;
+}
+
+static void tv_change_preferences(const struct tv_preferences *oldp)
+{
+#ifdef HAVE_LCD_BITMAP
+ static bool is_executing = false;
+
+ is_executing = true;
+
+ /* change font */
+ if (oldp == NULL || rb->strcmp(oldp->font_name, prefs->font_name))
+ {
+ if (!tv_set_font(prefs->font_name))
+ {
+ struct tv_preferences new_prefs = *prefs;
+ const unsigned char *font_str;
+
+ font_str = (oldp == NULL)? rb->global_settings->font_file : oldp->font_name;
+
+ if (!tv_set_font(font_str) && oldp != NULL)
+ {
+ font_str = rb->global_settings->font_file;
+ tv_set_font(new_prefs.font_name);
+ }
+
+ rb->strlcpy(new_prefs.font_name, font_str, MAX_PATH);
+ tv_set_preferences(&new_prefs);
+ }
+ }
+
+ /* calculates display lines */
+ tv_check_header_and_footer();
+ display_lines = tv_calc_display_lines();
+
+ if (!is_executing)
+ return;
+
+ is_executing = false;
+#else
+ (void)oldp;
+
+ /* REAL fixed pitch :) all chars use up 1 cell */
+ display_lines = 2;
+#endif
+
+#ifdef HAVE_LCD_BITMAP
+ col_width = 2 * rb->font_get_width(prefs->font, ' ');
+#else
+ col_width = 1;
+#endif
+
+ max_windows = (prefs->view_mode == NARROW)? 1: TV_WINDOWS_PER_SCREEN;
+ if (cur_window >= max_windows)
+ cur_window = 0;
+
+ window_width = LCD_WIDTH;
+#ifdef HAVE_LCD_BITMAP
+ need_scrollbar = false;
+ start_width = 0;
+ tv_seek_top();
+ tv_set_read_conditions(max_windows, window_width);
+ if (tv_traverse_lines() && prefs->scrollbar_mode)
+ {
+ need_scrollbar = true;
+ start_width = TV_SCROLLBAR_WIDTH;
+ }
+ tv_seek_top();
+#else
+ start_width = 1;
+#endif
+ window_width -= start_width;
+ window_columns = window_width / col_width;
+
+ cur_column = 0;
+
+ tv_set_read_conditions(max_windows, window_width);
+}
+
+bool tv_init_window(unsigned char *buf, size_t bufsize, size_t *used_size)
+{
+ tv_add_preferences_change_listner(tv_change_preferences);
+ if (!tv_init_text_reader(buf, bufsize, used_size))
+ return false;
+
+ prefs = tv_get_preferences();
+ return true;
+}
+
+void tv_finalize_window(void)
+{
+ tv_finalize_text_reader();
+}
+
+void tv_move_window(int window_delta, int column_delta)
+{
+ cur_window += window_delta;
+ cur_column += column_delta;
+
+ if (cur_window < 0)
+ {
+ cur_window = 0;
+ cur_column = 0;
+ }
+ else if (cur_window >= max_windows)
+ {
+ cur_window = max_windows - 1;
+ cur_column = 0;
+ }
+
+ if (cur_column < 0)
+ {
+ if (cur_window == 0)
+ cur_column = 0;
+ else
+ {
+ cur_window--;
+ cur_column = window_columns - 1;
+ }
+ }
+ else
+ {
+ if (cur_window == max_windows - 1)
+ cur_column = 0;
+ else if (cur_column >= window_columns)
+ {
+ cur_window++;
+ cur_column = 0;
+ }
+ }
+}
diff --git a/apps/plugins/text_viewer/tv_window.h b/apps/plugins/text_viewer/tv_window.h
new file mode 100644
index 0000000000..fe87ec6569
--- /dev/null
+++ b/apps/plugins/text_viewer/tv_window.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Gilles Roux
+ * 2003 Garrett Derner
+ * 2010 Yoshihisa Uchida
+ *
+ * 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.
+ *
+ ****************************************************************************/
+#ifndef PLUGIN_TEXT_VIEWER_WINDOW_H
+#define PLUGIN_TEXT_VIEWER_WINDOW_H
+
+/*
+ * initialize the window module
+ *
+ * [In] buf
+ * the start pointer of the buffer
+ *
+ * [In] size
+ * enabled buffer size
+ *
+ * [Out] used_size
+ * the size of the buffer which the pager uses
+ *
+ * return
+ * true initialize success
+ * false initialize failure
+ */
+bool tv_init_window(unsigned char *buf, size_t bufsize, size_t *used_size);
+
+/* finalize the window module */
+void tv_finalize_window(void);
+
+/* draw the display */
+void tv_draw_window(void);
+
+/* traverse all lines of the current page */
+bool tv_traverse_lines(void);
+
+/*
+ * move to the window
+ *
+ * new position
+ * new window: the current window + window_delta
+ * new column: the current column + column_delta
+ *
+ * [In] window_delta
+ * variation of the window
+ *
+ * [In] column_delta
+ * variation of the column
+ *
+ */
+void tv_move_window(int window_delta, int column_delta);
+
+#endif
diff --git a/apps/plugins/viewers.config b/apps/plugins/viewers.config
index 1c70c2957f..2a98bb6ed7 100644
--- a/apps/plugins/viewers.config
+++ b/apps/plugins/viewers.config
@@ -1,6 +1,6 @@
ch8,viewers/chip8,0
-txt,viewers/viewer,1
-nfo,viewers/viewer,1
+txt,viewers/text_viewer,1
+nfo,viewers/text_viewer,1
txt,apps/text_editor,2
bmp,viewers/bmp,2
jpg,viewers/jpeg,2