diff options
| author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-21 04:30:08 +0000 |
|---|---|---|
| committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-07-21 04:30:08 +0000 |
| commit | a3570c360e8884f2bbf60b89e4d9531321ae01ad (patch) | |
| tree | a6bc829daca69956b99be4dfa3939ff14193ec54 /src | |
| download | kbookreader-a3570c360e8884f2bbf60b89e4d9531321ae01ad.tar.gz kbookreader-a3570c360e8884f2bbf60b89e4d9531321ae01ad.zip | |
Initial import of kbookreader
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/kbookreader@1242482 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 47 | ||||
| -rw-r--r-- | src/README | 81 | ||||
| -rw-r--r-- | src/bookmark.cpp | 32 | ||||
| -rw-r--r-- | src/bookmark.h | 57 | ||||
| -rw-r--r-- | src/bookmarksdlg.cpp | 174 | ||||
| -rw-r--r-- | src/bookmarksdlg.h | 71 | ||||
| -rw-r--r-- | src/bookreader.cpp | 511 | ||||
| -rw-r--r-- | src/bookreader.desktop | 39 | ||||
| -rw-r--r-- | src/bookreader.h | 127 | ||||
| -rw-r--r-- | src/bookreader.kcfg | 24 | ||||
| -rw-r--r-- | src/bookreader.lsm | 16 | ||||
| -rw-r--r-- | src/bookreader_client.cpp | 45 | ||||
| -rw-r--r-- | src/bookreaderiface.h | 36 | ||||
| -rw-r--r-- | src/bookreaderui.rc | 19 | ||||
| -rw-r--r-- | src/bookwidget.cpp | 473 | ||||
| -rw-r--r-- | src/bookwidget.h | 125 | ||||
| -rw-r--r-- | src/hi16-app-bookreader.png | bin | 0 -> 631 bytes | |||
| -rw-r--r-- | src/hi32-app-bookreader.png | bin | 0 -> 1568 bytes | |||
| -rw-r--r-- | src/main.cpp | 66 | ||||
| -rw-r--r-- | src/renderer.cpp | 398 | ||||
| -rw-r--r-- | src/renderer.h | 138 | ||||
| -rw-r--r-- | src/settings.kcfgc | 6 | ||||
| -rw-r--r-- | src/settingsdlg.ui | 167 | ||||
| -rw-r--r-- | src/theme.cpp | 59 | ||||
| -rw-r--r-- | src/theme.h | 46 |
25 files changed, 2757 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..94dcdea --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,47 @@ +## Makefile.am for bookreader + +# this is the program that gets installed. it's name is used for all +# of the other Makefile.am variables +bin_PROGRAMS = bookreader bookreader_client + +# set the include path for X, qt and KDE +INCLUDES = $(all_includes) + +# the library search path. +bookreader_LDFLAGS = $(KDE_RPATH) $(all_libraries) + +# the libraries to link against. +bookreader_LDADD = $(LIB_KFILE) $(LIB_KDEPRINT) + +# which sources should be compiled for bookreader +bookreader_SOURCES = main.cpp bookreader.cpp bookreaderiface.skel \ + bookwidget.cpp theme.cpp settings.kcfgc settingsdlg.ui renderer.cpp bookmark.cpp \ + bookmarksdlg.cpp + +# these are the headers for your project +noinst_HEADERS = bookreader.h bookwidget.h theme.h renderer.h bookmark.h \ + bookmarksdlg.h + +# client stuff +bookreader_client_LDFLAGS = $(KDE_RPATH) $(all_libraries) +bookreader_client_LDADD = $(LIB_KDECORE) +bookreader_client_SOURCES = bookreader_client.cpp + +kde_kcfg_DATA = bookreader.kcfg + +# let automoc handle all of the meta source files (moc) +METASOURCES = AUTO + +messages: rc.cpp + $(EXTRACTRC) `find . -name \*.ui -o -name \*.rc` > rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/bookreader.pot + +KDE_ICON = AUTO + +# this is where the kdelnk file will go +kdelnkdir = $(kde_appsdir)/Utilities +kdelnk_DATA = bookreader.desktop + +# this is where the XML-GUI resource file goes +rcdir = $(kde_datadir)/bookreader +rc_DATA = bookreaderui.rc diff --git a/src/README b/src/README new file mode 100644 index 0000000..f04a563 --- /dev/null +++ b/src/README @@ -0,0 +1,81 @@ +----------------------------------------------- +Kde application framework template quickstart +Author: Thomas Nagy +Date: 2004-03-22 +----------------------------------------------- + +This README file explains you basic things for starting with +this application template. + + +** Building and installing ** + +* Build the configure script by "make -f Makefile.cvs" + +* To clean, use "make clean", and to clean everything +(remove the makefiles, etc), use "make distclean" + +* To distribute your program, try "make dist". +This will make a compact tarball archive of your release with the +necessary scripts inside. + +* Modifying the auto-tools scripts +for automake scripts there is an excellent tutorial there : +http://developer.kde.org/documentation/other/makefile_am_howto.html + +* Simplify your life : install the project in your home directory for +testing purposes. +./configure --prefix=/home/user/dummyfolder/ +In the end when you finished the development you can +rm -rf /home/user/dummyfolder/ +without fear. + + +** Technologies ** + +* Build the menus of your application easily +kde applications now use an xml file (*ui.rc file) to build the menus. +This allow a great customization of the application. However, when +programming the menu is shown only after a "make install" + +For more details, consult : +http://devel-home.kde.org/~larrosa/tutorial/p9.html +http://developer.kde.org/documentation/tutorials/xmlui/preface.html + +* Use KConfig XT to create your configuration dialogs and make +them more maintainable. + +For more details, consult : +http://developer.kde.org/documentation/tutorials/kconfigxt/kconfigxt.html + +* With KParts, you can embed other kde components in your program, or make your program +embeddable in other apps. For example, the kmplayer kpart can be called to play videos +in your app. + +For more details, consult : +http://www-106.ibm.com/developerworks/library/l-kparts/ +http://developer.kde.org/documentation/tutorials/dot/writing-plugins.html +http://developer.kde.org/documentation/tutorials/developing-a-plugin-structure/index.html + +* With dcop, you can control your app from other applications +Make sure to include K_DCOP and a kdcop: section in your .h file +http://developer.kde.org/documentation/tutorials/dot/dcopiface/dcop-interface.html + + +** Documentation ** + +* For the translations : +1. Download a patched gettext which can be found at: + http://public.kde.planetmirror.com/pub/kde/devel/gettext-kde/ +2. Install that gettext in ~/bin/ +3. cd ~/yourproject, export PATH=~/bin:$PATH, export +KDEDIR=/where_your_KDE3_is +4. make -f admin/Makefile.common package-messages +5. make package-messages +6. Translate the po files (not the pot!!) with kbabel or xemacs + +* Do not forget to write the documentation for your kde app +edit the documentation template index.docbook in doc/ + + + diff --git a/src/bookmark.cpp b/src/bookmark.cpp new file mode 100644 index 0000000..e438dc3 --- /dev/null +++ b/src/bookmark.cpp @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright (C) 2005 by Alexandr Nemish * + * atlanter@gmail.com * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "bookmark.h" + +Bookmark::Bookmark(int page, const QString& name, QDateTime dateTime): + m_page(page), + m_name(name), + m_dateTime(dateTime) +{ +} + + +Bookmark::~Bookmark() +{ +} diff --git a/src/bookmark.h b/src/bookmark.h new file mode 100644 index 0000000..b144b2a --- /dev/null +++ b/src/bookmark.h @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright (C) 2005 by Alexander Nemish * + * atlanter@gmail.com * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef BOOKMARK_H +#define BOOKMARK_H + +#include <qdatetime.h> +#include <qstring.h> +#include <vector> + +class Bookmark; +typedef std::vector<Bookmark> Bookmarks; + +/** +*/ +class Bookmark +{ +public: + Bookmark(int page, const QString& name = QString::null, + QDateTime dateTime = QDateTime::currentDateTime()); + ~Bookmark(); + + void setPage(unsigned int page) { m_page = page; } + unsigned int page() const { return m_page; } + const QString & name() const { return m_name; } + const QDateTime & dateTime() const { return m_dateTime; } + + bool operator < (const Bookmark& rhs) const { return (m_page < rhs.m_page); } + + void setName(const QString& theValue) + { + m_name = theValue; + } + +private: + unsigned int m_page; + QString m_name; + QDateTime m_dateTime; +}; + +#endif diff --git a/src/bookmarksdlg.cpp b/src/bookmarksdlg.cpp new file mode 100644 index 0000000..f3bd823 --- /dev/null +++ b/src/bookmarksdlg.cpp @@ -0,0 +1,174 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include <kdialog.h> +#include <klocale.h> +#include <qvariant.h> +#include <qpushbutton.h> +#include <qheader.h> +#include <qtable.h> +#include <qlayout.h> +#include <qtooltip.h> +#include <qwhatsthis.h> + +#include "bookmarksdlg.h" +/* + * Constructs a BookmarksDlg as a child of 'parent', with the + * name 'name' and widget flags set to 'f'. + * + * The dialog will by default be modeless, unless you set 'modal' to + * TRUE to construct a modal dialog. + */ + +BookmarksDlg::BookmarksDlg(const Bookmarks& bms, QWidget* parent, + const char* name, bool modal, WFlags fl) + : QDialog(parent, name, modal, fl), + m_bookmarks(bms) +{ + if ( !name ) + setName( "BookmarksDlg" ); + setSizeGripEnabled(TRUE); + BookmarksDlgLayout = new QVBoxLayout( this, 11, 6, "BookmarksDlgLayout"); + + layout3 = new QHBoxLayout( 0, 0, 6, "layout3"); + + bookmarksTable = new QTable(3, 2, this, "bookmarksList" ); + bookmarksTable->horizontalHeader()->setLabel(0, "Title"); + bookmarksTable->horizontalHeader()->setLabel(1, "Page number"); +// bookmarksTable->setLeftMargin(0); + bookmarksTable->setColumnStretchable(0, true); + bookmarksTable->setSelectionMode(QTable::SingleRow); + layout3->addWidget( bookmarksTable ); + + layout2 = new QVBoxLayout( 0, 0, 6, "layout2"); + + addButton = new QPushButton( this, "addButton" ); + layout2->addWidget( addButton ); + + deleteButton = new QPushButton( this, "deleteButton" ); + layout2->addWidget( deleteButton ); + Horizontal_Spacing2_2 = new QSpacerItem( 20, 60, QSizePolicy::Minimum, QSizePolicy::Expanding ); + layout2->addItem( Horizontal_Spacing2_2 ); + layout3->addLayout( layout2 ); + BookmarksDlgLayout->addLayout( layout3 ); + + Layout1 = new QHBoxLayout( 0, 0, 6, "Layout1"); + + buttonHelp = new QPushButton( this, "buttonHelp" ); + buttonHelp->setAutoDefault( TRUE ); + Layout1->addWidget( buttonHelp ); + Horizontal_Spacing2 = new QSpacerItem( 20, 20, QSizePolicy::Expanding, QSizePolicy::Minimum ); + Layout1->addItem( Horizontal_Spacing2 ); + + buttonOk = new QPushButton( this, "buttonOk" ); + buttonOk->setAutoDefault( TRUE ); + buttonOk->setDefault( TRUE ); + Layout1->addWidget( buttonOk ); + + buttonCancel = new QPushButton( this, "buttonCancel" ); + buttonCancel->setAutoDefault( TRUE ); + Layout1->addWidget( buttonCancel ); + BookmarksDlgLayout->addLayout( Layout1 ); + languageChange(); + resize( QSize(400, 267).expandedTo(minimumSizeHint()) ); + clearWState( WState_Polished ); + + updateBookmarks(); + // signals and slots connections + connect(buttonOk, SIGNAL(clicked()), this, SLOT(accept())); + connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject())); + connect(addButton, SIGNAL(clicked()), this, SLOT(addBookmark())); + connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteBookmark())); + connect(bookmarksTable, SIGNAL(valueChanged(int, int)), this, SLOT(valueChanged(int, int))); +} + +/* + * Destroys the object and frees any allocated resources + */ +BookmarksDlg::~BookmarksDlg() +{ + // no need to delete child widgets, Qt does it all for us +} + +/* + * Sets the strings of the subwidgets using the current + * language. + */ +void BookmarksDlg::languageChange() +{ + setCaption( tr2i18n( "Bookmarks editor" ) ); + addButton->setText( tr2i18n( "Add" ) ); + deleteButton->setText( tr2i18n( "Delete" ) ); + buttonHelp->setText( tr2i18n( "&Help" ) ); + buttonHelp->setAccel( QKeySequence( tr2i18n( "F1" ) ) ); + buttonOk->setText( tr2i18n( "&OK" ) ); + buttonOk->setAccel( QKeySequence( QString::null ) ); + buttonCancel->setText( tr2i18n( "Ca&ncel" ) ); + buttonCancel->setAccel( QKeySequence( tr2i18n( "Alt+N" ) ) ); +} + +void BookmarksDlg::addBookmark() +{ + m_bookmarks.push_back(Bookmark(2, "new bookmark")); + std::sort(m_bookmarks.begin(), m_bookmarks.end()); + updateBookmarks(); +} + +void BookmarksDlg::deleteBookmark() +{ + int row = bookmarksTable->currentRow(); + if (row >= 0 && row < m_bookmarks.size()) + { + m_bookmarks.erase(m_bookmarks.begin() + row); + updateBookmarks(); + } +} + +void BookmarksDlg::updateBookmarks() +{ + bookmarksTable->setNumRows(0); + for (Bookmarks::size_type i = 0; i < m_bookmarks.size(); ++i) + { + const Bookmark& bm = m_bookmarks[i]; + bookmarksTable->setNumRows(i+1); + bookmarksTable->setText(i, 0, bm.name()); + bookmarksTable->setText(i, 1, QString::number(bm.page())); + } +} + +void BookmarksDlg::valueChanged(int row, int col) +{ + if (row >= m_bookmarks.size()) return; + Bookmark& bm = m_bookmarks[row]; + //Bookmark title + if (col == 0) + bm.setName(bookmarksTable->text(row, col)); + //page number + else if (col == 1) + { + bool ok; + unsigned int page = bookmarksTable->text(row, col).toUInt(&ok); + if (ok) + bm.setPage(page); + else + //return old value + bookmarksTable->setText(row, col, QString::number(bm.page())); + } +} + +#include "bookmarksdlg.moc" diff --git a/src/bookmarksdlg.h b/src/bookmarksdlg.h new file mode 100644 index 0000000..79d8df2 --- /dev/null +++ b/src/bookmarksdlg.h @@ -0,0 +1,71 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ + +#ifndef BOOKMARKSDLG_H +#define BOOKMARKSDLG_H + +#include <qvariant.h> +#include <qdialog.h> +#include <algorithm> +#include "bookmark.h" + +class QVBoxLayout; +class QHBoxLayout; +class QGridLayout; +class QSpacerItem; +class QTable; +class QPushButton; + +class BookmarksDlg : public QDialog +{ + Q_OBJECT + +public: + BookmarksDlg(const Bookmarks& bms, QWidget* parent = 0, + const char* name = 0, bool modal = FALSE, WFlags fl = 0); + ~BookmarksDlg(); + + const Bookmarks& bookmarks() const { return m_bookmarks; } + + QTable* bookmarksTable; + QPushButton* addButton; + QPushButton* deleteButton; + QPushButton* buttonHelp; + QPushButton* buttonOk; + QPushButton* buttonCancel; + +protected: + QVBoxLayout* BookmarksDlgLayout; + QHBoxLayout* layout3; + QVBoxLayout* layout2; + QSpacerItem* Horizontal_Spacing2_2; + QHBoxLayout* Layout1; + QSpacerItem* Horizontal_Spacing2; + +protected slots: + virtual void languageChange(); + void addBookmark(); + void deleteBookmark(); + void valueChanged(int row, int col); + void updateBookmarks(); +private: + Bookmarks m_bookmarks; +}; + +#endif // BOOKMARKSDLG_H diff --git a/src/bookreader.cpp b/src/bookreader.cpp new file mode 100644 index 0000000..662d283 --- /dev/null +++ b/src/bookreader.cpp @@ -0,0 +1,511 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* atlanter@gmail.com * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include <kaccel.h> +#include <kaction.h> +#include <kcharsets.h> +#include <kconfig.h> +#include <kconfigdialog.h> +#include <kdebug.h> +#include <kdeversion.h> +#include <kencodingfiledialog.h> +#include <kfiledialog.h> +#include <kglobal.h> +#include <kinputdialog.h> +#include <kiconloader.h> +#include <klocale.h> +#include <kmenubar.h> +#include <kmessagebox.h> +#include <kprinter.h> +#include <kstatusbar.h> +#include <ksplashscreen.h> +#include <kstandarddirs.h> +#include <kstdaccel.h> +#include <kstdaction.h> +#include <kurl.h> +#include <kurldrag.h> +#include <kurlrequesterdlg.h> +#include <kio/netaccess.h> +#include <qdragobject.h> +#include <qpainter.h> +#include <qpaintdevicemetrics.h> +#include <qpixmap.h> +#include <qtextcodec.h> +#include <qsignalmapper.h> +#include <assert.h> +#include "bookreader.h" +#include "settings.h" +#include "settingsdlg.h" +#include "bookmarksdlg.h" + +namespace { +QStringList listEncodings() +{ + const QStringList encodings(KGlobal::charsets()->availableEncodingNames()); + QStringList availEncodings; + for (unsigned int i=0; i < encodings.count(); ++i) + { + bool found = false; + KGlobal::charsets()->codecForName(encodings[i], found); + if (found) + availEncodings << encodings[i]; + } + return availEncodings; +} +} + + +BookReader::BookReader() + : KMainWindow(0, "BookReader"), + m_view(new BookWidget(this)), + m_fullScreenAction(0), + m_splash(0), + m_printer(0) +{ + + QPixmap splash(KGlobal::dirs()->findResource("appdata", + "themes/default/splash.png")); + m_splash = new KSplashScreen(splash); + m_splash->show(); + // accept dnd + setAcceptDrops(true); + + // tell the KMainWindow that this is indeed the main widget + setCentralWidget(m_view); + + // then, setup our actions + setupActions(); + + // and a status bar + statusBar()->show(); + + // Apply the create the main window and ask the mainwindow to + // automatically save settings if changed: window size, toolbar + // position, icon size, etc. Also to add actions for the statusbar + // toolbar, and keybindings if necessary. + KStdAction::keyBindings(guiFactory(), SLOT(configureShortcuts()), actionCollection()); + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled( true ); + KStdAction::configureToolbars(this, SLOT(configureToolbars() ), actionCollection()); + createGUI(QString::null, false); + initialGeometrySet(); + setAutoSaveSettings(); + + // allow the view to change the statusbar and caption + connect(m_view, SIGNAL(signalChangeStatusbar(const QString&)), + this, SLOT(changeStatusbar(const QString&))); + connect(m_view, SIGNAL(signalChangeCaption(const QString&)), + this, SLOT(changeCaption(const QString&))); + + readSettings(); + m_splash->finish(m_view); +} + +BookReader::~BookReader() +{ + writeSettings(); + delete m_splash; +} + +void BookReader::load(const KURL& url) +{ + QString target; + // download the contents + if(!KIO::NetAccess::download(url, target, this)) + { + KMessageBox::error(this, KIO::NetAccess::lastErrorString()); + return; + } + recentFilesAction->addURL(url); + setCaption(url.fileName()); + m_view->openURL(url); + updateBookmarks(); + KIO::NetAccess::removeTempFile(target); +} + +void BookReader::setupActions() +{ +// KStdAction::openNew(this, SLOT(fileNew()), actionCollection()); + KStdAction::open(this, SLOT(fileOpen()), actionCollection()); + recentFilesAction = KStdAction::openRecent(this, SLOT(slotURLSelected(const KURL &)), + actionCollection()); + KStdAction::save(this, SLOT(fileSave()), actionCollection()); + KStdAction::saveAs(this, SLOT(fileSaveAs()), actionCollection()); + KStdAction::print(this, SLOT(filePrint()), actionCollection()); + KStdAction::quit(kapp, SLOT(quit()), actionCollection()); + + KStdAction::firstPage(this, SLOT(gotoFirstPage()), actionCollection()); + KStdAction::prior(this, SLOT(prevPage()), actionCollection()); + KStdAction::next(this, SLOT(nextPage()), actionCollection()); + KStdAction::lastPage(this, SLOT(gotoLastPage()), actionCollection()); + + KStdAction::addBookmark(this, SLOT(addBookmark()), actionCollection()); + KStdAction::editBookmarks(this, SLOT(editBookmarks()), actionCollection()); + + KStdAction::gotoPage(this, SLOT(gotoPage()), actionCollection()); + + m_fullScreenAction = KStdAction::fullScreen(this, SLOT(fullScreen()), + actionCollection(), this); + + KStdAction::preferences(this, SLOT(optionsPreferences()), actionCollection()); + + KConfig *config = kapp->config(); + recentFilesAction->loadEntries(config); + + // this doesn't do anything useful. it's just here to illustrate + // how to insert a custom menu and menu item + /* + KAction *custom = new KAction( i18n( "Cus&tom Menuitem" ), 0, + this, SLOT( optionsPreferences() ), + actionCollection(), "custom_action" );*/ +} + +void BookReader::saveProperties(KConfig *config) +{ + // the 'config' object points to the session managed + // config file. anything you write here will be available + // later when this app is restored + if (!m_view->currentURL().isEmpty()) + { + config->writeEntry("lastURL", m_view->currentURL()); + config->writeEntry("currentPage", m_view->currentPage()); + } +} + +void BookReader::readProperties(KConfig */*config*/) +{ + // the 'config' object points to the session managed + // config file. this function is automatically called whenever + // the app is being restored. read in here whatever you wrote + // in 'saveProperties' + +// QString url = config->readPathEntry("lastURL"); +// int currentPage = config->readEntry("currentPage").toInt(); + /* Disable forawhile + if (!url.isEmpty()) + { + m_view->openURL(KURL(url)); + m_view->setCurrentPage(currentPage); + } + */ +} + +void BookReader::dragEnterEvent(QDragEnterEvent *event) +{ + // accept uri drops only + event->accept(KURLDrag::canDecode(event)); +} + +void BookReader::dropEvent(QDropEvent *event) +{ + // this is a very simplistic implementation of a drop event. we + // will only accept a dropped URL. the Qt dnd code can do *much* + // much more, so please read the docs there + KURL::List urls; + + // see if we can decode a URI.. if not, just ignore it + if (KURLDrag::decode(event, urls) && !urls.isEmpty()) + { + // okay, we have a URI.. process it + const KURL & url = urls.first(); + + // load in the file + load(url); + } +} + +void BookReader::fileOpen() +{ + // this slot is called whenever the File->Open menu is selected, + // the Open shortcut is pressed (usually CTRL+O) or the Open toolbar + // button is clicked + /* + // this brings up the generic open dialog + KURL url = KURLRequesterDlg::getURL(QString::null, this, i18n("Open Location") ); + */ + // standard filedialog + KEncodingFileDialog::Result res = + KEncodingFileDialog::getOpenURLAndEncoding( + listEncodings()[Settings::defaultEncoding()]); + KURL url = res.URLs.front(); + m_view->setEncoding(res.encoding); + if (!url.isEmpty()) + load(url); +} + +void BookReader::fileSave() +{ + // this slot is called whenever the File->Save menu is selected, + // the Save shortcut is pressed (usually CTRL+S) or the Save toolbar + // button is clicked + + // save the current file +} + +void BookReader::fileSaveAs() +{ + // this slot is called whenever the File->Save As menu is selected, + KURL file_url = KFileDialog::getSaveURL(); + if (!file_url.isEmpty() && file_url.isValid()) + { + // save your info, here + } +} + +void BookReader::filePrint() +{ + // this slot is called whenever the File->Print menu is selected, + // the Print shortcut is pressed (usually CTRL+P) or the Print toolbar + // button is clicked + if (!m_printer) m_printer = new KPrinter; + if (m_printer->setup(this)) + { + // setup the printer. with Qt, you always "print" to a + // QPainter.. whether the output medium is a pixmap, a screen, + // or paper + QPainter p; + p.begin(m_printer); + + // we let our view do the actual printing + QPaintDeviceMetrics metrics(m_printer); + //m_view->print( &p, metrics.height(), metrics.width() ); + + // and send the result to the printer + p.end(); + } +} + +void BookReader::optionsPreferences() +{ + if (KConfigDialog::showDialog("settings")) + return ; + + KConfigDialog *dialog = new KConfigDialog(this, "settings", Settings::self(), + KDialogBase::IconList); + SettingsWidget *general = new SettingsWidget(0, "General"); + + const QStringList encodings(listEncodings()); + + QString curEncoding(encodings[Settings::defaultEncoding()]); + if (curEncoding.isEmpty()) + curEncoding = QString::fromLatin1(KGlobal::locale()->encoding()); + + general->kcfg_DefaultEncoding->clear(); + general->kcfg_DefaultEncoding->insertStringList(encodings); + for (unsigned int i=0; i < encodings.count(); ++i) + if (encodings[i] == curEncoding) + general->kcfg_DefaultEncoding->setCurrentItem(i); + + dialog->addPage(general, i18n("General"), "settings"); + connect(dialog, SIGNAL(settingsChanged()), this, SLOT(loadSettings())); + dialog->show(); +} + +void BookReader::changeStatusbar(const QString& text) +{ + // display the text on the statusbar + statusBar()->message(text); +} + +void BookReader::changeCaption(const QString& text) +{ + // display the text on the caption + setCaption(text); +} + +void BookReader::loadSettings() +{ + m_view->setFont(Settings::font()); + m_view->setParaOffset(Settings::paraOffset()); + m_view->setEncoding(Settings::defaultEncoding()); +} + +void BookReader::slotURLSelected(const KURL & url) +{ + load(url); +} + +bool BookReader::queryExit() +{ + writeSettings(); + return true; +} + +void BookReader::writeSettings() +{ + KConfig * config = kapp->config(); + config->writeEntry("size", m_view->size()); + config->writeEntry("lastURL", m_view->currentURL()); + config->writeEntry("lastURLPage", m_view->currentPage()); + recentFilesAction->saveEntries(KGlobal::config()); +} + +void BookReader::readSettings() +{ + recentFilesAction->loadEntries(KGlobal::config()); + recentFilesAction->setEnabled(true); // force enabling + + m_view->setParaOffset(Settings::paraOffset()); + m_view->setEncodings(listEncodings()); + m_view->setEncoding(Settings::defaultEncoding()); + + if (Settings::loadLastUrl()) + { + loadLastUrl(); + } +} + +void BookReader::fullScreen() +{ + if (m_fullScreenAction->isChecked()) + { + menuBar()->hide(); + setWindowState(windowState() | WindowFullScreen); + } + else + { + menuBar()->show(); + setWindowState(windowState() & ~WindowFullScreen); + } +} + +void BookReader::loadLastUrl() +{ + KConfig * config = kapp->config(); + QSize size = config->readSizeEntry("size"); + lastURL = config->readEntry("lastURL"); + connect(m_view, SIGNAL(loadingFinished()), this, SLOT(loadLastURLSetPage())); + if (!lastURL.isEmpty()) + { +// kdDebug() << "loadLastUrl: initial size = " << m_view->size() << endl; + m_view->resize(size); +// kdDebug() << "loadLastUrl: resize = " << m_view->size() << endl; + m_view->setupPageSize(); +// kdDebug() << "render = 1" << endl; + load(lastURL); + } +} + +void BookReader::loaded(int loaded) +{ + //m_loaded = loaded; + m_splash->message(tr("Loading: %1 - %2%").arg(lastURL.fileName()).arg(loaded)); +} + +void BookReader::gotoFirstPage() +{ + m_view->firstPage(); +} + +void BookReader::nextPage() +{ + m_view->nextPage(); +} + +void BookReader::prevPage() +{ + m_view->prevPage(); +} + +void BookReader::gotoLastPage( ) +{ + m_view->lastPage(); +} + +void BookReader::gotoPage() +{ + bool isOk; + int page = KInputDialog::getInteger(tr("Goto page"), tr("Page number"), + m_view->currentPage(), 1, + m_view->pageCount(), 1, &isOk); + m_view->setCurrentPage(page - 1); +} + +void BookReader::loadLastURLSetPage() +{ + int lastURLPage = kapp->config()->readNumEntry("lastURLPage"); + m_view->setCurrentPage(lastURLPage); +} + +void BookReader::addBookmark() +{ + bool isOk; + QString name = KInputDialog::getText(tr("Add bookmark"), + tr("Bookmark name"), tr("Here"), &isOk); + if (isOk) + { + m_view->addBookmark(name); + updateBookmarks(); + } +} + +/*! + \fn BookReader::updateBookmarks() + */ +void BookReader::updateBookmarks() +{ + unplugActionList("my_bookmarks"); + m_bookmarkActions.clear(); + m_bookmarkActions.setAutoDelete(true); + const Bookmarks & bms = m_view->bookmarks(); + + QSignalMapper *bookmarkMapper = new QSignalMapper(this); + connect(bookmarkMapper, SIGNAL(mapped(int)), this, SLOT(gotoBookmark(int))); + for (Bookmarks::size_type i = 0; i < 9 && i < bms.size(); ++i) + { + const Bookmark & bm = bms[i]; + KAction * action = new KAction(bm.name(), ALT+Key_1 + i); + connect(action, SIGNAL(activated()), bookmarkMapper, SLOT(map())); + m_bookmarkActions.append(action); + bookmarkMapper->setMapping(action, i); + } + plugActionList("my_bookmarks", m_bookmarkActions); +} + + +/*! + \fn BookReader::gotoBookmark(int index) + */ +void BookReader::gotoBookmark(int index) +{ + const Bookmarks & bms = m_view->bookmarks(); + assert(index < bms.size()); + + unsigned int page = bms[index].page(); + m_view->setCurrentPage(page); +} + + + +/*! + \fn BookReader::editBookmarks() + */ +void BookReader::editBookmarks() +{ + /// @todo implement me + BookmarksDlg dialog(m_view->bookmarks()); + if (dialog.exec() == QDialog::Accepted) + { + m_view->setBookmarks(dialog.bookmarks()); + updateBookmarks(); + } +} + + +#include "bookreader.moc" diff --git a/src/bookreader.desktop b/src/bookreader.desktop new file mode 100644 index 0000000..e946f19 --- /dev/null +++ b/src/bookreader.desktop @@ -0,0 +1,39 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=BookReader +Exec=bookreader %i %m -caption "%c" +Icon=bookreader +Type=Application +DocPath=bookreader/bookreader.html +Comment=A KDE KPart Application +Comment[br]=Ur meziant Kpart evit KDE +Comment[ca]=Una aplicació KPart per a KDE +Comment[cy]=Cymhwysiad KPart KDE +Comment[da]=Et KDE KPart program +Comment[de]=Eine KDE-Komponenten-Anwendung +Comment[el]=Μια εφαρμογή KPart του KDE +Comment[es]=Una aplicación KPart de KDE +Comment[et]=KDE KPart rakendus +Comment[eu]=KDE KPart aplikazio bat +Comment[fr]=Une application KPart pour KDE. +Comment[ga]=Feidhmchlár KPart KDE +Comment[hi]=एक केडीई के-पार्ट अनुप्रयोग +Comment[hu]=KPart-alapú KDE-alkalmazás +Comment[is]=KDE KPart forrit +Comment[it]=Applicazione KPart di KDE +Comment[ja]=KDE KPart アプリケーション +Comment[lt]=KDE KPart programa +Comment[nb]=Et KDE KPart-program +Comment[nl]=Een KDE KPart-toepassing +Comment[pl]=Osadzalny element KPart KDE +Comment[pt]=Uma aplicação KPart do KDE +Comment[pt_BR]=Um Aplicativo KPart do KDE +Comment[ru]=Приложение KPart для KDE +Comment[sl]=Program KPart za KDE +Comment[sr]=KDE KPart програм +Comment[sr@Latn]=KDE KPart program +Comment[sv]=Ett KDE Kpart-program +Comment[ta]=கெடி கெபார்ட் பயன்பாடு +Comment[tg]=Ба кор андохтани KPart барои KDE +Comment[zh_CN]=一个 KDE KPart 应用程序 +Terminal=false diff --git a/src/bookreader.h b/src/bookreader.h new file mode 100644 index 0000000..989d099 --- /dev/null +++ b/src/bookreader.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright (C) 2005 by Alexandr Nemish * + * atlanter@gmail.com * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef _BOOKREADER_H_ +#define _BOOKREADER_H_ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kapplication.h> +#include <kmainwindow.h> +#include "bookwidget.h" + + +class KPrinter; +class KURL; +class KRecentFilesAction; +class KToggleFullScreenAction; +class KSplashScreen; + +/** + * This class serves as the main window for BookReader. It handles the + * menus, toolbars, and status bars. + * + * @short Main window class + * @author Alexandr Nemish <atlanter@gmail.com> + */ +class BookReader : public KMainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + BookReader(); + + /** + * Default Destructor + */ + virtual ~BookReader(); + + /** + * Use this method to load whatever file/URL you have + */ + void load(const KURL& url); + void loadLastUrl(); + void updateBookmarks(); + +protected: + /** + * Overridden virtuals for Qt drag 'n drop (XDND) + */ + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual bool queryExit(); + +protected: + /** + * This function is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfig *); + + /** + * This function is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(KConfig *); + + +private slots: + void fileOpen(); + void fileSave(); + void fileSaveAs(); + void filePrint(); + void optionsPreferences(); + void slotURLSelected(const KURL &); + void fullScreen(); + void changeStatusbar(const QString& text); + void changeCaption(const QString& text); + void loadSettings(); + void loaded(int); + void gotoFirstPage(); + void nextPage(); + void prevPage(); + void gotoLastPage(); + void gotoPage(); + void loadLastURLSetPage(); + void addBookmark(); + void gotoBookmark(int index); + void editBookmarks(); +private: + void setupAccel(); + void setupActions(); + void readSettings(); + void writeSettings(); +private: + BookWidget * m_view; + //Actions + KRecentFilesAction * recentFilesAction; + KToggleFullScreenAction * m_fullScreenAction; + QPtrList<KAction> m_bookmarkActions; + KURL lastURL; + KSplashScreen * m_splash; + KPrinter * m_printer; +}; + +#endif // _BOOKREADER_H_ + diff --git a/src/bookreader.kcfg b/src/bookreader.kcfg new file mode 100644 index 0000000..efec977 --- /dev/null +++ b/src/bookreader.kcfg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="UTF-8"?> +<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0 + http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" > + <kcfgfile name="bookreaderrc"/> + <group name="Preferences"> + <entry name="Font" type="Font"> + <label>The font used to display the contents of books.</label> + <default code="true">KGlobalSettings::generalFont()</default> + </entry> + <entry name="ParaOffset" type="Int"> + <label>This is an offset of paragraph's first line in pixels</label> + <default>50</default> + </entry> + <entry name="DefaultEncoding" type="Int"> + <label>This is a default encoding</label> + </entry> + <entry name="LoadLastUrl" type="Bool"> + <label>This is ...</label> + <default>false</default> + </entry> + </group> +</kcfg> diff --git a/src/bookreader.lsm b/src/bookreader.lsm new file mode 100644 index 0000000..333b057 --- /dev/null +++ b/src/bookreader.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: BookReader -- Some description +Version: 0.1 +Entered-date: +Description: +Keywords: KDE Qt +Author: Alexandr Nemish <atlanter@gmail.com> +Maintained-by: Alexandr Nemish <atlanter@gmail.com> +Home-page: +Alternate-site: +Primary-site: ftp://ftp.kde.org/pub/kde/unstable/apps/utils + xxxxxx bookreader-0.1.tar.gz + xxx bookreader-0.1.lsm +Platform: Linux. Needs KDE +Copying-policy: GPL +End diff --git a/src/bookreader_client.cpp b/src/bookreader_client.cpp new file mode 100644 index 0000000..d07eb27 --- /dev/null +++ b/src/bookreader_client.cpp @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2005 by Alexander Nemish * + * atlanter@gmail.com * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include <kapplication.h> +#include <dcopclient.h> +#include <qdatastream.h> +#include <qstring.h> + +int main(int argc, char **argv) +{ + KApplication app(argc, argv, "bookreader_client", false); + + // get our DCOP client and attach so that we may use it + DCOPClient *client = app.dcopClient(); + client->attach(); + + // do a 'send' for now + QByteArray data; + QDataStream ds(data, IO_WriteOnly); + if (argc > 1) + ds << QString(argv[1]); + else + ds << QString("http://www.kde.org"); + client->send("bookreader", "BookReaderIface", "openURL(QString)", data); + + return app.exec(); +} diff --git a/src/bookreaderiface.h b/src/bookreaderiface.h new file mode 100644 index 0000000..6f733ec --- /dev/null +++ b/src/bookreaderiface.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * Copyright (C) 2005 by Alexandr Nemish * + * atlanter@gmail.com * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifndef _BOOKREADERIFACE_H_ +#define _BOOKREADERIFACE_H_ + +#include <dcopobject.h> + +class BookReaderIface : virtual public DCOPObject +{ + K_DCOP +public: + +k_dcop: + virtual void openURL(QString url) = 0; +}; + +#endif // _BOOKREADERIFACE_H_ diff --git a/src/bookreaderui.rc b/src/bookreaderui.rc new file mode 100644 index 0000000..65c25af --- /dev/null +++ b/src/bookreaderui.rc @@ -0,0 +1,19 @@ +<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd"> +<kpartgui version="1" name="bookreader" > + <MenuBar> + <Menu name="bookmarks" > + <ActionList name="my_bookmarks" /> + <Action name="copy" /> + </Menu> + </MenuBar> + <ToolBar noMerge="1" name="mainToolBar" > + <text>Main Toolbar</text> + <Action name="file_open" /> + <Action name="file_save" /> + <Action name="file_print" /> + <Separator weakSeparator="1" name="separator_0" /> + <Action name="go_previous" /> + <Action name="go_next" /> + <Action name="fullscreen" /> + </ToolBar> +</kpartgui> diff --git a/src/bookwidget.cpp b/src/bookwidget.cpp new file mode 100644 index 0000000..8eec9df --- /dev/null +++ b/src/bookwidget.cpp @@ -0,0 +1,473 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include <qpainter.h> +#include <qevent.h> +#include <qimage.h> +#include <kdebug.h> +#include <kprogress.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qtextcodec.h> +#include <qstring.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include "bookwidget.h" +#include "theme.h" +#include "settings.h" +#include "renderer.h" +//#include "ktextprogressdlg.h" + +class QStringList; +class QRect; +class QPixmap; + +BookWidget::BookWidget(QWidget *parent, const char *name) + : QWidget(parent, name), + m_modified(true), + m_currentPage(0), + m_left_margin(0), + m_right_margin(0), + m_top_margin(0), + m_bottom_margin(0), + m_middle_margin(0), + m_encoding(0), + m_renderer(new Renderer) +{ + //setupPageSize(); + //setupSlots(); + m_left_margin = 30; + m_right_margin = 30; + m_top_margin = 20; + m_bottom_margin = 15; + m_middle_margin = 20; + + setFont(Settings::font()); + + Theme::self()->loadTheme("default"); + + setWFlags(Qt::WNoAutoErase); + setFocusPolicy(QWidget::StrongFocus); + + connect(&m_timer, SIGNAL(timeout()), this, SLOT(loadLine())); + connect(m_renderer.get(), SIGNAL(renderingFinished()), this, SLOT(renderingFinished())); +} + + +BookWidget::~BookWidget() +{ +} + +void BookWidget::openURL(const KURL & url) +{ + m_url = url; + m_file.setName(m_url.path()); + if (! m_file.open(IO_ReadOnly)) return; + + ///@todo Write encoding detection + m_stream.reset(new QTextStream(&m_file)); + m_textLines.reset(new QStringList); + QString name(encoding()); + QTextCodec *codec = QTextCodec::codecForName(encoding()); // get the codec + if (codec) + m_stream->setCodec(codec); + //show progress dialog + m_progressDlg.reset(new KProgressDialog(this, "progressDlg", "Loading...", + "Loading file ", true)); + m_progressDlg->setLabel("Loading file " + m_url.path()); + m_progressDlg->setAllowCancel(true); + m_progressDlg->progressBar()->setTotalSteps(0); + connect(m_progressDlg.get(), SIGNAL(cancelClicked()), SLOT(cancelLoading())); + KDialog::centerOnScreen(m_progressDlg.get()); + m_progressDlg->show(); + //start timer for processing gui events + m_timer.start(0, false); + //load bookmark for the book + loadBookmarks(); + // just for fun, set the status bar + emit signalChangeStatusbar(m_url.prettyURL()); +} + +QString BookWidget::currentURL() +{ + return m_url.path(); +} + +void BookWidget::drawContent(QPainter& paint) +{ + const QRect lrect (rectLeftPage()); + const QRect rrect(rectRightPage()); + const unsigned int curPage = currentPage(); + //draw pages + m_renderer->drawPage(paint, lrect, curPage); + m_renderer->drawPage(paint, rrect, curPage + 1); +} + +void BookWidget::drawPageNumbers(QPainter & paint) +{ + if (m_renderer->isEmpty()) + return; + const QRect lrect (rectLeftPage()); + const QRect rrect(rectRightPage()); + const QString number = QString::number(currentPage() + 2); + // setup font + QFont pageFont(font()); + int pointSize = 10; + pageFont.setPointSize(pointSize); + pageFont.setBold(false); + pageFont.setItalic(false); + pageFont.setUnderline(false); + paint.setFont(pageFont); + const QFontMetrics fm(pageFont); + const int width = fm.width(number); + //paint page numbers + const int offset = 8; + paint.drawText(lrect.left(), QWidget::height() - offset, + QString::number(currentPage() + 1)); + paint.drawText(rrect.left() + rrect.width() - width, + QWidget::height() - offset, number); +} + +void BookWidget::drawBookmark(QPainter & paint, Bookmark const& bm) +{ + const QRect lrect (rectLeftPage()); + const QRect rrect(rectRightPage()); + const QPixmap & bookmarkImage = Theme::self()->bookmarkPixmap(); + const int width = bookmarkImage.width(); + paint.drawPixmap(QWidget::width()/2 - width/2, 0, bookmarkImage); + if (!bm.name().isEmpty()) + { + paint.save(); + paint.translate(QWidget::width()/2., 20.); + paint.rotate(90.); + const QString text = bm.name() + " (" + bm.dateTime().toString(Qt::LocalDate) + ")"; + paint.drawText(0, 0, text); + paint.restore(); + } +} + +void BookWidget::paintEvent(QPaintEvent* event) +{ + // use cached page pixmap if the page wasn't modified + if (!modified()) + { + QRect rect(event->rect()); + bitBlt(this, rect.x(), rect.y(), &m_cachePixmap, rect.x(), rect.y(), rect.width(), rect.height()); + return; + } + // do full painting otherwise + setupPageSize(); + const QRect lrect (rectLeftPage()); + const QRect rrect(rectRightPage()); + const unsigned int curPage = currentPage(); + QPixmap pm(size()); + QPainter paint(&pm); + //draw background image + const QPixmap & image = Theme::self()->bgPixmap(QWidget::size()); + paint.drawPixmap(0, 0, image); + if (m_renderer->busy()) + { + QFont f(font()); + f.setPointSize(20); + paint.setFont(f); + const QFontMetrics fm(f); + QString text = tr("Rendering..."); + const int w = fm.width(text); + const int h = fm.height(); + const int dx = (lrect.width() - w) / 2; + const int dy = (lrect.height() - h) / 2; + paint.drawText(lrect.x() + dx, lrect.y() + dy, text); + paint.drawText(rrect.x() + dx, rrect.y() + dy, text); + } + else + { + paint.setFont(font()); + //draw pages + drawContent(paint); + drawPageNumbers(paint); + //draw bookmark if it is + typedef Bookmarks::const_iterator IT; + IT it = std::lower_bound(m_bookmarks.begin(), m_bookmarks.end(), curPage); + if (it != m_bookmarks.end() && (*it).page() == curPage) + drawBookmark(paint, *it); + } + paint.end(); + bitBlt(this, 0, 0, &pm); + // store painted pixmap as cache + m_cachePixmap = pm; + m_modified = false; +} + +void BookWidget::setupPageSize() +{ + QSize size((width() - m_left_margin - m_right_margin - 2 * m_middle_margin) / 2, + (height() - m_top_margin - m_bottom_margin)); + m_renderer->setPageSize(size); +} + +void BookWidget::mousePressEvent(QMouseEvent * event) +{ + if (event->button() == LeftButton) + { + if (rectLeftPage().contains(event->pos())) + { + prevPage(); + } + else if (rectRightPage().contains(event->pos())) + { + nextPage(); + } + } + QWidget::mousePressEvent(event); +} + +const QRect BookWidget::rectLeftPage() const +{ + return QRect(m_left_margin, m_top_margin, + m_renderer->pageSize().width(), + m_renderer->pageSize().height()); +} + +const QRect BookWidget::rectRightPage() const +{ + return QRect(m_left_margin + 2 * m_middle_margin + m_renderer->pageSize().width(), + m_top_margin, + m_renderer->pageSize().width(), + m_renderer->pageSize().height()); +} + +void BookWidget::clearAll() +{ + setCurrentPage(0); +} + + +void BookWidget::prevPage() +{ + setCurrentPage(currentPage() - 2); +} + +void BookWidget::nextPage() +{ + setCurrentPage(currentPage() + 2); +} + +void BookWidget::firstPage() +{ + setCurrentPage(0); +} + +void BookWidget::lastPage() +{ + setCurrentPage(m_renderer->pageCount() - 1); +} + +void BookWidget::wheelEvent(QWheelEvent * e) +{ + e->accept(); + if (e->delta() > 0) + prevPage(); + else nextPage(); +} + +void BookWidget::resizeEvent(QResizeEvent * e) +{ + m_modified = true; + setupPageSize(); + m_renderer->render(); +// //if size become lower recalc current page +// if (currentPage() > m_renderer->pageCount()) +// lastPage(); + QWidget::resizeEvent(e); +} + +QSize BookWidget::minimumSizeHint() const +{ + return QSize(400, 300); +} + +void BookWidget::setFont(const QFont & font) +{ + m_modified = true; + QWidget::setFont(font); + m_renderer->setFont(font); + update(); +} + +void BookWidget::setCurrentPage(int number) +{ + // do nothing while renderer is working + if (m_renderer->busy()) + return; + if (number >= m_renderer->pageCount()) + number = m_renderer->pageCount() - 1; + if (number < 0) number = 0; + //only even page numbers allowed + int newPage = (number % 2) ? number - 1 : number; + if (newPage != m_currentPage) + m_modified = true; + m_currentPage = newPage; + update(); +} + +void BookWidget::keyPressEvent(QKeyEvent * e) +{ + if (e->key() == Qt::Key_Right || e->key() == Qt::Key_Down + || e->key() == Qt::Key_Space) + { + nextPage(); + } + else if (e->key() == Qt::Key_Left || e->key() == Qt::Key_Up) + { + prevPage(); + } + else if (e->key() == Qt::Key_Home) + { + firstPage(); + } + else if (e->key() == Qt::Key_End) + { + lastPage(); + } + + QWidget::keyPressEvent(e); +} + +void BookWidget::loadLine() +{ + static int lineCount = 0; + if (!m_stream->eof()) + { + *m_textLines << m_stream->readLine(); + //progress update after every 50 lines + if (!(++lineCount % 50)) + m_progressDlg->progressBar()->setProgress( + m_progressDlg->progressBar()->progress() + 1); + return; + } + m_timer.stop(); + //render + m_renderer->load(*m_textLines); + //setup and show + firstPage(); + //clean up + m_progressDlg.reset(0); + m_stream.reset(0); + m_textLines.reset(0); + m_file.close(); + m_modified = true; + emit loadingFinished(); +} + +void BookWidget::cancelLoading() +{ + m_timer.stop(); + if (parentWidget()) + parentWidget()->setCaption(""); + //clean up + m_progressDlg->hide(); + m_stream.reset(0); + m_textLines.reset(0); + m_file.close(); + //setup and show + m_renderer->clear(); + firstPage(); +} + +void BookWidget::setParaOffset(int offset) +{ + m_modified = true; + m_renderer->setParaOffset(offset); + update(); +} + +void BookWidget::setEncoding(int a_encoding) +{ + m_encoding = a_encoding; +} + +void BookWidget::setEncodings(const QStringList & a_encodings) +{ + m_encodings = a_encodings; +} + +void BookWidget::setEncoding(const QString & a_encoding) +{ + m_encoding = (m_encodings.findIndex(a_encoding)); +} + +void BookWidget::addBookmark(const QString& name) +{ + m_bookmarks.push_back(Bookmark(currentPage(), name)); + std::sort(m_bookmarks.begin(), m_bookmarks.end()); + +// KAction menu = new KAction() + + saveBookmarks(); +} + +void BookWidget::saveBookmarks( ) +{ + const QString fileName = "bookreader/" + m_url.fileName(); + QFile::remove(KGlobal::dirs()->findResource("appdata", m_url.fileName())); + KConfig config(fileName, false, false, "data"); + for (Bookmarks::size_type i = 0; i < m_bookmarks.size(); ++i) + { + const Bookmark & bm = m_bookmarks[i]; + config.setGroup(tr("Bookmark %1").arg(i)); + config.writeEntry("page", bm.page()); + config.writeEntry("name", bm.name()); + config.writeEntry("dateTime", bm.dateTime()); + } +} + +void BookWidget::loadBookmarks() +{ + m_bookmarks.clear(); + const QString fileName = "bookreader/" + m_url.fileName(); + KConfig config(fileName, true, false, "data"); + QStringList groups = config.groupList(); + typedef QStringList::const_iterator IT; + for (IT it = groups.constBegin(); it != groups.constEnd(); ++it) + { + config.setGroup(*it); + const QString name = config.readEntry("name"); + const unsigned int page = config.readUnsignedNumEntry("page"); + const QDateTime dateTime = config.readDateTimeEntry("dateTime"); + const Bookmark bm(page, name, dateTime); + m_bookmarks.push_back(bm); + } + std::sort(m_bookmarks.begin(), m_bookmarks.end()); +} + +void BookWidget::setBookmarks(const Bookmarks & bms) +{ + m_bookmarks.clear(); + m_bookmarks = bms; + std::sort(m_bookmarks.begin(), m_bookmarks.end()); + saveBookmarks(); +} + +void BookWidget::renderingFinished() +{ + m_modified = true; + setCurrentPage(currentPage()); + update(); +} + +#include "bookwidget.moc" diff --git a/src/bookwidget.h b/src/bookwidget.h new file mode 100644 index 0000000..8089b3d --- /dev/null +++ b/src/bookwidget.h @@ -0,0 +1,125 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef BOOKWIDGET_H +#define BOOKWIDGET_H + +#include <qwidget.h> +#include <qevent.h> +#include <qpixmap.h> +#include <qthread.h> +#include <qtimer.h> +#include <qfile.h> +#include <kurl.h> +#include <algorithm> +#include "renderer.h" +#include "bookmark.h" + +class QStringList; +class QRect; +class KProgressDialog; + + +/** + */ +class BookWidget : public QWidget +{ + Q_OBJECT +public: + + + BookWidget(QWidget *parent = 0, const char *name = 0); + ~BookWidget(); + + virtual void openURL(const KURL & url); + virtual QString currentURL(); + void paintEvent (QPaintEvent *); + void setFont( const QFont & ); + void setParaOffset(int offset); + void setCurrentPage(int number); + int currentPage() const { return m_currentPage; }; + int pageCount() const { return m_renderer->pageCount(); } + void nextPage(); + void prevPage(); + void firstPage(); + void lastPage(); + QSize minimumSizeHint () const; + void setupPageSize(); + void setEncoding(int); + void setEncoding(const QString & a_encoding); + void setEncodings(const QStringList & a_encodings); + QString encoding() const { return m_encodings[m_encoding]; } + //const TextThread * thread() const {return m_thread;} + void addBookmark(const QString& name); + void saveBookmarks(); + void loadBookmarks(); + void setBookmarks(const Bookmarks& bms); + const Bookmarks & bookmarks() const { return m_bookmarks; } +signals: + /** + * Use this signal to change the content of the statusbar + */ + void signalChangeStatusbar(const QString& text); + + /** + * Use this signal to change the content of the caption + */ + void signalChangeCaption(const QString& text); + + void loaded(int); + void loadingFinished(); +protected: + const QRect rectLeftPage() const; + const QRect rectRightPage() const; + void mousePressEvent(QMouseEvent *); + void keyPressEvent (QKeyEvent * e); + void resizeEvent (QResizeEvent * e); + void wheelEvent (QWheelEvent * e); + void clearAll(); + void drawContent(QPainter& paint); + void drawPageNumbers(QPainter& paint); + void drawBookmark(QPainter& paint, Bookmark const& bm); + bool modified() const { return m_modified; }; + +private slots: + void loadLine(); + void cancelLoading(); + void renderingFinished(); + +private: + KURL m_url; + bool m_modified; + QPixmap m_cachePixmap; + int m_currentPage; + int m_left_margin; + int m_right_margin; + int m_top_margin; + int m_bottom_margin; + int m_middle_margin; + QFile m_file; + QTimer m_timer; + QStringList m_encodings; + int m_encoding; + std::auto_ptr<Renderer> m_renderer; + std::auto_ptr<QTextStream> m_stream; + std::auto_ptr<QStringList> m_textLines; + std::auto_ptr<KProgressDialog> m_progressDlg; + Bookmarks m_bookmarks; +}; + +#endif diff --git a/src/hi16-app-bookreader.png b/src/hi16-app-bookreader.png Binary files differnew file mode 100644 index 0000000..90705d9 --- /dev/null +++ b/src/hi16-app-bookreader.png diff --git a/src/hi32-app-bookreader.png b/src/hi32-app-bookreader.png Binary files differnew file mode 100644 index 0000000..73418b9 --- /dev/null +++ b/src/hi32-app-bookreader.png diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..73504d2 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2005 by Alexander Nemish * + * atlanter@gmail.com * + * * + * 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 program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include <kapplication.h> +#include <dcopclient.h> +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <klocale.h> +#include <ksplashscreen.h> +#include "bookreader.h" + +static const char description[] = I18N_NOOP("A KDE Application"); +static const char version[] = "0.2"; +static KCmdLineOptions options[] = +{ + { "[URL]", I18N_NOOP("Document to open"), 0 }, + KCmdLineLastOption +}; + +int main(int argc, char **argv) +{ + KAboutData about("bookreader", I18N_NOOP("BooKreader"), version, description, + KAboutData::License_GPL, + "(C) 2005-2007 Alexander Nemish", 0, 0, "atlanter@gmail.com"); + about.addAuthor("Alexander Nemish", 0, "atlanter@gmail.com"); + KCmdLineArgs::init(argc, argv, &about); + KCmdLineArgs::addCmdLineOptions(options); + KApplication app; + + // register ourselves as a dcop client + app.dcopClient()->registerAs(app.name(), false); + + // see if we are starting with session management + if (app.isRestored()) + { + RESTORE(BookReader); + } + else + { + // no session.. just start up normally + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + BookReader *widget = new BookReader; + app.setMainWidget(widget->centralWidget()); + if (args->count() == 1) + widget->load(args->url(0)); + widget->show(); + args->clear(); + } + return app.exec(); +} diff --git a/src/renderer.cpp b/src/renderer.cpp new file mode 100644 index 0000000..a04b0d0 --- /dev/null +++ b/src/renderer.cpp @@ -0,0 +1,398 @@ +/*************************************************************************** +* Copyright (C) 2005 - 2007 by Alexander Nemish * +* atlanter@gmail.com * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include <kdebug.h> +#include <qfontmetrics.h> +#include <qpainter.h> +#include <vector> +#include <cmath> +#include <iostream> +#include "renderer.h" + +Renderer::Renderer(): + m_pageCount(0), + m_linesPerPage(0), + m_paraOffset(50), + m_fontMetrics(new QFontMetrics(m_font)), + m_curParagraph(0), + m_isRendering(false) +{ + connect(&m_timer, SIGNAL(timeout()), this, SLOT(timeout())); +} + + +Renderer::~Renderer() +{ +} + +/** +* If the list is empty do nothing. +* \param a_list List of strings to render +*/ +void Renderer::load(const QStringList & a_list) +{ + if (a_list.isEmpty()) + return; + m_text.clear(); + + QString string; + TStringIter it(a_list.constBegin()); + + while (it != a_list.constEnd()) + { + //skip spaces; + string = (*it).stripWhiteSpace(); + //process string until paragraph ends + while (++it != a_list.constEnd()) + { + QChar const ch((*it)[0]); + //insert empty lines + if ((*it).isEmpty()) + break; + //check whether paragraph not ends + if (!ch.isSpace() && (ch != '-')) + { + string += " " + *it; + } + else break; + } + // add paragraph + m_text.push_back(string); + //add empty line if we have one + if ((*it).isEmpty()) + m_text.push_back(""); + } + render(); +} + +/** +* \note You need manually update your widget. Like +* \code +* renderer->render(); +* update(); +* \endcode +*/ +void Renderer::render() +{ + if (isEmpty()) + return; + if (busy()) + cancel(); + clear(); + m_isRendering = true; + m_linesPerPage = m_pageSize.height() / (QFontMetrics(font()).height()); + m_timer.start(0, false); +} + +void Renderer::timeout() +{ + if (m_curParagraph < m_text.size()) + { + parseParagraph(m_curParagraph); + m_curParagraph++; + } + else + { + cancel(); + emit renderingFinished(); + } + m_pageCount = m_linesPerPage ? (m_lines.size() / m_linesPerPage) + 1 : 0; +} + +void Renderer::cancel() +{ + m_timer.stop(); + m_curParagraph = 0; + m_isRendering = false; +} + +/** + * Call it for simple view clearing. +* \code +* renderer->clear(); +* update(); +* \endcode +*/ +void Renderer::clear() +{ + m_lines.clear(); + m_pageCount = 0; + m_linesPerPage = 0; +} + +/** +* \param a_string Line to parse +*/ +void Renderer::parseParagraph(TIndex paraIndex) +{ + //Don't parse empty lines + const QString a_string(m_text[paraIndex]); + if (a_string.isEmpty()) + return; + + QString string(a_string); + const int avgCharWidth = m_fontMetrics->width(" "); + //calc approx string width + unsigned int avgLen = m_pageSize.width() / avgCharWidth; + unsigned int len; + int begin = 0; + m_isStartAdded = false; + int curWidth = width(string); + + // whole paragraph in single line + if (curWidth <= m_pageSize.width()) + { + addLine(TextLine(paraIndex, 0, a_string.length(), TextLine::PARA_BOTH)); + return; + } + + for (; curWidth > m_pageSize.width(); curWidth = width(string)) + { + len = avgLen; + //turn left if missed + int w = width(string.left(len)); + for (; (w > m_pageSize.width()) && (len > 0); w = width(string.left(len))) + { + int diff = w - m_pageSize.width(); + int charDiff = diff / 10; + if (charDiff > len) + charDiff = charDiff % len; + charDiff = (charDiff == 0) ? 1 : charDiff; + len -= charDiff; + } + //turn right if missed + while ((width(string.left(len)) <= m_pageSize.width()) + && (len < string.length())) + ++len; + --len; + + // update statistics on average length + avgLen = (unsigned int) (len / (1 + 1. / avgLen)); + + //check whether we in a word + if (!(string[len - 1].isSpace() || string[len].isSpace())) + { + //find last word start index + const int wordBegin = wordAt(string, len); + //check whether its width less than page width + if (width(getWord(string, wordBegin)) <= m_pageSize.width()) + { + //if so, move last word to the next line + // invariant: wordBegin > 0, + // because otherwise the word is greater than page width + + // i points to a space before the word + int i = wordBegin -1; + // skip spaces + while (i && string[i].isSpace()) + --i; + addLine(TextLine(paraIndex, begin, begin + i + 1)); + string = string.right(string.length() - wordBegin); + begin += wordBegin; + } + else + { + //if its width greater than page width - split it + addLine(TextLine(paraIndex, begin, begin + len)); + string = string.right(string.length() - len); + begin += len; + } + } + // line ends with spaces and next line begins with spaces, + // trim them + else if (string[len - 1].isSpace() && string[len].isSpace()) + { + int idx = len - 1; + while (string[--idx] == ' '); + addLine(TextLine(paraIndex, begin, begin + idx + 1)); + idx = len; + while (string[++idx] == ' '); + string = string.right(string.length() - idx); + begin += idx; + } + else if (string[len - 1].isSpace()) + { + int idx = len - 1; + while (string[--idx] == ' '); + addLine(TextLine(paraIndex, begin, begin + idx + 1)); + string = string.right(string.length() - len); + begin += len; + } + else if (string[len].isSpace()) + { + int idx = len; + while (string[++idx] == ' '); + addLine(TextLine(paraIndex, begin, begin + len)); + string = string.right(string.length() - idx); + begin += idx; + } + } + //last line in multiline para. + len = string.length(); + addLine(TextLine(paraIndex, begin, begin + len, TextLine::PARA_END)); +} + +int Renderer::width(const QString & a_string) const +{ + int w = m_fontMetrics->width(a_string); + return m_isStartAdded ? w : w + paraOffset(); +} + +void Renderer::addLine(TextLine line) +{ + if (!m_isStartAdded) + line.setParagraphFlags(line.paragraphFlags() | TextLine::PARA_START); + m_lines.push_back(line); + m_isStartAdded = true; +} + +int Renderer::wordAt(const QString & string, int len) +{ + while (--len >= 0) + if (string[len] == ' ') + return ++len; + return 0; +} + +QString Renderer::getWord(const QString & a_string, int a_idx) +{ + int idx = a_idx; + while (a_string[++idx] != ' ' && idx < a_string.length()); + return QString(a_string.mid(a_idx, idx - a_idx)); +} + +/** +* Draws page number \c pageNumber on \c paint +* bounding \c rect rectangle +*/ +void Renderer::drawPage(QPainter & paint, QRect rect, int pageNumber) +{ + int height = m_fontMetrics->height(); + int line = 1; + TLines::size_type count = m_lines.size(); + + if (count == 0) + return; + + if ((pageNumber * m_linesPerPage) >= count) + return; + + TLines::size_type idx = pageNumber * m_linesPerPage; + for (; (line <= m_linesPerPage) && idx < m_lines.size(); ++idx, ++line) + drawLine(paint, rect.left(), rect.top() + line * height, idx); +} + +void Renderer::drawLine(QPainter & paint, int x, int y, const TLines::size_type index) +{ + const TextLine textLine(m_lines[index]); + const int length = textLine.size(); + const QString & paragraph = m_text[textLine.paragraphIndex()]; + const QString string = paragraph.mid(textLine.begin(), textLine.size()); + + // indent paragraph + if (textLine.isParaStart()) + x += paraOffset(); + + // don't justify on paragraph end + if (textLine.isParaEnd()) + { + paint.drawText(x, y, string); + return; + } + + const int pageWidth = m_pageSize.width(); + const int spaceWidth = m_fontMetrics->width(" "); + int width = m_fontMetrics->width(string); + const int curWidth = textLine.isParaStart() ? width + paraOffset() : width; + + if (curWidth == pageWidth) + { + paint.drawText(x, y, string); + } + + // string width is lower than page width. justify by space width + long pos = 0, off = 0; + + //count spaces + std::vector<int> spaces; + spaces.reserve(50); + while (((pos = string.find(' ', off)) != -1) && (pos < length)) + { + spaces.push_back(pos); + off = pos + 1; + } + const std::vector<int>::size_type spacesCount = spaces.size(); + // no spaces no justifications + if (!spacesCount) + { + paint.drawText(x, y, string); + return; + } + // justify line + double x1 = x; + // calc average space width + const double avgLen = ((double)(pageWidth - curWidth) / spacesCount); + int start = 0; + QString tmp; + for (std::vector<int>::size_type i = 0; i < spacesCount; ++i) + { + pos = spaces[i]; + tmp = string.mid(start, pos - start); + paint.drawText((int)std::ceil(x1), y, tmp); + x1 += m_fontMetrics->width(tmp); + x1 += spaceWidth + avgLen; + start = pos + 1; + } + //last chunk + paint.drawText((int)std::ceil(x1), y, string.right(length - start)); +} + +/** + * Sets current font to \c font and re-renders text. + * You don't need to directly call render(). + */ +void Renderer::setFont(const QFont & font) +{ + if (font == m_font) return; + m_font = font; + m_fontMetrics.reset(new QFontMetrics(m_font)); + render(); +} + +/** +* Sets current paragraph offset to \c offset +* and re-renders text if it's changed. +*/ +void Renderer::setParaOffset(int offset) +{ + if (offset == m_paraOffset) return; + m_paraOffset = offset; + render(); +} + +/** + * Sets current page size to \c size. + * \note It don't call render() after changing. + */ +void Renderer::setPageSize(const QSize & size) +{ + m_pageSize = size; +} + +#include "renderer.moc" diff --git a/src/renderer.h b/src/renderer.h new file mode 100644 index 0000000..23263ca --- /dev/null +++ b/src/renderer.h @@ -0,0 +1,138 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* atlanter@gmail.com * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef RENDERER_H +#define RENDERER_H + +#include <qstringlist.h> +#include <qfont.h> +#include <qstring.h> +#include <qobject.h> +#include <qsize.h> +#include <qtimer.h> +#include <vector> +#include <memory> +class QStringList; + +class TextLine +{ +public: + static const int PARA_NONE = 0; + static const int PARA_START = 1; + static const int PARA_END = 2; + static const int PARA_BOTH = 3; + + TextLine(int index, int begin, int end, int paraFlags = PARA_NONE): + m_paragraphFlags(paraFlags), + m_begin(begin), + m_end(end), + m_index(index) + {} + bool isParaStart() const { return m_paragraphFlags & PARA_START; } + bool isParaEnd() const { return m_paragraphFlags & PARA_END; } + void setParagraphFlags(int flags) { m_paragraphFlags = flags; } + const int paragraphFlags() const { return m_paragraphFlags; } + const int paragraphIndex() const { return m_index; } + const int begin() const { return m_begin; } + const int end() const { return m_end; } + const int size() const { return m_end - m_begin; } +private: + int m_paragraphFlags; + int m_begin; + int m_end; + int m_index; +}; + +/** +* \brief Renders input text into list of word wrapped strings +* \author Alexandr Nemish <anemish@gmail.com> +*/ +class Renderer : public QObject +{ + Q_OBJECT +public: + Renderer(); + ~Renderer(); + + /// \brief Loads and renders list of strings + void load(const QStringList & list); + /// \brief Renders loaded list of strings + void render(); + /// \brief Clears all rendered data + void clear(); + /// \brief Draws page + void drawPage(QPainter & paint, QRect rect, int pageNumber); + //Getters + /// \brief Returns whether the text is empty + bool isEmpty() const { return m_text.empty(); }; + /// \brief Returns current font + QFont font() const { return m_font; } + /// \brief Returns current paragraph offset in pixels + int paraOffset() const { return m_paraOffset; } + /// \brief Returns the number of pages + int pageCount() const { return m_pageCount; } + /// \brief Returns page size in pixels + QSize pageSize() const { return m_pageSize; } + //Setters + /// \brief Sets current font + void setFont(const QFont & font); + /// \brief Sets current paragraph offset in pixels + void setParaOffset(int offset); + /// \brief Sets size of a page + void setPageSize(const QSize & size); + + bool busy() const { return m_isRendering; } +signals: + void renderingFinished(); +private slots: + void timeout(); +private: + typedef QStringList::const_iterator TStringIter; + typedef std::vector<QString> TParagraphs; + typedef TParagraphs::size_type TIndex; + typedef std::vector<TextLine> TLines; + //make it non-copyable + Renderer(const Renderer &); + Renderer & operator = (const Renderer &); + /// \brief Renders input paragraph into list of wrapped lines + void parseParagraph(TIndex idx); + /// \brief Returns width of string in pixels + int width(const QString & a_string) const; + void addLine(TextLine line); + int wordAt(const QString & str, int len); + QString getWord(const QString & str, int idx); + /// \brief Draws justified string + void drawLine(QPainter& paint, int x, int y, const TLines::size_type idx); + void cancel(); + + TParagraphs m_text; + TLines m_lines; + int m_pageCount; + int m_linesPerPage; + int m_paraOffset; + QFont m_font; + std::auto_ptr<QFontMetrics> m_fontMetrics; + QSize m_pageSize; + bool m_isStartAdded; + TIndex m_curParagraph; + bool m_isRendering; + QTimer m_timer; +}; + +#endif diff --git a/src/settings.kcfgc b/src/settings.kcfgc new file mode 100644 index 0000000..2f6554f --- /dev/null +++ b/src/settings.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=bookreader.kcfg +ClassName=Settings +Singleton=true +Mutators=col_background,col_foreground +# will create the necessary code for setting those variables diff --git a/src/settingsdlg.ui b/src/settingsdlg.ui new file mode 100644 index 0000000..a2a3db8 --- /dev/null +++ b/src/settingsdlg.ui @@ -0,0 +1,167 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>SettingsWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>SettingsDlg</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>405</width> + <height>170</height> + </rect> + </property> + <property name="caption"> + <string>Form1</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame3</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Font</string> + </property> + <property name="buddy" stdset="0"> + <cstring>kFontRequester1</cstring> + </property> + </widget> + <widget class="KFontRequester"> + <property name="name"> + <cstring>kcfg_Font</cstring> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout3</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Paragraph Offset</string> + </property> + </widget> + <widget class="QSpinBox"> + <property name="name"> + <cstring>kcfg_ParaOffset</cstring> + </property> + <property name="value"> + <number>30</number> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout8</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Default encoding</string> + </property> + </widget> + <widget class="QComboBox"> + <property name="name"> + <cstring>kcfg_DefaultEncoding</cstring> + </property> + <property name="editable"> + <bool>false</bool> + </property> + </widget> + </hbox> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>kcfg_LoadLastUrl</cstring> + </property> + <property name="text"> + <string>Load last URL on startup</string> + </property> + <property name="accel"> + <string></string> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>30</height> + </size> + </property> + </spacer> + </vbox> + </widget> + </vbox> +</widget> +<connections> + <connection> + <sender>kcfg_ParaOffset</sender> + <signal>valueChanged(const QString&)</signal> + <receiver>SettingsDlg</receiver> + <slot>kcfg_ParaOffset_valueChanged(const QString&)</slot> + </connection> +</connections> +<slots> + <slot>kcfg_ParaOffset_valueChanged(const QString&)</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kfontrequester.h</includehint> +</includehints> +</UI> diff --git a/src/theme.cpp b/src/theme.cpp new file mode 100644 index 0000000..d6e868c --- /dev/null +++ b/src/theme.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* atlanter@gmail.com * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#include <qstring.h> +#include <qpixmap.h> +#include <qimage.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include "theme.h" + +Theme* Theme::theme = 0; + +Theme::Theme() +{} + + +Theme * Theme::self() +{ + if ( !theme ) + theme = new Theme(); + return theme; +} + +void Theme::loadTheme(const QString& name) +{ + const QString themePath = "themes/" + name; //FIXME: Add 'name' check. Could be a hole. + QString bgPath = KGlobal::dirs()->findResource("appdata", themePath + "/bg.png"); + QString bookmarkPath = KGlobal::dirs()->findResource("appdata", themePath + "/bookmark.png"); + m_bgPixmap = QPixmap(bgPath); + m_bgResizedPixmap = m_bgPixmap; + m_bookmarkPixmap = QPixmap(bookmarkPath); +} + +const QPixmap & Theme::bgPixmap(QSize size) const +{ + if (size == QSize(0, 0) || size == m_bgPixmap.size()) + return m_bgPixmap; + if (size == m_bgResizedPixmap.size()) + return m_bgResizedPixmap; + m_bgResizedPixmap = m_bgPixmap; + m_bgResizedPixmap.convertFromImage(m_bgResizedPixmap.convertToImage().scale(size)); + return m_bgResizedPixmap; +} diff --git a/src/theme.h b/src/theme.h new file mode 100644 index 0000000..fae0f45 --- /dev/null +++ b/src/theme.h @@ -0,0 +1,46 @@ +/*************************************************************************** +* Copyright (C) 2005 by Alexander Nemish * +* atlanter@gmail.com * +* * +* 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 program is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU General Public License for more details. * +* * +* You should have received a copy of the GNU General Public License * +* along with this program; if not, write to the * +* Free Software Foundation, Inc., * +* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * +***************************************************************************/ +#ifndef BOOKREADER_THEME_H +#define BOOKREADER_THEME_H + +#include <qstring.h> +#include <qpixmap.h> + + +class Theme +{ +public: + static Theme * self(); + /// \brief Loads theme with given name. Default is "default". :) + void loadTheme(const QString& name = "default"); + const QPixmap & bgPixmap(QSize size = QSize(0,0)) const; + const QPixmap & bookmarkPixmap() const { return m_bookmarkPixmap; } + //const QFont & font() const {return m_font;} +private: + static Theme * theme; + Theme(); + Theme(const Theme &); + Theme & operator = (const Theme&); + QPixmap m_bgPixmap; + mutable QPixmap m_bgResizedPixmap; + QPixmap m_bookmarkPixmap; +}; + +#endif |
