Sailfish integration

This commit is contained in:
Slava Monich 2015-06-28 14:22:35 +03:00
parent 9629f7bb46
commit e9db1e773a
87 changed files with 12517 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
harbour-books.pro.user
build

3
.gitmodules vendored
View file

@ -4,3 +4,6 @@
[submodule "fbreader/upstream"] [submodule "fbreader/upstream"]
path = fbreader/upstream path = fbreader/upstream
url = https://github.com/geometer/FBReader.git url = https://github.com/geometer/FBReader.git
[submodule "harbour-lib"]
path = harbour-lib
url = https://github.com/monich/harbour-lib.git

5
README.md Normal file
View file

@ -0,0 +1,5 @@
## E-book reader for Sailfish OS
![icon](app/harbour-books.png)
The core functionality is based on [FBReader](https://github.com/geometer/FBReader) source code with very few modifications. Books are imported from the Downloads folder, where they are saved by the browser or email client. Alternatively, you can manually copy your books to the Books directory under the home folder. Removable storage is supported as well. In theory, it should be able to handle all E-book formats supported by FBReader. Tested mostly with epub and fb2.

138
app/app.pro Normal file
View file

@ -0,0 +1,138 @@
TARGET = harbour-books
CONFIG += sailfishapp link_pkgconfig
PKGCONFIG += sailfishapp mlite5 expat glib-2.0
#QT += dbus
!include(../common.pri)
CONFIG(debug, debug|release) {
DEFINES += HARBOUR_DEBUG=1
}
# Directories
FBREADER_DIR = $$_PRO_FILE_PWD_/../fbreader
FRIBIDI_DIR = $$_PRO_FILE_PWD_/../fribidi
LINEBREAK_DIR = $$_PRO_FILE_PWD_/../linebreak
HARBOUR_LIB_DIR = $$_PRO_FILE_PWD_/../harbour-lib
# Libraries
FBREADER_LIB = $$OUT_PWD/../fbreader/libfbreader.a
FRIBIDI_LIB = $$OUT_PWD/../fribidi/libfribidi.a
LINEBREAK_LIB = $$OUT_PWD/../linebreak/liblinebreak.a
HARBOUR_LIB = $$OUT_PWD/../harbour-lib/libharbour-lib.a
PRE_TARGETDEPS += \
$$FBREADER_LIB \
$$FRIBIDI_LIB \
$$LINEBREAK_LIB \
$$HARBOUR_LIB
LIBS += \
$$FBREADER_LIB \
$$FRIBIDI_LIB \
$$LINEBREAK_LIB \
$$HARBOUR_LIB \
-lbz2 -lz -ldl
OTHER_FILES += \
harbour-books.png \
harbour-books.desktop \
qml/*.qml \
qml/images/* \
data/default/* \
data/zlibrary/core/encodings/* \
data/zlibrary/core/resources/* \
translations/*.ts
TARGET_DATA_DIR = /usr/share/harbour-books
TARGET_DEFAULT_DATA_DIR = $$TARGET_DATA_DIR/data
TARGET_ZLIBRARY_DATA_DIR = $$TARGET_DEFAULT_DATA_DIR
core_data.files = \
data/zlibrary/core/*.gz \
data/zlibrary/core/*.zip \
data/zlibrary/core/encodings \
data/zlibrary/core/resources
core_data.path = $$TARGET_ZLIBRARY_DATA_DIR
INSTALLS += core_data
text_data.files = data/zlibrary/text/*.zip
text_data.path = $$TARGET_ZLIBRARY_DATA_DIR
INSTALLS += text_data
defaults.files = data/default/*
defaults.path = $$TARGET_ZLIBRARY_DATA_DIR
INSTALLS += defaults
formats.files = data/formats/*
formats.path = $$TARGET_DEFAULT_DATA_DIR/formats
INSTALLS += formats
CONFIG += sailfishapp_i18n sailfishapp_i18n_idbased
TRANSLATIONS += \
translations/harbour-books.ts \
translations/harbour-books-ru.ts
INCLUDEPATH += \
src \
$$HARBOUR_LIB_DIR/include \
$$FBREADER_DIR/fbreader/fbreader/src \
$$FBREADER_DIR/fbreader/zlibrary/text/include \
$$FBREADER_DIR/fbreader/zlibrary/core/include \
$$FBREADER_DIR/fbreader/zlibrary/core/src/view \
$$FBREADER_DIR/fbreader/zlibrary/core/src/dialogs \
$$FBREADER_DIR/fbreader/zlibrary/core/src/application \
$$FBREADER_DIR/fbreader/zlibrary/core/src/options \
$$FBREADER_DIR/fbreader/zlibrary/core/src/unix \
$$FBREADER_DIR/fbreader/zlibrary/ui/src/qt4
SOURCES += \
src/BooksBook.cpp \
src/BooksBookModel.cpp \
src/BooksConfig.cpp \
src/BooksCoverModel.cpp \
src/BooksCoverWidget.cpp \
src/BooksDialogManager.cpp \
src/BooksImportModel.cpp \
src/BooksListWatcher.cpp \
src/BooksLoadingProperty.cpp \
src/BooksPageWidget.cpp \
src/BooksPaintContext.cpp \
src/BooksSaveTimer.cpp \
src/BooksSettings.cpp \
src/BooksShelf.cpp \
src/BooksStorage.cpp \
src/BooksStorageModel.cpp \
src/BooksTask.cpp \
src/BooksTextStyle.cpp \
src/BooksTaskQueue.cpp \
src/BooksTextView.cpp \
src/libudev.c \
src/main.cpp \
src/ZLApplication.cpp \
src/ZLibrary.cpp
HEADERS += \
src/BooksBook.h \
src/BooksBookModel.h \
src/BooksConfig.h \
src/BooksCoverModel.h \
src/BooksCoverWidget.h \
src/BooksDefs.h \
src/BooksDialogManager.h \
src/BooksImportModel.h \
src/BooksItem.h \
src/BooksListWatcher.h \
src/BooksLoadingProperty.h \
src/BooksPageWidget.h \
src/BooksPaintContext.h \
src/BooksPos.h \
src/BooksSaveTimer.h \
src/BooksSettings.h \
src/BooksShelf.h \
src/BooksStorage.h \
src/BooksStorageModel.h \
src/BooksTask.h \
src/BooksTaskQueue.h \
src/BooksTextView.h \
src/BooksTextStyle.h \
src/BooksTypes.h

1
app/data/default/styles.xml Symbolic link
View file

@ -0,0 +1 @@
../../../fbreader/fbreader/fbreader/data/default/styles.desktop.xml

1
app/data/formats Symbolic link
View file

@ -0,0 +1 @@
../../fbreader/fbreader/fbreader/data/formats

1
app/data/zlibrary/core Symbolic link
View file

@ -0,0 +1 @@
../../../fbreader/fbreader/zlibrary/core/data

1
app/data/zlibrary/text Symbolic link
View file

@ -0,0 +1 @@
../../../fbreader/fbreader/zlibrary/text/data

View file

@ -0,0 +1,8 @@
[Desktop Entry]
Type=Application
Name=Books
Name[ru]=Книги
Comment=E-Book Reader
Exec=harbour-books
Icon=harbour-books
X-Nemo-Application-Type=silica-qt5

BIN
app/harbour-books.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

52
app/harbour-books.svg Normal file
View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 17.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
width="86px"
height="86px"
viewBox="0 0 86 86"
enable-background="new 0 0 86 86"
xml:space="preserve"
id="svg2"
inkscape:version="0.48.2 r9819"><metadata
id="metadata25"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata>
<g
id="Layer_1">
<linearGradient
id="SVGID_1_"
gradientUnits="userSpaceOnUse"
x1="79.2383"
y1="91.3279"
x2="6.7619"
y2="18.8514">
<stop
offset="0"
style="stop-color:#f5d9a8;stop-opacity:1;"
id="stop7" />
<stop
offset="0.99510002"
style="stop-color:#cc9933;stop-opacity:1;"
id="stop9" />
</linearGradient>
<path
fill="url(#SVGID_1_)"
d="M43,0.3C19.417,0.3,0.3,19.418,0.3,43c0,0.408,0,41.277,0,41.277c0,0.786,0.637,1.423,1.423,1.423 h82.553c0.786,0,1.423-0.637,1.423-1.423c0,0,0-40.908,0-41.277C85.7,19.418,66.583,0.3,43,0.3z"
id="path11" />
</g>
<path
inkscape:connector-curvature="0"
d="m 65.244239,25.145325 2.755924,0 0,52.854675 -39.567187,0 C 22.625852,78 17.999837,73.964541 17.999837,67.960564 l 0,-45.768014 c 0,-3.248053 2.657498,-5.90555 5.905551,-5.90555 l 36.614411,0 0,45.374311 -32.086823,0 c -4.035459,0 -6.889809,2.165368 -6.889809,6.102402 l 0,0.196851 c 0,3.937034 2.85435,6.496106 6.889809,6.496106 l 36.811263,0 0,-49.311345 z m -4.72444,44.193201 -31.69312,0 c -0.885832,0 -1.673239,-0.492128 -1.673239,-1.476387 0,-0.787407 0.787407,-1.279537 1.673239,-1.279537 l 31.69312,0 0,2.755924 z"
id="path4"
style="fill:#cc0000;fill-opacity:1" /></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

253
app/qml/BooksBookView.qml Normal file
View file

@ -0,0 +1,253 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
SilicaFlickable {
id: root
property variant book
property variant settings
signal closeBook()
signal pageClicked(var page)
property int _currentPage: bookListWatcher.currentIndex
property bool _loading: minLoadingDelay.running || !bookModel.pageCount
property bool _pagerVisible: (_currentState.pager && book && bookModel.pageCount) ? 1 : 0
property var _currentState: _visibilityStates[settings.pageDetails % _visibilityStates.length]
readonly property var _visibilityStates: [
{ pager: false, page: false, title: false, tools: false },
{ pager: false, page: true, title: true, tools: false },
{ pager: true, page: true, title: true, tools: true }
]
PullDownMenu {
MenuItem {
id: defaultFontMenuItem
//% "Use default fonts"
text: qsTrId("book-font-default")
enabled: settings.fontSize != Settings.DefaultFontSize
onClicked: settings.fontSize = Settings.DefaultFontSize
}
MenuItem {
id: smallerFontMenuItem
//% "Use smaller fonts"
text: qsTrId("book-font-smaller")
enabled: settings.fontSize >= Settings.MinFontSize
onClicked: settings.fontSize -= 1
}
MenuItem {
id: largerFontMenuItem
//% "Use larger fonts"
text: qsTrId("book-font-larger")
enabled: settings.fontSize <= Settings.MaxFontSize
onClicked: settings.fontSize += 1
}
MenuItem {
//% "Back to library"
text: qsTrId("book-view-back")
onClicked: root.closeBook()
}
// Remove disabled items from the menu when we have a chance
onActiveChanged: if (!active) hideDisabledItems()
Component.onCompleted: hideDisabledItems()
function hideDisabledItems() {
defaultFontMenuItem.visible = defaultFontMenuItem.enabled
smallerFontMenuItem.visible = smallerFontMenuItem.enabled
largerFontMenuItem.visible = largerFontMenuItem.enabled
}
}
Timer {
id: minLoadingDelay
interval: 1000
}
Timer {
id: resetPager
interval: 0
onTriggered: {
if (_currentPage >= 0) {
console.log("resetting pager to", _currentPage)
pager.currentPage = _currentPage
}
}
}
BookModel {
id: bookModel
book: root.book ? root.book : null
size: bookListWatcher.size
currentPage: _currentPage
leftMargin: Theme.horizontalPageMargin
rightMargin: Theme.horizontalPageMargin
topMargin: Theme.itemSizeSmall
bottomMargin: Theme.itemSizeSmall
settings: root.settings
onJumpToPage: bookView.jumpTo(index)
onCurrentPageChanged: {
if (currentPage >= 0 && bookView._jumpingTo < 0) {
pager.currentPage = currentPage
}
}
onLoadingChanged: {
if (loading && !pageCount) {
minLoadingDelay.start()
bookView._jumpingTo = -1
}
}
}
ListWatcher {
id: bookListWatcher
listView: bookView
}
SilicaListView {
id: bookView
model: bookModel
anchors.fill: parent
flickDeceleration: maximumFlickVelocity
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem
spacing: Theme.paddingMedium
opacity: _loading ? 0 : 1
visible: opacity > 0
delegate: BooksPageView {
width: bookView.width
height: bookView.height
model: bookModel
page: index
settings: root.settings
leftMargin: bookModel.leftMargin
rightMargin: bookModel.rightMargin
topMargin: bookModel.topMargin
bottomMargin: bookModel.bottomMargin
titleVisible: _currentState.title
pageNumberVisible: _currentState.page
title: bookModel.title
onPageClicked: {
root.pageClicked(index)
settings.pageDetails = (settings.pageDetails+ 1) % _visibilityStates.length
}
}
property int _jumpingTo: -1
function jumpTo(page) {
if (page >=0 && page !== _currentPage) {
_jumpingTo = page
positionViewAtIndex(page, ListView.Center)
_jumpingTo = -1
if (_currentPage !== page) {
console.log("oops, still at", _currentPage)
resetPager.restart()
}
}
}
Behavior on opacity { FadeAnimation {} }
}
/*
BooksPageTools {
anchors {
top: parent.top
left: parent.left
right: parent.right
leftMargin: bookModel.leftMargin
rightMargin: bookModel.rightMargin
}
opacity: _currentState.tools ? 1 : 0
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
}
*/
BooksPager {
id: pager
anchors {
bottom: parent.bottom
bottomMargin: (Theme.itemSizeExtraSmall + 2*(bookModel.bottomMargin - height))/4
}
pageCount: bookModel.pageCount
width: parent.width
opacity: _pagerVisible ? 1 : 0
visible: opacity > 0
onPageChanged: bookView.jumpTo(page)
Behavior on opacity { FadeAnimation {} }
}
Column {
id: loadingAnimation
opacity: _loading ? 1 : 0
visible: opacity > 0
anchors.centerIn: parent
spacing: Theme.paddingLarge
Behavior on opacity { FadeAnimation {} }
Item {
width: busyIndicator.width
height: busyIndicator.height
anchors.horizontalCenter: parent.horizontalCenter
BusyIndicator {
id: busyIndicator
anchors.centerIn: parent
size: BusyIndicatorSize.Large
running: _loading
}
BooksFitLabel {
anchors {
fill: parent
margins: Theme.paddingMedium
}
maxFontSize: Theme.fontSizeMedium
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
color: Theme.highlightColor
text: bookModel.progress > 0 ? bookModel.progress : ""
visible: opacity > 0
opacity: bookModel.progress > 0 ? 1 : 0
Behavior on opacity { FadeAnimation {} }
}
}
Label {
anchors.horizontalCenter: parent.horizontalCenter
horizontalAlignment: Text.AlignHCenter
color: Theme.highlightColor
//% "Loading..."
text: qsTrId("book-view-loading")
}
}
}

109
app/qml/BooksCoverPage.qml Normal file
View file

@ -0,0 +1,109 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
CoverBackground {
id: root
transparent: !_haveBook || grid.count < 6
property variant book
property variant shelf
property bool _haveBook: book ? true : false
CoverModel {
id: coverModel
source: shelf
}
GridView {
id: grid
visible: !_haveBook
anchors.fill: parent
interactive: false
cellWidth: Math.floor(width/2.0)
cellHeight: Math.ceil(height/((grid.count > 4) ? 3.0 : 2.0))
model: coverModel
delegate: BookCover {
book: model.book
width: grid.cellWidth
height: grid.cellHeight
borderWidth: 0
stretch: true
}
}
Rectangle {
anchors.centerIn: parent
visible: !_haveBook && !coverModel.count
color: "#cc0000"
height: Math.ceil(parent.height*3/10)
width: height
radius: height/2
Image {
anchors.centerIn: parent
anchors.horizontalCenter: parent.horizontalCenter
source: "images/cover-image.svg"
width: parent.width - Math.max(Math.ceil(parent.width/20),2)
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
}
}
BookCover {
id: bookCover
visible: _haveBook
anchors.fill: parent
anchors.centerIn: parent
width: Theme.coverSizeLarge.width
height: Theme.coverSizeLarge.height
borderRadius: Theme.paddingSmall
borderColor: Theme.primaryColor
synchronous: true
book: root.book ? root.book : null
defaultCover: "images/default-cover.jpg"
stretch: true
}
BooksTitleText {
anchors {
margins: Theme.paddingMedium
fill: parent
}
text: book ? book.title : ""
visible: bookCover.empty
}
}

View file

@ -0,0 +1,66 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
Rectangle {
id: root
signal clicked()
property real size
property real margin: Theme.paddingMedium
width: size
height: size
color: Theme.highlightColor
radius: size/2
opacity: enabled ? 1 : 0
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
Image {
anchors.fill: parent
sourceSize.width: width
sourceSize.height: height
source: "images/close-x.svg"
}
MouseArea {
anchors {
fill: parent
margins: -root.margin
}
onClicked: root.clicked()
}
}

260
app/qml/BooksDragArea.qml Normal file
View file

@ -0,0 +1,260 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
MouseArea {
id: root
parent: dragInProgress ? dragParent : gridView
anchors.fill: parent
signal deleteItemAt(var index)
signal dropItem(var mouseX, var mouseY)
property var dragParent
property var gridView
property int draggedItemIndex: -1
property int pressedItemIndex: -1
property int pressedDeleteItemIndex: -1
property int lastPressedItemScalingIndex: -1
property int lastReleasedItemIndex: -1
property int lastReleasedDeleteItemIndex: -1
property bool pressedItemScaling: (pressedItemIndex >= 0 && lastPressedItemScalingIndex === pressedItemIndex)
property bool dragPositionChanged
property bool dragCloseToTheLeftEdge
property bool dragCloseToTheRightEdge
property real dragLastX
property real dragLastY
onDraggedItemIndexChanged: {
draggedItem = (draggedItemIndex >= 0) ? shelf.get(draggedItemIndex) : null
}
function itemX(index) { return shelfView.cellWidth * (index % shelfView._cellsPerRow) - gridView.contentX }
function itemY(index) { return shelfView.cellHeight * Math.floor(index / shelfView._cellsPerRow) - gridView.contentY }
onCanceled: {
stopDrag()
}
onClicked: {
var index
if (shelfView.editMode) {
if (!dragInProgress || !dragPositionChanged) {
index = gridView.indexAt(mouseX + gridView.contentX, mouseY + currentShelfView.contentY)
if (index >= 0 &&
index === lastReleasedDeleteItemIndex &&
dragItem.withinDeleteButton(mouseX - itemX(index), mouseY - itemY(index))) {
lastReleasedDeleteItemIndex = -1
root.deleteItemAt(index)
dragScrollAnimation.stop()
} else if (shelfView.shelf.deleteRequested(index)) {
shelfView.shelf.setDeleteRequested(index, false);
} else {
shelfView.stopEditing()
}
}
} else {
index = gridView.indexAt(mouseX + gridView.contentX, mouseY + currentShelfView.contentY)
if (index >= 0 && index === lastReleasedItemIndex) {
var item = shelf.get(index);
if (item.book && item.accessible) {
shelfView.openBook(item.book)
}
}
}
resetPressState()
dragScrollAnimation.stop()
}
onPressed: {
var index = gridView.indexAt(mouseX + gridView.contentX, mouseY + currentShelfView.contentY)
lastReleasedItemIndex = -1
lastReleasedDeleteItemIndex = -1
pressedItemIndex = index
if (pressedDeleteItemIndex < 0 &&
dragItem.withinDeleteButton(mouseX - itemX(index), mouseY - itemY(index))) {
pressedDeleteItemIndex = index
} else {
pressedDeleteItemIndex = -1
}
}
onReleased: {
stopDrag(mouseX, mouseY)
}
onPressAndHold: {
if (!shelfView.editMode) {
var index = gridView.indexAt(mouseX + gridView.contentX, mouseY + currentShelfView.contentY)
if (index === pressedItemIndex) {
shelfView.startEditing()
}
}
}
onPositionChanged: {
if (shelfView.editMode) {
if (!pressedItemScaling && !dragInProgress && pressedDeleteItemIndex < 0) {
startDrag(mouseX, mouseY)
} else {
doDrag(mouseX, mouseY)
}
}
}
function startDrag(x, y) {
var index = gridView.indexAt(x + gridView.contentX, y + currentShelfView.contentY)
if (index >= 0) {
var item = shelf.get(index)
if (item.accessible) {
var delta = gridView.mapToItem(dragParent,0,0)
console.log(shelf.path, "dragging", item.name)
dragLastX = x + delta.x
dragLastY = y + delta.y
dragItem.moveAnimationEnabled = false
dragItem.shelfIndex = shelfView.shelfIndex
dragItem.dropShelfIndex = -1
dragItem.dropItemIndex = -1
dragItem.x = itemX(index) + delta.x
dragItem.y = itemY(index) + delta.y
dragCloseToTheLeftEdge = (x < horizontalScrollThreshold)
dragCloseToTheRightEdge = (x > (gridView.width - horizontalScrollThreshold))
dragPositionChanged = false
draggedItemIndex = index
pressedItemIndex = -1
dragItem.visible = true
} else {
console.log(item.name, "is not draggable")
}
}
}
function doDrag(x, y) {
if (dragInProgress && !dragItem.moving) {
var dx = x - dragLastX
var dy = y - dragLastY
if (dx !== 0 || dy !== 0) {
dragPositionChanged = true
dragItem.x += dx
dragItem.y += dy
dragLastX = x
dragLastY = y
var newIndex = currentShelfView.indexAt(x + gridView.contentX, Math.max(y + currentShelfView.contentY, 0))
if (newIndex < 0) newIndex = currentShelf.count - 1
if (newIndex >= 0) {
if (currentShelf.hasDummyItem) {
currentShelf.dummyItemIndex = newIndex
} else {
currentShelf.move(draggedItemIndex, newIndex)
draggedItemIndex = newIndex
}
}
if (y < verticalScrollThreshold && currentShelfView.contentY > 0) {
if (!dragScrollAnimation.running) {
dragScrollAnimation.from = currentShelfView.contentY
dragScrollAnimation.to = 0
dragScrollAnimation.duration = currentShelfView.contentY*1000/gridView.height
dragScrollAnimation.start()
}
} else if (y > (gridView.height - verticalScrollThreshold) && (currentShelfView.contentY < maxContentY)) {
if (!dragScrollAnimation.running) {
dragScrollAnimation.from = currentShelfView.contentY
dragScrollAnimation.to = maxContentY
dragScrollAnimation.duration = (maxContentY - currentShelfView.contentY)*1000/gridView.height
dragScrollAnimation.start()
}
} else {
if (dragScrollAnimation.running) {
dragScrollAnimation.stop()
}
if (x < horizontalScrollThreshold) {
if (!dragCloseToTheLeftEdge) {
dragCloseToTheLeftEdge = true
shelfView.scrollLeft()
}
} else {
dragCloseToTheLeftEdge = false
}
if (x > (gridView.width - horizontalScrollThreshold)) {
if (!dragCloseToTheRightEdge) {
dragCloseToTheRightEdge = true
shelfView.scrollRight()
}
} else {
dragCloseToTheRightEdge = false
}
}
}
}
}
function stopDrag(x, y) {
lastReleasedItemIndex = pressedItemIndex
lastReleasedDeleteItemIndex = pressedDeleteItemIndex
if (draggedItemIndex >= 0) {
if (x !== undefined && y !== undefined) doDrag(x, y)
var delta = gridView.mapToItem(dragParent,0,0)
dragItem.moveAnimationEnabled = true
dragItem.x = itemX(draggedItemIndex) + delta.x
dragItem.y = itemY(draggedItemIndex) + delta.y
// Allow the listener to modify dragItem coordinates
if (x !== undefined && y !== undefined) root.dropItem(x,y)
dragItem.dragged = false
// Normally we would finish things up once all the
// animations have completed
if (!dragItem.animating) finishDrag()
}
pressedItemIndex = -1
dragScrollAnimation.stop()
}
function finishDrag() {
console.log(shelf.path, "done with drag animation")
draggedItemIndex = -1
dragItem.dropShelfIndex = -1
dragItem.dropItemIndex = -1
dragItem.moveAnimationEnabled = false
dragItem.visible = false
}
function resetPressState() {
pressedItemIndex = -1
pressedDeleteItemIndex = -1
lastReleasedItemIndex = -1
lastReleasedDeleteItemIndex = -1
dragCloseToTheLeftEdge = false
dragCloseToTheRightEdge = false
}
NumberAnimation {
id: dragScrollAnimation
target: currentShelfView
property: "contentY"
easing.type: Easing.InOutQuad
}
}

66
app/qml/BooksFitLabel.qml Normal file
View file

@ -0,0 +1,66 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
Label {
property int minFontSize: Theme.fontSizeTiny
property int maxFontSize: Theme.fontSizeHuge
smooth: true
Component.onCompleted: refitText()
onWidthChanged: refitText()
onHeightChanged: refitText()
onTextChanged: refitText()
onMaxFontSizeChanged: refitText()
onMinFontSizeChanged: refitText()
function refitText() {
if (paintedHeight > 0 && paintedWidth > 0) {
if (font.pixelSize % 2) font.pixelSize++
while (paintedWidth > width || paintedHeight > height) {
if ((font.pixelSize -= 2) <= minFontSize)
break
}
while (paintedWidth < width && paintedHeight < height) {
font.pixelSize += 2
}
font.pixelSize -= 2
if (font.pixelSize >= maxFontSize) {
font.pixelSize = maxFontSize
return
}
}
}
}

118
app/qml/BooksImport.qml Normal file
View file

@ -0,0 +1,118 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
Dialog {
id: root
allowedOrientations: window.allowedOrientations
canAccept: importModel.selectedCount > 0
property alias selectedCount: importModel.selectedCount
property alias destination: importModel.destination
property bool _loading: importModel.busy || startTimer.running
function selectedBook(index) { return importModel.selectedBook(index) }
BooksImportModel {
id: importModel
}
SilicaFlickable {
anchors.fill: parent
DialogHeader {
id: dialogHeader
spacing: 0
acceptText: (_loading || !importModel.count) ? "" :
//% "Import %0 book(s)"
importModel.selectedCount ? qsTrId("import-view-import-n-books",importModel.selectedCount).arg(importModel.selectedCount) :
//% "Select books"
qsTrId("import-view-select-books")
}
SilicaListView {
anchors {
top: dialogHeader.bottom
bottom: parent.bottom
left: parent.left
right: parent.right
}
model: importModel
clip: true
opacity: (importModel.count > 0) ? 1 : 0
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
delegate: BooksImportItem {
width: root.width
highlighted: down || model.selected
contentHeight: Theme.itemSizeExtraLarge
onClicked: importModel.setSelected(model.index, !model.selected)
book: model.book
}
VerticalScrollDecorator {}
}
ViewPlaceholder {
//% "No new books found"
text: qsTrId("import-view-no-new-books-found")
enabled: !_loading && !importModel.count
}
}
BusyIndicator {
visible: opacity > 0
anchors.centerIn: parent
size: BusyIndicatorSize.Large
running: _loading && !importModel.count
}
// Give the dialog 1 second to initialize and finish the transition
// then ask the model to actually examine the downloads
Timer {
interval: 1000
running: true
onTriggered: importModel.refresh()
}
// Then show the spinner for at least one more second
Timer {
id: startTimer
interval: 2000
running: true
}
}

101
app/qml/BooksImportItem.qml Normal file
View file

@ -0,0 +1,101 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
BackgroundItem {
id: root
width: parent.width
height: contentHeight
enabled: book ? true : false
property var book
property string _title: book ? book.title : ""
property string _authors: book ? book.authors : ""
BooksShelfItem {
id: cover
visible: root.enabled
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
leftMargin: Theme.horizontalPageMargin
}
width: Math.round(height*3/4)
book: root.book ? root.book : null
name: _title
}
Column {
visible: root.enabled
anchors {
left: cover.right
leftMargin: Theme.paddingMedium
right: parent.right
rightMargin: Theme.horizontalPageMargin
verticalCenter: parent.verticalCenter
}
Label {
width: parent.width
text: _authors
color: root.highlighted ? Theme.highlightColor : Theme.primaryColor
truncationMode: TruncationMode.Fade
font.pixelSize: Theme.fontSizeExtraSmall
}
Label {
width: parent.width
text: _title
color: root.highlighted ? Theme.highlightColor : Theme.primaryColor
truncationMode: TruncationMode.Fade
}
}
Image {
anchors {
top: parent.top
topMargin: Theme.paddingSmall
right: parent.right
rightMargin: Theme.paddingSmall
}
visible: highlighted
source: "image://theme/icon-s-installed" + (highlighted ? "?" + Theme.highlightColor : "")
}
BusyIndicator {
anchors.centerIn: parent
visible: !root.enabled
running: visible
size: BusyIndicatorSize.Medium
}
}

55
app/qml/BooksMain.qml Normal file
View file

@ -0,0 +1,55 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
ApplicationWindow {
id: window
allowedOrientations: Orientation.All
property variant currentShelf: mainPage.currentShelf
Settings { id: globalSettings }
initialPage: BooksMainPage {
id: mainPage
settings: globalSettings
}
cover: Component {
BooksCoverPage {
book: globalSettings.currentBook
shelf: currentShelf
}
}
}

82
app/qml/BooksMainPage.qml Normal file
View file

@ -0,0 +1,82 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
Page {
id: root
allowedOrientations: window.allowedOrientations
//property variant shelf
property variant settings
property variant currentShelf: storageView.currentShelf
property Item _bookView
function createBookViewIfNeeded() {
if (settings.currentBook && !_bookView) {
_bookView = bookViewComponent.createObject(root)
}
}
Connections {
target: settings
onCurrentBookChanged: createBookViewIfNeeded()
}
Component {
id: bookViewComponent
BooksBookView {
anchors.fill: parent
opacity: book ? 1 : 0
visible: opacity > 0
settings: root.settings
book: settings.currentBook ? settings.currentBook : null
onCloseBook: settings.currentBook = null
Behavior on opacity { FadeAnimation {} }
}
}
BooksStorageView {
id: storageView
anchors.fill: parent
settings: root.settings
opacity: settings.currentBook ? 0 : 1
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
onOpenBook: settings.currentBook = book
}
Component.onCompleted: createBookViewIfNeeded()
}

287
app/qml/BooksPageSlider.qml Normal file
View file

@ -0,0 +1,287 @@
/****************************************************************************************
**
** Copyright (C) 2013 Jolla Ltd.
** Contact: Martin Jones <martin.jones@jollamobile.com>
** All rights reserved.
**
** This file is part of Sailfish Silica UI component package.
**
** You may use this file under the terms of BSD license as follows:
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** * Neither the name of the Jolla Ltd nor the
** names of its contributors may be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR
** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
** (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
** LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
****************************************************************************************/
import QtQuick 2.0
import Sailfish.Silica 1.0
MouseArea {
id: slider
// Same as Silica Slider except that its colors are configurable
property color primaryColor: Theme.primaryColor
property color secondaryColor: Theme.secondaryColor
property color highlightColor: Theme.highlightColor
property color secondaryHighlightColor: Theme.secondaryHighlightColor
property real maximumValue: 1.0
property real minimumValue: 0.0
property real stepSize
property real value: 0.0
readonly property real sliderValue: Math.max(minimumValue, Math.min(maximumValue, value))
property bool handleVisible: true
property string valueText
property alias label: labelText.text
property bool down: pressed
property bool highlighted: down
property real leftMargin: Math.round(Screen.width/8)
property real rightMargin: Math.round(Screen.width/8)
property bool _hasValueLabel: false
property real _oldValue
property bool _tracking: true
property real _precFactor: 1.0
property real _grooveWidth: Math.max(0, width - leftMargin - rightMargin)
property bool _widthChanged
property bool _cancel
property bool _componentComplete
onStepSizeChanged: {
// Avoid rounding errors. We assume that the range will
// be sensibly related to stepSize
var decimial = Math.floor(stepSize) - stepSize
if (decimial < 0.001) {
_precFactor = Math.pow(10, 7)
} else if (decimial < 0.1) {
_precFactor = Math.pow(10, 4)
} else {
_precFactor = 1.0
}
}
height: valueText !== "" ? Theme.itemSizeExtraLarge : label !== "" ? Theme.itemSizeMedium : Theme.itemSizeSmall
onWidthChanged: updateWidth()
onLeftMarginChanged: updateWidth()
onRightMarginChanged: updateWidth()
// changing the width of the slider shouldn't animate the slider bar/handle
function updateWidth() {
_widthChanged = true
_grooveWidth = Math.max(0, width - leftMargin - rightMargin)
_widthChanged = false
}
function cancel() {
_cancel = true
value = _oldValue
_updateHighlightToValue()
}
drag {
target: draggable
minimumX: leftMargin - highlight.width/2
maximumX: slider.width - leftMargin - highlight.width/2
axis: Drag.XAxis
}
function _updateHighlightToValue() {
if (maximumValue > minimumValue) {
highlight.x = (sliderValue - minimumValue) / (maximumValue - minimumValue) * _grooveWidth - highlight.width/2 + leftMargin
} else {
highlight.x = leftMargin - highlight.width/2
}
}
function _updateValueToDraggable() {
if (width > (leftMargin + rightMargin)) {
highlight.x = draggable.x
var pos = draggable.x + highlight.width/2 - leftMargin
value = _calcValue((pos / _grooveWidth) * (maximumValue - minimumValue) + minimumValue)
}
}
function _calcValue(newVal) {
if (newVal <= minimumValue) {
return minimumValue
}
if (stepSize > 0.0) {
var offset = newVal - minimumValue
var intervals = Math.round(offset / stepSize)
newVal = Math.round((minimumValue + (intervals * stepSize)) * _precFactor) / _precFactor
}
if (newVal > maximumValue) {
return maximumValue
}
return newVal
}
onPressed: {
_cancel = false
_oldValue = value
draggable.x = Math.min(Math.max(drag.minimumX, mouseX - highlight.width/2), drag.maximumX)
}
onReleased: {
if (!_cancel) {
_tracking = false
_updateValueToDraggable()
if (stepSize != 0.0) {
// on release make sure that we settle on a step boundary
_updateHighlightToValue()
}
_oldValue = value
}
}
onCanceled: value = _oldValue
onValueTextChanged: {
if (valueText && !_hasValueLabel) {
_hasValueLabel = true
var valueIndicatorComponent = Qt.createComponent("private/SliderValueLabel.qml")
if (valueIndicatorComponent.status === Component.Ready) {
valueIndicatorComponent.createObject(slider)
} else {
console.log(valueIndicatorComponent.errorString())
}
}
}
onSliderValueChanged: {
if (!slider.drag.active) {
_tracking = false
_updateHighlightToValue()
}
}
GlassItem {
id: background
// extra painting margins (Theme.paddingMedium on both sides) are needed,
// because glass item doesn't visibly paint across the full width of the item
x: slider.leftMargin-Theme.paddingMedium
width: slider._grooveWidth + 2*Theme.paddingMedium
height: Theme.itemSizeExtraSmall/2
anchors.verticalCenter: parent.verticalCenter
dimmed: true
radius: 0.06
falloffRadius: 0.09
ratio: 0.0
onWidthChanged: { _tracking = true; _updateHighlightToValue() }
color: slider.highlighted ? highlightColor : secondaryColor
states: State {
name: "hasText"; when: slider.valueText !== "" || text !== ""
AnchorChanges { target: background; anchors.verticalCenter: undefined; anchors.bottom: slider.bottom }
PropertyChanges { target: background; anchors.bottomMargin: Theme.fontSizeSmall + Theme.paddingSmall + 1 }
}
}
GlassItem {
id: progressBar
x: background.x // some margin at each end
anchors.verticalCenter: background.verticalCenter
// height added as GlassItem will not display correctly with width < height
width: (sliderValue - minimumValue) / (maximumValue - minimumValue) * (background.width-height) + height
height: Theme.itemSizeExtraSmall/2
visible: sliderValue > minimumValue
dimmed: false
radius: 0.05
falloffRadius: 0.14
ratio: 0.0
color: slider.highlighted ? highlightColor : primaryColor
Behavior on width {
enabled: !_widthChanged
SmoothedAnimation { velocity: 1500 }
}
}
Item {
id: draggable
width: highlight.width
height: highlight.height
onXChanged: {
if (_cancel) {
return
}
if (slider.drag.active) {
_updateValueToDraggable()
}
if (!_tracking && Math.abs(highlight.x - draggable.x) < 5) {
_tracking = true
}
}
}
GlassItem {
id: highlight
width: Theme.itemSizeMedium
height: Theme.itemSizeMedium
radius: 0.17
falloffRadius: 0.17
anchors.verticalCenter: background.verticalCenter
visible: handleVisible
color: slider.highlighted ? highlightColor : primaryColor
Behavior on x {
enabled: !_widthChanged
SmoothedAnimation { velocity: 1500 }
}
}
Label {
id: labelText
visible: text.length
font.pixelSize: Theme.fontSizeSmall
color: slider.highlighted ? secondaryHighlightColor : secondaryColor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: background.verticalCenter
anchors.topMargin: Theme.paddingMedium
width: Math.min(paintedWidth, parent.width - 2*Theme.paddingMedium)
truncationMode: TruncationMode.Fade
}
states: State {
name: "invalidRange"
when: _componentComplete && minimumValue >= maximumValue
PropertyChanges {
target: progressBar
width: progressBar.height
}
PropertyChanges {
target: slider
enabled: false
opacity: 0.6
}
StateChangeScript {
script: console.log("Warning: Slider.maximumValue needs to be higher than Slider.minimumValue")
}
}
Component.onCompleted: {
_componentComplete = true
_updateHighlightToValue()
}
}

107
app/qml/BooksPageView.qml Normal file
View file

@ -0,0 +1,107 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
Item {
id: view
property alias model: widget.model
property alias page: widget.page
property alias leftMargin: widget.leftMargin
property alias rightMargin: widget.rightMargin
property alias topMargin: widget.topMargin
property alias bottomMargin: widget.bottomMargin
property alias settings: widget.settings
property alias title: titleLabel.text
property bool titleVisible
property bool pageNumberVisible
property color extraInfoColor: "#808080"
signal pageClicked()
PageWidget {
id: widget
anchors.fill: parent
model: bookModel
}
Label {
id: titleLabel
anchors {
top: parent.top
left: parent.left
right: parent.right
leftMargin: Theme.paddingLarge
rightMargin: Theme.paddingLarge
}
height: Theme.itemSizeExtraSmall
font.pixelSize: Theme.fontSizeSmall
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
color: extraInfoColor
elide: Text.ElideRight
opacity: titleVisible ? 1 : 0
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
}
BusyIndicator {
anchors.centerIn: parent
size: BusyIndicatorSize.Large
running: widget.loading
opacity: running ? 1 : 0
visible: opacity > 0
Behavior on opacity {}
}
Label {
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
}
text: widget.page + 1
height: Theme.itemSizeExtraSmall
font.pixelSize: Theme.fontSizeSmall
verticalAlignment: Text.AlignVCenter
color: extraInfoColor
opacity: pageNumberVisible ? 1 : 0
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
}
MouseArea {
anchors.fill: parent
onClicked: view.pageClicked()
}
}

68
app/qml/BooksPager.qml Normal file
View file

@ -0,0 +1,68 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
Item {
id: root
height: slider.height
property alias pageCount: slider.maximumValue
property alias currentPage: slider.value
property alias pressed: slider.pressed
property alias leftMargin: slider.leftMargin
property alias rightMargin: slider.rightMargin
signal pageChanged(var page)
BooksPageSlider {
id: slider
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
stepSize: 1
minimumValue: 0
valueText: ""
label: ""
//highlightColor: primaryColor
//secondaryHighlightColor: secondaryColor
//highlightColor: "#000000"
//secondaryHighlightColor: "#808080"
//primaryColor: "#808080"
primaryColor: highlightColor
secondaryColor: secondaryHighlightColor
onValueChanged: root.pageChanged(value)
}
}

View file

@ -0,0 +1,54 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
Rectangle {
color: "transparent"
width: image.width
Rectangle {
color: Theme.highlightColor
x: parent.width*3/80
y: parent.height*3/60
width: parent.width*74/80
height: parent.height*47/60
}
Image {
id: image
height: parent.height
sourceSize.height: height
fillMode: Image.PreserveAspectFit
source: "images/sdcard.svg"
}
}

205
app/qml/BooksShelfItem.qml Normal file
View file

@ -0,0 +1,205 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
Item {
id: root
signal clicked()
signal deleteRequested()
signal deleteMe()
opacity: deletingAll ? deleteAllOpacity : deleting ? scale : 1
property alias name: title.text
property alias book: cover.book
property alias synchronous: cover.synchronous
property alias remorseTimeout: deleteAnimation.duration
property real margins: Theme.paddingMedium
property real deleteAllOpacity: 1
property bool editMode
property bool dropped
property bool dragged
property bool pressed
property bool deletingAll
property bool deleting
property bool copyingOut
property bool copyingIn
property bool scaleAnimationEnabled: true
property bool moveAnimationEnabled: false
readonly property bool scaling: scaleUpAnimation.running || scaleDownAnimation.running || deleteAnimation.running
readonly property bool moving: moveAnimationX.running || moveAnimationY.running
readonly property bool animating: scaling || moving
property bool _deleting: deleting && !deletingAll
property real _borderRadius: Theme.paddingSmall
property color _borderColor: Theme.primaryColor
property bool scaledDown: (editMode && !dragged && !pressed && !dropped)
BookCover {
id: cover
anchors {
margins: root.margins
fill: parent
}
borderWidth: 2
borderRadius: _borderRadius
borderColor: _borderColor
opacity: (copyingIn || copyingOut) ? 0.1 : 1
Behavior on opacity { FadeAnimation { } }
BooksTitleText {
id: title
anchors.centerIn: parent
width: parent.width - 2*Theme.paddingMedium
height: parent.height - 2*Theme.paddingMedium
visible: parent.empty
}
layer.enabled: root.pressed
layer.effect: ShaderEffect { // i.e. PressEffect from Lipstick
property variant source
property color color: Theme.rgba(Theme.highlightBackgroundColor, 0.4)
fragmentShader: "
uniform sampler2D source;
uniform highp vec4 color;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main(void) {
highp vec4 pixelColor = texture2D(source, qt_TexCoord0);
gl_FragColor = vec4(mix(pixelColor.rgb/max(pixelColor.a, 0.00390625), color.rgb/max(color.a, 0.00390625), color.a) * pixelColor.a, pixelColor.a) * qt_Opacity;
}"
}
}
MouseArea {
anchors {
fill: parent
margins: -Theme.paddingSmall
}
onClicked: root.clicked()
}
BooksDeleteButton {
id: deleteButton
x: cover.x + (cover.width - width)/2
y: cover.y + cover.height - height/2
size: 3*margins
enabled: editMode && !pressed && !deleting && !deletingAll && !copyingIn && !copyingOut
onClicked: root.deleteRequested()
}
BusyIndicator {
id: busyIndicator
anchors.centerIn: parent
size: BusyIndicatorSize.Large
running: copyingIn || copyingOut
}
function withinDeleteButton(x, y) {
return x >= deleteButton.x - deleteButton.margin &&
x < deleteButton.x + deleteButton.width + deleteButton.margin &&
y >= deleteButton.y - deleteButton.margin &&
y < deleteButton.y + deleteButton.height + deleteButton.margin;
}
function updateScaledDown() {
if (scaleAnimationEnabled) {
if (scaledDown) {
scaleUpAnimation.stop()
scaleDownAnimation.restart()
} else {
scaleDownAnimation.stop()
scaleUpAnimation.restart()
}
} else {
scale = scaledDown ? scaleDownAnimation.to : scaleUpAnimation.to
}
}
onScaledDownChanged: if (!_deleting) updateScaledDown()
onScaleAnimationEnabledChanged: {
if (!_deleting && !scaleAnimationEnabled) {
scale = scaledDown ? scaleDownAnimation.to : scaleUpAnimation.to
}
}
on_DeletingChanged: {
if (_deleting) {
scaleUpAnimation.stop()
scaleDownAnimation.stop()
deleteAnimation.start()
} else {
deleteAnimation.stop()
updateScaledDown()
}
}
NumberAnimation {
id: scaleUpAnimation
target: root
property: "scale"
to: 1
duration: 250
easing.type: Easing.InOutQuad
}
NumberAnimation {
id: scaleDownAnimation
target: root
property: "scale"
to: 0.9
duration: 250
easing.type: Easing.InOutQuad
}
NumberAnimation {
id: deleteAnimation
target: root
property: "scale"
to: 0
duration: 2000
easing.type: Easing.OutSine
onRunningChanged: if (!running && _deleting) root.deleteMe()
}
NumberAnimation { id: moveAnimationX; duration: 150; easing.type: Easing.InOutQuad }
NumberAnimation { id: moveAnimationY; duration: 150; easing.type: Easing.InOutQuad }
Behavior on x { animation: moveAnimationX; enabled: moveAnimationEnabled }
Behavior on y { animation: moveAnimationY; enabled: moveAnimationEnabled }
}

258
app/qml/BooksShelfView.qml Normal file
View file

@ -0,0 +1,258 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
SilicaFlickable {
id: shelfView
property variant settings
property int shelfIndex
property bool singleStorage
property bool removableStorage
property bool editMode
property bool deleteAllRequest
property real cellWidth
property real cellHeight
property alias view: grid
property alias shelf: shelfModel
property alias device: shelfModel.device
property alias dummyItemIndex: shelfModel.dummyItemIndex
signal openBook(var book)
signal dropItem(var mouseX, var mouseY)
signal startEditing()
signal stopEditing()
signal cancelDeleteAll()
signal scrollRight()
signal scrollLeft()
property bool _haveBooks: shelf && shelf.count
property int _cellsPerRow: Math.floor(width/cellWidth)
readonly property int _remorseTimeout: 5000
property bool _loading: !shelf || shelf.loading || startAnimationTimer.running
property var _remorse
on_HaveBooksChanged: if (!_haveBooks) shelfView.stopEditing()
Shelf {
id: shelfModel
property bool needDummyItem: dragInProgress && dragItem.shelfIndex !== shelfView.shelfIndex
onNeedDummyItemChanged: if (needDummyItem) hasDummyItem = true
editMode: shelfView.editMode
}
onEditModeChanged: {
dragItem.visible = false
shelf.cancelAllDeleteRequests()
dragArea.draggedItemIndex = -1
fadeAnimation.stop()
if (!editMode) {
dragArea.resetPressState()
dragScrollAnimation.stop()
if (_remorse) _remorse.cancel()
}
}
function doDeleteAll() {
if (deleteAllRequest) {
fadeAnimation.stop()
shelf.removeAll()
shelf.cancelAllDeleteRequests()
}
}
onDeleteAllRequestChanged: {
if (deleteAllRequest) {
if (!_remorse) _remorse = remorseComponent.createObject(shelfView)
fadeAnimation.restart()
//% "Deleting all books"
_remorse.execute(qsTrId("shelf-view-about-to-delete-all"),
doDeleteAll, _remorseTimeout)
} else if (_remorse) {
fadeAnimation.stop()
_remorse.cancel()
}
}
Component {
id: remorseComponent
RemorsePopup {
onCanceled: {
shelfView.cancelDeleteAll()
shelf.cancelAllDeleteRequests()
fadeAnimation.stop()
}
}
}
Connections {
target: dragItem
onAnimatingChanged: if (!dragItem.animating && !dragArea.pressed) dragArea.finishDrag()
}
BooksStorageHeader {
id: storageHeader
removable: removableStorage
count: shelfModel.count
showCount: !_loading
}
SilicaGridView {
id: grid
anchors {
top: singleStorage ? parent.top : storageHeader.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
leftMargin: Math.floor((shelfView.width - _cellsPerRow * shelfView.cellWidth)/_cellsPerRow/2)
}
model: shelfModel
interactive: !dragInProgress
clip: true
opacity: (!_loading && _haveBooks) ? 1 : 0
visible: opacity > 0
cellWidth: shelfView.cellWidth
cellHeight: shelfView.cellHeight
flickableDirection: Flickable.VerticalFlick
delegate: BooksShelfItem {
editMode: shelfView.editMode
dropped: dragItem.dropShelfIndex >= 0 &&
dragItem.dropShelfIndex === shelfView.shelfIndex &&
dragItem.dropItemIndex >= 0 &&
dragItem.dropItemIndex === model.index
dragged: dragItem.shelfIndex === shelfView.shelfIndex &&
dragArea.draggedItemIndex === model.index
visible: !model.dummy && !dragged && !dropped
pressed: model.accessible && (model.index === dragArea.pressedItemIndex) && (model.index !== dragArea.pressedDeleteItemIndex)
deleting: model.deleteRequested
deletingAll: shelfView.deleteAllRequest
deleteAllOpacity: grid.itemOpacity
width: shelfView.cellWidth
height: shelfView.cellHeight
book: model.book
name: model.name
copyingIn: model.copyingIn
copyingOut: model.copyingOut
remorseTimeout: _remorseTimeout
onScalingChanged: updateLastPressedItemScalingIndex()
onPressedChanged: updateLastPressedItemScalingIndex()
onDeleteMe: shelfView.shelf.remove(model.index)
function updateLastPressedItemScalingIndex() {
if (pressed && scaling) {
dragArea.lastPressedItemScalingIndex = model.index
} else if (dragArea.lastPressedItemScalingIndex === model.index) {
dragArea.lastPressedItemScalingIndex = -1
}
}
}
property real itemOpacity: 1
moveDisplaced: Transition {
SmoothedAnimation { properties: "x,y"; duration: 150 }
}
removeDisplaced: Transition {
SmoothedAnimation { properties: "x,y"; duration: 150 }
}
NumberAnimation {
id: fadeAnimation
target: grid
property: "itemOpacity"
from: 1
to: 0
duration: _remorseTimeout
easing.type: Easing.OutCubic
}
BooksDragArea {
id: dragArea
dragParent: storageView
gridView: grid
onDeleteItemAt: {
if (!shelfView.deleteAllRequest) {
shelfView.shelf.setDeleteRequested(index, true);
}
}
onDropItem: shelfView.dropItem(mouseX, mouseY)
}
Behavior on y { SpringAnimation {} }
Behavior on opacity { FadeAnimation {} }
VerticalScrollDecorator {}
}
ViewPlaceholder {
//% "No books"
text: qsTrId("shelf-view-no-books")
enabled: !_loading && !_haveBooks
PulleyAnimationHint {
id: pulleyAnimationHint
flickable: storageView
anchors.fill: parent
enabled: parent.enabled && !editMode
}
}
property Item _busyIndicator
Component {
id: busyIndicatorComponent
BusyIndicator {
visible: opacity > 0
anchors.centerIn: parent
size: BusyIndicatorSize.Large
running: _loading && !longStartTimer.running
}
}
Timer {
id: longStartTimer
interval: 500
running: true
onTriggered: {
if (shelf.loading) {
console.log(shelfModel.path, "startup is taking too long")
startAnimationTimer.start()
if (!_busyIndicator) _busyIndicator = busyIndicatorComponent.createObject(shelfView)
}
}
}
Timer {
id: startAnimationTimer
interval: 2000
}
}

View file

@ -0,0 +1,125 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
Column {
anchors {
top: parent.top
left: parent.left
right: parent.right
topMargin: Theme.paddingMedium
}
spacing: 0
visible: opacity > 0
opacity: singleStorage ? 0 : 1
Behavior on opacity { FadeAnimation {} }
property bool removable
property int count
property bool showCount: true
Item {
width: parent.width
height: Math.max(left.height, right.height)
Row {
id: left
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
spacing: 0
Item {
width: Theme.paddingMedium
height: parent.height
}
BooksSDCardIcon {
visible: removableStorage
anchors.bottom: parent.bottom
height: storageLabel.height*3/4
}
Item {
visible: removableStorage
width: Theme.paddingMedium
height: parent.height
}
Label {
id: storageLabel
anchors.bottom: parent.bottom
color: Theme.highlightColor
text: removable ?
//% "Memory card"
qsTrId("storage-removable") :
//% "Internal storage"
qsTrId("storage-internal")
}
}
Label {
id: right
anchors {
bottom: parent.bottom
right: parent.right
rightMargin: Theme.paddingMedium
}
//% "%0 book(s)"
text: qsTrId("storage-book-count",count).arg(count)
font.pixelSize: Theme.fontSizeExtraSmall
color: Theme.highlightColor
opacity: (showCount && count > 0) ? 1 : 0
visible: opacity > 0
Behavior on opacity { FadeAnimation {} }
}
}
Item {
height: Theme.paddingSmall
width: parent.width
}
Rectangle {
color: Theme.secondaryHighlightColor
height: 1
anchors {
left: parent.left
right: parent.right
}
}
Rectangle {
color: Theme.highlightColor
height: 1
anchors {
left: parent.left
right: parent.right
}
}
}

View file

@ -0,0 +1,247 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
import harbour.books 1.0
SilicaFlickable {
id: storageView
interactive: !dragInProgress
property variant settings
property bool editMode: false
signal openBook(var book)
property real _cellWidth: calculateCellWidth()
property real _cellHeight: _cellWidth*8/5
property var draggedItem
property var currentShelf
property var currentShelfView
property int currentShelfIndex: storageListWatcher.currentIndex
readonly property bool dragInProgress: draggedItem ? true : false
readonly property real maxContentY: currentShelfView ? Math.max(0, currentShelfView.contentHeight - currentShelfView.height) : 0
readonly property real verticalScrollThreshold: _cellHeight/2
readonly property real horizontalScrollThreshold: _cellWidth/2
readonly property real _minGridCellWidth: 10*Theme.paddingMedium
// Books in the library shouldn't be too small or too big.
// At least 3 (or 5 in landscape) should fit in the horizontal direction.
// The width shouldn't be smaller than 10*Theme.paddingMedium or 0.88 inch
function calculateCellWidth() {
var result = 0
if (width > 0) {
// At least 3 books in portrait, 5 in landscape
var n = (height > width) ? 4 : 6
var cellSize = width/n
while (cellSize > _minGridCellWidth && (cellSize/PointsPerInch) > 0.88 && n < 11) {
cellSize = width/(++n)
}
result = Math.floor(width/(n-1))
}
return result
}
PullDownMenu {
MenuItem {
//% "Scan downloads"
text: qsTrId("storage-view-scan-downloads")
visible: !editMode
onClicked: pageStack.push(importComponent)
}
MenuItem {
//% "Delete all books"
text: qsTrId("storage-view-delete-everything")
visible: editMode
enabled: currentShelf && (currentShelf.count > 0)
onClicked: storageModel.setDeleteAllRequest(storageListWatcher.currentIndex, true)
}
}
onEditModeChanged: {
storageModel.cancelDeleteAllRequests()
dragScrollAnimation.stop()
}
Component {
id: importComponent
BooksImport {
destination: currentShelf ? currentShelf.path : ""
onAccepted: {
var count = selectedCount
for (var i=0; i<count; i++) {
currentShelf.importBook(selectedBook(i))
}
}
}
}
SystemState {
id: globalSystemState
onLockModeChanged: if (lockMode == "locked") editMode = false
}
BookStorage {
id: storageModel
// Show the contents of SD-card and let use know that he can switch
// between the internal memory and the removable storage by swiping
// the list horizontally
onNewStorage: storageList.scrollToPage(index)
}
ListWatcher {
id: storageListWatcher
listView: storageList
}
SilicaListView {
id: storageList
anchors.fill: parent
model: storageModel
flickDeceleration: maximumFlickVelocity
orientation: ListView.Horizontal
snapMode: ListView.SnapOneItem
spacing: Theme.paddingMedium
interactive: !dragInProgress && !dragScrollAnimation.running
readonly property real maxContentX: Math.max(0, contentWidth - width)
onMaxContentXChanged: {
// SD-card can be removed while scroll animation is still active
if (dragScrollAnimation.running && dragScrollAnimation.to > maxContentX) {
dragScrollAnimation.to = maxContentX
dragScrollAnimation.restart()
}
}
delegate: BooksShelfView {
width: storageList.width
height: storageList.height
cellWidth: storageView._cellWidth
cellHeight: storageView._cellHeight
settings: storageView.settings
singleStorage: storageModel.count < 2
editMode: storageView.editMode
deleteAllRequest: model.deleteAllRequest
device: model.device
removableStorage: model.removable
shelfIndex: model.index
onStartEditing: storageView.editMode = true
onStopEditing: storageView.editMode = false
onScrollLeft: storageList.scrollOnePageLeft()
onScrollRight: storageList.scrollOnePageRight()
onCancelDeleteAll: storageModel.cancelDeleteAllRequests()
onDropItem: storageView.dropItem()
property bool current: model.index === currentShelfIndex
Component.onCompleted: updateCurrentShelf()
onCurrentChanged: updateCurrentShelf()
function updateCurrentShelf() {
if (current) {
storageView.currentShelf = shelf
storageView.currentShelfView = view
} else {
// no need for dummy item anymore
shelf.hasDummyItem = false
}
}
onOpenBook: storageView.openBook(book)
}
function scrollOnePageLeft() {
if (contentX > 0) {
dragScrollAnimation.from = contentX
dragScrollAnimation.to = Math.max(0, contentX - width - spacing)
dragScrollAnimation.start()
}
}
function scrollOnePageRight() {
if (contentX < maxContentX) {
dragScrollAnimation.from = contentX
dragScrollAnimation.to = Math.min(maxContentX, contentX + width + spacing)
dragScrollAnimation.start()
}
}
function scrollToPage(index) {
dragScrollAnimation.from = contentX
dragScrollAnimation.to = (width + spacing) * index
dragScrollAnimation.start()
}
}
function dropItem() {
if (draggedItem && dragItem.shelfIndex !== currentShelfIndex && currentShelf) {
var targetIndex = currentShelf.dummyItemIndex
if (targetIndex >= 0 && currentShelf.drop(draggedItem)) {
console.log("drop accepted")
// Update coordinates of the drag item to make it move toward the drop target
var cellsPerRow = Math.floor(currentShelfView.width/_cellWidth)
var delta = currentShelfView.mapToItem(storageView,0,0)
var x = _cellWidth * (targetIndex % cellsPerRow) - currentShelfView.contentX
var y = _cellHeight * Math.floor(targetIndex / cellsPerRow) - currentShelfView.contentY
dragItem.x = x + delta.x
dragItem.y = y + delta.y
// This hides the target item until the grag item makes it to the destination:
dragItem.dropShelfIndex = currentShelfIndex
dragItem.dropItemIndex = targetIndex
}
}
}
BooksShelfItem {
id: dragItem
visible: false
width: storageView._cellWidth
height: storageView._cellHeight
pressed: false
editMode: false
scaledDown: false
scaleAnimationEnabled: false
synchronous: true
book: draggedItem ? draggedItem.book : null
name: draggedItem ? draggedItem.name : ""
property int shelfIndex: -1
property int dropShelfIndex: -1
property int dropItemIndex: -1
}
NumberAnimation {
id: dragScrollAnimation
target: storageList
property: "contentX"
duration: 500
easing.type: Easing.InOutQuad
}
}

View file

@ -0,0 +1,63 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the Jolla Ltd nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
*/
import QtQuick 2.0
import Sailfish.Silica 1.0
Text {
color: Theme.primaryColor
font.pixelSize: Theme.fontSizeSmall
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
wrapMode: Text.Wrap
onTextChanged: fitText()
onWidthChanged: fitText()
onHeightChanged: fitText()
onPaintedHeightChanged: fitText()
property bool _fittingText: false
function fitText() {
if (!_fittingText) {
_fittingText = true
font.pixelSize = Theme.fontSizeSmall
maximumLineCount = 100000
elide = Text.ElideNone
if (width > 0 && height > 0 && paintedHeight > height) {
font.pixelSize = Theme.fontSizeTiny
if (paintedHeight > height && lineHeight > 0) {
maximumLineCount = height/lineHeight
elide = Text.ElideRight
}
}
_fittingText = false
}
}
}

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 17.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
width="100"
height="100"
viewBox="0 0 100 100"
xml:space="preserve"
inkscape:version="0.48.4 r9939"
sodipodi:docname="close-x.svg"><metadata
id="metadata3074"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs3072" /><g
id="graphic-close-app"
transform="translate(0,-8)"><path
style="fill:#ffffff;stroke:none"
d="M 50,0 C 22.385763,0 0,22.385763 0,50 0,77.614238 22.385763,100 50,100 77.614237,100 100,77.614238 100,50 100,22.385763 77.614237,0 50,0 z M 28.84375,23.90625 44.6875,39.75 c 5.653,5.653 5.652,14.847 0,20.5 L 28.84375,76.09375 c -1.56467,1.257409 -3.594505,1.303267 -4.9375,0 -1.367,-1.367 -1.367,-3.5715 0,-4.9375 L 39.75,55.3125 c 2.924,-2.925 2.924,-7.701 0,-10.625 L 23.90625,28.84375 c -1.367,-1.367 -1.367,-3.5705 0,-4.9375 1.565153,-1.257526 3.594022,-1.302845 4.9375,0 z m 47.25,0 c 1.367,1.367 1.367,3.5705 0,4.9375 L 62.375,42.5625 c -4.094,4.094 -4.095,10.779 0,14.875 l 13.71875,13.71875 c 1.366,1.367 1.367,3.5705 0,4.9375 -1.566108,1.257128 -3.593266,1.302721 -4.9375,0 L 57.4375,62.375 c -6.822,-6.825 -6.823,-17.927 0,-24.75 L 71.15625,23.90625 c 1.564445,-1.258008 3.595058,-1.302741 4.9375,0 z"
transform="translate(0,8)"
id="circle3060"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ssssscscccccsscccsscccccscc" /></g></svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 17.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
x="0px"
y="0px"
width="86px"
height="86px"
viewBox="0 0 86 86"
enable-background="new 0 0 86 86"
xml:space="preserve"
id="svg2"
inkscape:version="0.48.4 r9939"
sodipodi:docname="cover-image.svg"><defs
id="defs12"><linearGradient
y2="18.8514"
x2="6.7619"
y1="91.3279"
x1="79.2383"
gradientUnits="userSpaceOnUse"
id="linearGradient3843"><stop
id="stop3845"
style="stop-color:#f5d9a8;stop-opacity:1;"
offset="0" /><stop
id="stop3847"
style="stop-color:#cc9933;stop-opacity:1;"
offset="1" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3843"
id="linearGradient3833"
x1="26.594183"
y1="24.500792"
x2="3.6175976"
y2="4.4139266"
gradientUnits="userSpaceOnUse" /></defs><metadata
id="metadata25"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><path
sodipodi:type="arc"
style="fill:url(#linearGradient3833);fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path3825"
sodipodi:cx="13.5"
sodipodi:cy="13.1875"
sodipodi:rx="10.5"
sodipodi:ry="9.8125"
d="m 24,13.1875 a 10.5,9.8125 0 1 1 -21,0 10.5,9.8125 0 1 1 21,0 z"
transform="matrix(4.0952381,0,0,4.3821656,-12.285714,-14.789809)" /><g
id="Layer_1"></g><path
inkscape:connector-curvature="0"
d="m 63.019685,23.201772 2.480315,0 0,47.568897 -35.610236,0 C 24.663386,70.770669 20.5,67.13878 20.5,61.735235 l 0,-41.190944 c 0,-2.923228 2.391733,-5.31496 5.314961,-5.31496 l 32.952755,0 0,40.836614 -28.877952,0 c -3.63189,0 -6.200788,1.948818 -6.200788,5.492126 l 0,0.177164 c 0,3.543308 2.568898,5.846458 6.200788,5.846458 l 33.129921,0 0,-44.379921 z m -4.251969,39.773621 -28.523622,0 c -0.797243,0 -1.505905,-0.442912 -1.505905,-1.328739 0,-0.708662 0.708662,-1.151576 1.505905,-1.151576 l 28.523622,0 0,2.480315 z"
id="path4"
style="fill:#cc0000;fill-opacity:1;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none" /><path
sodipodi:type="arc"
style="opacity:0;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:square;stroke-miterlimit:3;stroke-opacity:1;stroke-dasharray:none"
id="path2999"
sodipodi:cx="43"
sodipodi:cy="44.275425"
sodipodi:rx="42.088985"
sodipodi:ry="51.745762"
d="m 85.088985,44.275425 a 42.088985,51.745762 0 1 1 -84.17797044,0 42.088985,51.745762 0 1 1 84.17797044,0 z" /><path
sodipodi:type="arc"
style="opacity:0;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:square;stroke-miterlimit:3;stroke-opacity:1;stroke-dasharray:none"
id="path3769"
sodipodi:cx="22.684322"
sodipodi:cy="17.491526"
sodipodi:rx="12.298729"
sodipodi:ry="12.207627"
d="M 34.983051,17.491526 A 12.298729,12.207627 0 1 1 22.792077,5.2843669 L 22.684322,17.491526 z"
sodipodi:start="0"
sodipodi:end="4.7211505" /><path
sodipodi:type="arc"
style="opacity:0;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:square;stroke-miterlimit:3;stroke-opacity:1;stroke-dasharray:none"
id="path3799"
sodipodi:cx="3"
sodipodi:cy="5"
sodipodi:rx="80.07618"
sodipodi:ry="1.096934"
d="M 83.07618,5 A 80.07618,1.096934 0 1 1 3.70158,3.9031081 L 3,5 z"
sodipodi:start="0"
sodipodi:end="4.7211505" /><path
sodipodi:type="arc"
style="opacity:0;fill:#000000;fill-opacity:1;stroke:#ffffff;stroke-width:4;stroke-linecap:square;stroke-miterlimit:3;stroke-opacity:1;stroke-dasharray:none"
id="path3801"
sodipodi:cx="84.75"
sodipodi:cy="18.5"
sodipodi:rx="18.75"
sodipodi:ry="18.75"
d="m 103.5,18.5 a 18.75,18.75 0 1 1 -37.5,0 18.75,18.75 0 1 1 37.5,0 z" /></svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

50
app/qml/images/sdcard.svg Normal file
View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="80.000008"
height="60.001255"
id="svg5195"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="sdcard.svg">
<metadata
id="metadata5200">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-654.28125,-625.21875)">
<path
style="fill:none;stroke:#ffffff;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:3;stroke-opacity:1;stroke-dasharray:none"
inkscape:connector-curvature="0"
stroke-miterlimit="3"
d="m 731.87523,627.52841 0.40612,0.63624 0,46.62096 -0.56399,1.1596 -1.12305,0 -22.24234,0 -8.44016,7.27461 -7.68553,-0.0623 -0.47563,-0.29767 -0.27803,-0.58382 0,-2.04206 -5.07405,0 -3.94305,2.98613 -20.5052,0 -5.20258,0 -0.39718,-0.13334 -0.0694,-0.52447 0,-54.77808 0.0144,-0.0187 0.25471,-0.33121 0.25818,-0.0615 1.2803,0 46.21305,0 4.17044,0 22.66932,-0.15291 z"
id="path308-7-4"
sodipodi:nodetypes="cccccccccccccccccccccccccc" />
<path
style="fill:#000000;fill-opacity:1;stroke:none"
d="m 730.15625,628.22001 -22.0625,0.15625 -4.0625,0 -45,0 -1.25,0 -0.25,0.0625 -0.25,0.3125 0,52.84375 0.0625,0.5 0.40625,0.125 5.0625,0 19.96875,0 3.8125,-2.875 4.9375,0 0,1.96875 0.28125,0.5625 0.46875,0.28125 7.46875,0.0625 8.21875,-7 21.65625,0 1.09375,0 0.5625,-1.125 0,-44.96875 -0.40625,-0.59375 -0.71875,-0.3125 z m -61.125,10.28125 c 1.82713,-0.0787 3.62648,0.47484 4.21875,1.53125 1.48537,-2.34183 9.15625,-2.10021 9.15625,2.25 l 0,19.03125 -4.125,0 0,-17.15625 c -0.56407,-1.32417 -2.82364,-1.29592 -3.3125,-0.125 l 0,17.4375 -1.5,0 -0.46875,-0.0625 -1.46875,0 0,-17.4375 c -0.47005,-1.16523 -2.65468,-1.16223 -3.21875,0.15625 l 0,17.125 -4.03125,0 0,-19.03125 c 0,-2.46854 2.40084,-3.61757 4.75,-3.71875 z m 48.75,0 c 3.4878,0.0824 6.57521,3.26298 6.5,7.09375 0.002,0.0979 0.002,0.21668 0,0.3125 -0.0733,3.83086 -3.0122,6.86173 -6.5,6.78125 -3.4878,-0.0805 -6.54396,-3.26673 -6.46875,-7.09375 -0.002,-0.0977 -0.002,-0.21285 0,-0.3125 0.0733,-3.82899 2.97907,-6.86174 6.46875,-6.78125 z M 685.75,638.65751 c 1.25233,0 2.25,1.03607 2.25,2.3125 0,1.27641 -0.99767,2.3125 -2.25,2.3125 -1.25233,0 -2.28125,-1.03609 -2.28125,-2.3125 0,-1.27643 1.02892,-2.3125 2.28125,-2.3125 z m 8.71875,0.125 2.9375,0 c 1.2541,0 2.28125,0.89041 2.28125,2 0,1.10767 -1.02715,2.03125 -2.28125,2.03125 l -2.21875,0 c -1.01344,0.25296 -1.82345,0.98505 -2.15625,1.9375 -0.54338,1.55419 0.34988,3.22682 1.96875,3.75 l 2.40625,0 0.21875,0.0312 c 0.17298,0.0153 0.36579,0.0439 0.53125,0.0937 1.18078,0.36794 1.79053,1.51231 1.375,2.5625 -0.31776,0.79913 -1.16797,1.34375 -2.125,1.34375 l -2.4375,-0.0312 c -3.34114,-0.19164 -6.06943,-2.69748 -6.5,-5.96875 -0.4945,-3.76 2.18316,-7.19883 6,-7.6875 l 0,-0.0625 z m 10.09375,0 c 0.3134,-0.0123 0.61622,0.0366 0.9375,0.0937 0.62987,-0.11307 1.28578,-0.0985 1.90625,0.0625 2.53641,0.65157 4.07982,3.36426 3.46875,6.0625 l -3.78125,0 -0.0312,-1.09375 c 0.002,-0.0537 0.002,-0.13193 0,-0.1875 -0.0526,-0.82788 -0.80872,-1.45033 -1.71875,-1.40625 -0.91003,0.046 -1.61139,0.76203 -1.5625,1.59375 l 0,4.8125 0,3.125 0,0.6875 -3.78125,0 0,-8.71875 0,-0.34375 c 0.0113,-0.16481 0.0362,-0.30778 0.0625,-0.46875 0.37675,-2.39285 2.30619,-4.1329 4.5,-4.21875 z m 13.21875,3.8125 c -1.61889,0 -2.875,1.35016 -2.875,3 0,1.6502 1.25611,3 2.875,3 1.61889,0 3,-1.3498 3,-3 0,-1.64984 -1.38111,-3 -3,-3 z m -31.9375,1.0625 c 1.25222,0.0632 2.21642,1.12993 2.15625,2.40625 l 0,7.125 -4.53125,0 0,-7.125 c -0.004,-0.0751 -0.004,-0.14402 0,-0.21875 0.062,-1.27632 1.12466,-2.25075 2.375,-2.1875 z m 15.78125,9.875 c 1.77351,0.002 2.96875,0.0312 2.96875,0.0312 l -0.84375,2.4375 c 0,0 -6.58341,0.0146 -8.28125,0.0625 -1.56246,0.0422 -3.8125,-0.011 -3.8125,0.84375 0,0.66881 3.34677,1.44616 5.1875,2 2.71879,0.82404 5.00792,2.08047 4.40625,4.75 -0.84798,3.77911 -5.48319,3.5625 -11.09375,3.5625 -3.93905,0 -7.625,-0.25 -7.625,-0.25 l 1.53125,-4.28125 c 0,0 3.25363,0.39592 7.125,0.46875 2.49881,0.0479 3.34375,0.002 3.34375,-0.59375 0,-0.73206 -2.29471,-1.15725 -3.75,-1.71875 -1.52486,-0.596 -4.05245,-1.90761 -3.84375,-3.8125 0.1523,-1.46796 1.43869,-2.9988 8.21875,-3.34375 2.35027,-0.12074 4.69523,-0.15817 6.46875,-0.15625 z m 5.375,0.0312 c 2.11712,-0.006 2.87095,0.0184 4.78125,0.0625 0.47757,0.0153 1.28849,0.10627 2.28125,0.25 l -4.09375,2.5625 c -0.65243,-0.16865 -1.38653,-0.30027 -2.1875,-0.375 -0.8837,-0.0843 -1.10418,-0.0433 -1.625,-0.0625 L 707,653.56376 z m 10.34375,0.875 c 0.48134,0.13223 1.15307,0.38294 1.625,0.59375 l -6.90625,2.3125 c -0.0395,-0.0268 -0.0874,-0.0376 -0.125,-0.0625 -0.13726,-0.0958 -0.39328,-0.31235 -0.5625,-0.40625 l 5.96875,-2.4375 z m 3.34375,1.3125 c 1.29923,0.65157 2.40092,1.47148 2.96875,2.53125 0.50578,0.93328 0.39913,1.90088 -0.1875,2.84375 -4.67234,5.91493 -16.82844,5.86511 -21.21875,6.0625 l 1.90625,-5.46875 c 1.16009,-0.0441 2.83971,-0.20695 3.84375,-0.40625 1.00216,-0.20314 1.88983,-0.46505 2.625,-0.78125 0.722,-0.31429 1.28773,-0.68498 1.65625,-1.0625 0.36287,-0.37178 0.50143,-0.75956 0.4375,-1.15625 -0.015,-0.0974 -0.0486,-0.18544 -0.0937,-0.28125 -0.0263,-0.0613 -0.0505,-0.1281 -0.0937,-0.1875 l 8.15625,-2.09375 z"
id="path308-7"
inkscape:connector-curvature="0" />
<g
id="g5062"
transform="matrix(1.8802169,0,0,1.9163882,639.48533,-1272.1248)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.9 KiB

511
app/src/BooksBook.cpp Normal file
View file

@ -0,0 +1,511 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksBook.h"
#include "BooksDefs.h"
#include "BooksTask.h"
#include "BooksTextView.h"
#include "BooksTextStyle.h"
#include "BooksPaintContext.h"
#include "HarbourJson.h"
#include "HarbourDebug.h"
#include "ZLImage.h"
#include "image/ZLQtImageManager.h"
#include "bookmodel/BookModel.h"
#include "library/Author.h"
#include <QFile>
#include <QDirIterator>
#include <QGuiApplication>
#include <QScreen>
#define BOOK_STATE_POSITION "position"
#define BOOK_COVER_SUFFIX ".cover."
// ==========================================================================
// BooksBook::CoverContext
// ==========================================================================
class BooksBook::CoverPaintContext : public BooksPaintContext {
public:
CoverPaintContext();
void drawImage(int x, int y, const ZLImageData& image);
void drawImage(int x, int y, const ZLImageData& image, int width, int height, ScalingType type);
void handleImage(const ZLImageData& image);
bool gotIt() const;
public:
static QSize gMaxScreenSize;
static bool gMaxScreenSizeKnown;
QImage iImage;
};
QSize BooksBook::CoverPaintContext::gMaxScreenSize(480,640);
bool BooksBook::CoverPaintContext::gMaxScreenSizeKnown = false;
BooksBook::CoverPaintContext::CoverPaintContext()
{
if (!gMaxScreenSizeKnown) {
QList<QScreen*> screens = qGuiApp->screens();
const int n = screens.count();
for (int i=0; i<n; i++) {
QSize s = screens.at(i)->size();
gMaxScreenSize.setWidth(qMax(s.width(), gMaxScreenSize.width()));
gMaxScreenSize.setHeight(qMax(s.width(), gMaxScreenSize.height()));
}
}
setWidth(gMaxScreenSize.width());
setHeight(gMaxScreenSize.height());
}
void BooksBook::CoverPaintContext::drawImage(int x, int y, const ZLImageData& image)
{
handleImage(image);
}
void BooksBook::CoverPaintContext::drawImage(int x, int y, const ZLImageData& image,
int width, int height, ScalingType type)
{
handleImage(image);
}
void BooksBook::CoverPaintContext::handleImage(const ZLImageData& image)
{
const QImage* qImage = ((ZLQtImageData&)image).image();
HDEBUG(image.width() << 'x' << image.height());
if (qImage->height() > qImage->width() &&
qImage->width() > iImage.width() &&
qImage->height() > iImage.height()) {
iImage = *qImage;
}
}
bool BooksBook::CoverPaintContext::gotIt() const
{
return iImage.width() >= 50 &&
iImage.height() >= 80 &&
iImage.height() > iImage.width();
}
// ==========================================================================
// BooksBook::CoverTask
// ==========================================================================
class BooksBook::CoverTask : public BooksTask
{
public:
CoverTask(BooksStorage aStorage, shared_ptr<Book> aBook) :
iStorage(aStorage), iBook(aBook), iCoverMissing(false) {}
bool hasImage() const;
public:
BooksStorage iStorage;
shared_ptr<Book> iBook;
QImage iCoverImage;
bool iCoverMissing;
};
inline bool BooksBook::CoverTask::hasImage() const
{
return iCoverImage.width() > 0 && iCoverImage.height() > 0;
}
// ==========================================================================
// BooksBook::LoadCoverTask
// ==========================================================================
class BooksBook::LoadCoverTask : public BooksBook::CoverTask
{
public:
LoadCoverTask(BooksStorage aStorage, shared_ptr<Book> aBook,
shared_ptr<FormatPlugin> aFormatPlugin) :
BooksBook::CoverTask(aStorage, aBook),
iFormatPlugin(aFormatPlugin) {}
virtual void performTask();
public:
shared_ptr<FormatPlugin> iFormatPlugin;
};
void BooksBook::LoadCoverTask::performTask()
{
if (!isCanceled()) {
// Try to load cached (or custom) cover
if (iStorage.isValid()) {
QString coverPrefix(QString::fromStdString(
iBook->file().name(false)) + BOOK_COVER_SUFFIX);
QDirIterator it(iStorage.configDir());
while (it.hasNext()) {
QString path(it.next());
if (it.fileName().startsWith(coverPrefix)) {
if (QFile(path).size() == 0) {
HDEBUG("no cover for" << iBook->title().c_str());
iCoverMissing = true;
return;
} else if (iCoverImage.load(path)) {
HDEBUG("loaded cover" << iCoverImage.width() << 'x' <<
iCoverImage.height() << "for" <<
iBook->title().c_str() << "from" <<
qPrintable(path));
return;
}
}
}
}
// OK, fetch one from the book file
shared_ptr<ZLImageData> imageData;
shared_ptr<ZLImage> image = iFormatPlugin->coverImage(iBook->file());
if (!image.isNull()) {
imageData = ZLImageManager::Instance().imageData(*image);
}
if (!imageData.isNull()) {
QImage* qImage = (QImage*)((ZLQtImageData&)*imageData).image();
if (qImage) iCoverImage = *qImage;
}
}
#if HARBOUR_DEBUG
if (hasImage()) {
HDEBUG("loaded cover" << iCoverImage.width() << 'x' <<
iCoverImage.height() << "for" << iBook->title().c_str());
} else if (isCanceled()) {
HDEBUG("cancelled" << iBook->title().c_str());
} else {
HDEBUG("no cover found in" << iBook->title().c_str());
}
#endif
}
// ==========================================================================
// BooksBook::GuessCoverTask
// ==========================================================================
class BooksBook::GuessCoverTask : public BooksBook::CoverTask
{
public:
GuessCoverTask(BooksStorage aStorage, shared_ptr<Book> aBook) :
BooksBook::CoverTask(aStorage, aBook) {}
virtual void performTask();
};
void BooksBook::GuessCoverTask::performTask()
{
if (!isCanceled()) {
BooksMargins margins;
CoverPaintContext context;
BookModel bookModel(iBook);
shared_ptr<ZLTextModel> model(bookModel.bookTextModel());
BooksTextView view(context, BooksTextStyle::defaults(), margins);
view.setModel(model);
view.rewind();
if (!isCanceled()) {
view.rewind();
if (!isCanceled()) {
view.paint();
iCoverImage = context.iImage;
}
}
}
QString coverPath;
if (iStorage.isValid()) {
coverPath = iStorage.configDir().path() + "/" +
QString::fromStdString(iBook->file().name(false)) +
BOOK_COVER_SUFFIX + "jpg";
}
if (hasImage()) {
HDEBUG("using image" << iCoverImage.width() << 'x' <<
iCoverImage.width() << "as cover for" << iBook->title().c_str());
// Save the extracted image
if (!coverPath.isEmpty() && iCoverImage.save(coverPath)) {
HDEBUG("saved cover to" << qPrintable(coverPath));
}
} else if (isCanceled()) {
HDEBUG("cancelled" << iBook->title().c_str());
} else {
HDEBUG("no cover for" << iBook->title().c_str());
iCoverMissing = true;
// Create empty file. Guessing the cover image is an expensive task,
// we don't want to do it every time the application is started.
if (!coverPath.isEmpty() &&
// Check if the book file still exists - the failure could've
// been caused by the SD-card removal.
QFile::exists(QString::fromStdString(iBook->file().path()))) {
QFile(coverPath).open(QIODevice::WriteOnly);
}
}
}
// ==========================================================================
// BooksBook
// ==========================================================================
// This constructor isn't really used, but is required by qmlRegisterType
BooksBook::BooksBook(QObject* aParent) :
QObject(aParent),
iRef(-1)
{
init();
}
BooksBook::BooksBook(const BooksStorage& aStorage, shared_ptr<Book> aBook) :
QObject(NULL),
iRef(1),
iStorage(aStorage),
iBook(aBook),
iTaskQueue(BooksTaskQueue::instance())
{
init();
HASSERT(!iBook.isNull());
iTitle = QString::fromStdString(iBook->title());
iPath = QString::fromStdString(iBook->file().path());
iFileName = QString::fromStdString(iBook->file().name(false));
iFormatPlugin = PluginCollection::Instance().plugin(*iBook);
AuthorList authors(iBook->authors());
const int n = authors.size();
for (int i=0; i<n; i++) {
if (i > 0) iAuthors += ", ";
iAuthors += QString::fromStdString(authors[i]->name());
}
if (iStorage.isValid()) {
iStateFilePath = iStorage.configDir().path() + "/" +
iFileName + BOOKS_STATE_FILE_SUFFIX;
// Load the state
QVariantMap state;
if (HarbourJson::load(iStateFilePath, state)) {
iLastPos = BooksPos::fromVariant(state.value(BOOK_STATE_POSITION));
}
}
// Refcounted BooksBook objects are managed by C++ code
QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
}
void BooksBook::init()
{
iCoverTask = NULL;
iCoverTasksDone = false;
iCopyingOut = false;
iSaveTimer = NULL;
}
BooksBook::~BooksBook()
{
HDEBUG(qPrintable(iPath));
HASSERT(!iRef.load());
if (iCoverTask) iCoverTask->release(this);
}
BooksItem* BooksBook::retain()
{
if (iRef.load() >= 0) {
iRef.ref();
}
return this;
}
void BooksBook::release()
{
if (iRef.load() >= 0 && !iRef.deref()) {
delete this;
}
}
QObject* BooksBook::object()
{
return this;
}
BooksShelf* BooksBook::shelf()
{
return NULL;
}
BooksBook* BooksBook::book()
{
return this;
}
QString BooksBook::name() const
{
return iTitle;
}
QString BooksBook::fileName() const
{
return iFileName;
}
void BooksBook::setLastPos(const BooksPos& aPos)
{
if (iLastPos != aPos) {
iLastPos = aPos;
// We only need save timer if we have the state file
if (!iSaveTimer && iStorage.isValid()) {
iSaveTimer = new BooksSaveTimer(this);
connect(iSaveTimer, SIGNAL(save()), SLOT(saveState()));
}
if (iSaveTimer) iSaveTimer->requestSave();
}
}
void BooksBook::setCopyingOut(bool aValue)
{
if (iCopyingOut != aValue) {
const bool wasAccessible = accessible();
iCopyingOut = aValue;
Q_EMIT copyingOutChanged();
if (wasAccessible != accessible()) {
Q_EMIT accessibleChanged();
}
}
}
QImage BooksBook::coverImage()
{
return iCoverImage;
}
bool BooksBook::hasCoverImage() const
{
return iCoverImage.width() > 0 && iCoverImage.height() > 0;
}
void BooksBook::setCoverImage(QImage aImage)
{
if (iCoverImage != aImage) {
iCoverImage = aImage;
Q_EMIT coverImageChanged();
}
}
bool BooksBook::requestCoverImage()
{
if (!iBook.isNull() && !iFormatPlugin.isNull() &&
!iCoverTasksDone && !iCoverTask) {
HDEBUG(iTitle);
iCoverTask = new LoadCoverTask(iStorage, iBook, iFormatPlugin);
connect(iCoverTask, SIGNAL(done()), SLOT(onLoadCoverTaskDone()));
iTaskQueue->submit(iCoverTask);
Q_EMIT loadingCoverChanged();
}
return iCoverTask != NULL;
}
void BooksBook::cancelCoverRequest()
{
if (iCoverTask) {
iCoverTask->release(this);
iCoverTask = NULL;
}
}
bool BooksBook::coverTaskDone()
{
HASSERT(sender() == iCoverTask);
HASSERT(!iCoverTasksDone);
HDEBUG(iTitle << iCoverTask->hasImage());
const bool gotCover = iCoverTask->hasImage();
if (gotCover) {
iCoverImage = iCoverTask->iCoverImage;
Q_EMIT coverImageChanged();
}
iCoverTask->release(this);
iCoverTask = NULL;
return gotCover;
}
void BooksBook::onLoadCoverTaskDone()
{
HDEBUG(iTitle);
const bool coverMissing = iCoverTask->iCoverMissing;
if (coverTaskDone() || coverMissing) {
iCoverTasksDone = true;
Q_EMIT loadingCoverChanged();
} else {
iCoverTask = new GuessCoverTask(iStorage, iBook);
connect(iCoverTask, SIGNAL(done()), SLOT(onGuessCoverTaskDone()));
iTaskQueue->submit(iCoverTask);
}
}
void BooksBook::onGuessCoverTaskDone()
{
HDEBUG(iTitle);
coverTaskDone();
iCoverTasksDone = true;
Q_EMIT loadingCoverChanged();
}
void BooksBook::saveState()
{
if (!iStateFilePath.isEmpty()) {
QVariantMap state;
HarbourJson::load(iStateFilePath, state);
state.insert(BOOK_STATE_POSITION, iLastPos.toVariant());
if (HarbourJson::save(iStateFilePath, state)) {
HDEBUG("wrote" << iStateFilePath);
}
}
}
void BooksBook::deleteFiles()
{
if (iStorage.isValid()) {
if (QFile::remove(iPath)) {
HDEBUG("deleted" << qPrintable(iPath));
} else {
HWARN("failed to delete" << qPrintable(iPath));
}
QDirIterator it(iStorage.configDir());
while (it.hasNext()) {
QString path(it.next());
if (it.fileName().startsWith(iFileName)) {
if (QFile::remove(path)) {
HDEBUG(qPrintable(path));
} else {
HWARN("failed to delete" << qPrintable(path));
}
}
}
}
}

136
app/src/BooksBook.h Normal file
View file

@ -0,0 +1,136 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_BOOK_H
#define BOOKS_BOOK_H
#include "BooksItem.h"
#include "BooksPos.h"
#include "BooksStorage.h"
#include "BooksSaveTimer.h"
#include "BooksTaskQueue.h"
#include "ZLImageManager.h"
#include "formats/FormatPlugin.h"
#include "library/Book.h"
#include <QImage>
#include <QtQml>
class BooksBook : public QObject, public BooksItem
{
class CoverTask;
class LoadCoverTask;
class GuessCoverTask;
class CoverPaintContext;
Q_OBJECT
Q_PROPERTY(QString path READ path CONSTANT)
Q_PROPERTY(QString name READ title CONSTANT)
Q_PROPERTY(QString title READ title CONSTANT)
Q_PROPERTY(QString authors READ authors CONSTANT)
Q_PROPERTY(BooksBook* book READ book CONSTANT)
Q_PROPERTY(bool accessible READ accessible NOTIFY accessibleChanged)
Q_PROPERTY(bool loadingCover READ loadingCover NOTIFY loadingCoverChanged)
Q_PROPERTY(bool copyingOut READ copyingOut NOTIFY copyingOutChanged)
public:
BooksBook(QObject* aParent = NULL);
BooksBook(const BooksStorage& aStorage, shared_ptr<Book> aBook);
~BooksBook();
QString path() const { return iPath; }
QString title() const { return iTitle; }
QString authors() const { return iAuthors; }
BooksPos lastPos() const { return iLastPos; }
void setLastPos(const BooksPos& aPos);
shared_ptr<Book> bookRef() const { return iBook; }
bool accessible() const { return !iCopyingOut; }
bool copyingOut() const { return iCopyingOut; }
bool loadingCover() const { return !iCoverTasksDone; }
bool hasCoverImage() const;
bool requestCoverImage();
void cancelCoverRequest();
void setCoverImage(QImage aImage);
QImage coverImage();
void setCopyingOut(bool aValue);
void deleteFiles();
// BooksListItem
virtual BooksItem* retain();
virtual void release();
virtual QObject* object();
virtual BooksShelf* shelf();
virtual BooksBook* book();
virtual QString name() const;
virtual QString fileName() const;
Q_SIGNALS:
void coverImageChanged();
void loadingCoverChanged();
void accessibleChanged();
void copyingOutChanged();
void movedAway();
private Q_SLOTS:
void onLoadCoverTaskDone();
void onGuessCoverTaskDone();
void saveState();
private:
void init();
bool coverTaskDone();
private:
QAtomicInt iRef;
BooksPos iLastPos;
BooksStorage iStorage;
shared_ptr<Book> iBook;
QImage iCoverImage;
shared_ptr<FormatPlugin> iFormatPlugin;
shared_ptr<BooksTaskQueue> iTaskQueue;
BooksSaveTimer* iSaveTimer;
CoverTask* iCoverTask;
bool iCoverTasksDone;
bool iCopyingOut;
QString iTitle;
QString iAuthors;
QString iFileName;
QString iPath;
QString iStateFilePath;
};
QML_DECLARE_TYPE(BooksBook)
#endif // BOOKS_BOOK_H

543
app/src/BooksBookModel.cpp Normal file
View file

@ -0,0 +1,543 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksBookModel.h"
#include "BooksTextStyle.h"
#include "HarbourDebug.h"
#include "ZLTextHyphenator.h"
// ==========================================================================
// BooksBookModel::Data
// ==========================================================================
class BooksBookModel::Data {
public:
Data(int aWidth, int aHeight) : iWidth(aWidth), iHeight(aHeight) {}
int pickPage(const BooksPos& aPagePos) const;
int pickPage(const BooksPos& aPagePos, const BooksPos& aNextPagePos,
int aPageCount) const;
public:
int iWidth;
int iHeight;
shared_ptr<BookModel> iBookModel;
BooksPos::List iPageMarks;
};
int BooksBookModel::Data::pickPage(const BooksPos& aPagePos) const
{
int page = 0;
if (aPagePos.valid()) {
BooksPos::ConstIterator it = qFind(iPageMarks, aPagePos);
if (it == iPageMarks.end()) {
it = qUpperBound(iPageMarks, aPagePos);
page = (int)(it - iPageMarks.begin()) - 1;
HDEBUG("using page" << page << "for" << aPagePos);
} else {
page = it - iPageMarks.begin();
HDEBUG("found" << aPagePos << "at page" << page);
}
}
return page;
}
int BooksBookModel::Data::pickPage(const BooksPos& aPagePos,
const BooksPos& aNextPagePos, int aPageCount) const
{
int page = 0;
if (aPagePos.valid()) {
if (!aNextPagePos.valid()) {
// Last page stays the last
page = iPageMarks.count() - 1;
HDEBUG("last page" << page);
} else {
BooksPos::ConstIterator it = qFind(iPageMarks, aPagePos);
if (it == iPageMarks.end()) {
// Two 90-degrees rotations should return the reader
// back to the same page. That's what this is about.
if (iPageMarks.count() > aPageCount) {
it = qUpperBound(iPageMarks, aPagePos);
page = (int)(it - iPageMarks.begin()) - 1;
HDEBUG("using page" << page << "for" << aPagePos);
} else {
it = qLowerBound(iPageMarks, aNextPagePos);
page = (int)(it - iPageMarks.begin());
if (page > 0) page--;
HDEBUG("using page" << page << "for" << aNextPagePos);
}
} else {
page = it - iPageMarks.begin();
HDEBUG("found" << aPagePos << "at page" << page);
}
}
}
return page;
}
// ==========================================================================
// BooksBookModel::Task
// ==========================================================================
class BooksBookModel::Task : public BooksTask
{
Q_OBJECT
public:
Task(BooksBookModel* aReceiver, shared_ptr<Book> aBook,
const BooksPos& aPagePos, const BooksPos& aNextPagePos,
const BooksPos& aLastPos, int aPageCount);
~Task();
void performTask();
Q_SIGNALS:
void progress(int aProgress);
public:
shared_ptr<Book> iBook;
shared_ptr<ZLTextStyle> iTextStyle;
BooksMargins iMargins;
BooksPaintContext iPaint;
BooksBookModel::Data* iData;
BooksPos iPagePos;
BooksPos iNextPagePos;
BooksPos iLastPos;
int iOldPageCount;
int iPage;
};
BooksBookModel::Task::Task(BooksBookModel* aModel,
shared_ptr<Book> aBook, const BooksPos& aPagePos,
const BooksPos& aNextPagePos, const BooksPos& aLastPos, int aPageCount) :
iBook(aBook),
iTextStyle(aModel->textStyle()),
iMargins(aModel->margins()),
iPaint(aModel->width(), aModel->height()),
iData(NULL),
iPagePos(aPagePos),
iNextPagePos(aNextPagePos),
iLastPos(aLastPos),
iOldPageCount(aPageCount),
iPage(-1)
{
aModel->connect(this, SIGNAL(done()), SLOT(onResetDone()));
aModel->connect(this, SIGNAL(progress(int)), SLOT(onResetProgress(int)),
Qt::QueuedConnection);
}
BooksBookModel::Task::~Task()
{
delete iData;
}
void BooksBookModel::Task::performTask()
{
if (!isCanceled()) {
iData = new BooksBookModel::Data(iPaint.width(), iPaint.height());
iData->iBookModel = new BookModel(iBook);
shared_ptr<ZLTextModel> model(iData->iBookModel->bookTextModel());
ZLTextHyphenator::Instance().load(iBook->language());
if (!isCanceled()) {
BooksTextView view(iPaint, iTextStyle, iMargins);
view.setModel(model);
if (model->paragraphsNumber() > 0) {
BooksPos mark = view.rewind();
iData->iPageMarks.append(mark);
Q_EMIT progress(iData->iPageMarks.count());
while (!isCanceled() && view.nextPage()) {
mark = view.position();
iData->iPageMarks.append(mark);
Q_EMIT progress(iData->iPageMarks.count());
}
}
}
}
if (!isCanceled()) {
HDEBUG(iData->iPageMarks.count() << "page(s)" << qPrintable(
QString("%1x%2").arg(iData->iWidth).arg(iData->iHeight)));
iPage = iPagePos.valid() ?
iData->pickPage(iPagePos, iNextPagePos, iOldPageCount) :
iData->pickPage(iLastPos);
} else {
HDEBUG("giving up" << qPrintable(QString("%1x%2").arg(iPaint.width()).
arg(iPaint.height())) << "paging");
}
}
// ==========================================================================
// BooksBookModel
// ==========================================================================
enum BooksBookModelRole {
BooksBookModelPageIndex = Qt::UserRole
};
BooksBookModel::BooksBookModel(QObject* aParent) :
QAbstractListModel(aParent),
iCurrentPage(-1),
iProgress(0),
iBook(NULL),
iSettings(NULL),
iTask(NULL),
iData(NULL),
iData2(NULL),
iTaskQueue(BooksTaskQueue::instance()),
iTextStyle(BooksTextStyle::defaults())
{
HDEBUG("created");
#if QT_VERSION < 0x050000
setRoleNames(roleNames());
#endif
}
BooksBookModel::~BooksBookModel()
{
if (iTask) iTask->release(this);
delete iData;
delete iData2;
HDEBUG("destroyed");
}
void BooksBookModel::setBook(BooksBook* aBook)
{
shared_ptr<Book> oldBook;
shared_ptr<Book> newBook;
if (iBook != aBook) {
const QString oldTitle(iTitle);
if (iBook) iBook->release();
if (aBook) {
(iBook = aBook)->retain();
iBookRef = newBook;
iTitle = iBook->title();
HDEBUG(iTitle);
} else {
iBook = NULL;
iBookRef.reset();
iTitle = QString();
HDEBUG("<none>");
}
startReset();
if (oldTitle != iTitle) {
Q_EMIT titleChanged();
}
Q_EMIT bookModelChanged();
Q_EMIT bookChanged();
}
}
bool BooksBookModel::loading() const
{
return (iTask != NULL);
}
void BooksBookModel::setSettings(BooksSettings* aSettings)
{
if (iSettings != aSettings) {
shared_ptr<ZLTextStyle> oldTextStyle(iTextStyle);
if (iSettings) iSettings->disconnect(this);
iSettings = aSettings;
if (iSettings) {
iTextStyle = iSettings->textStyle();
connect(iSettings, SIGNAL(textStyleChanged()), SLOT(onTextStyleChanged()));
} else {
iTextStyle = BooksTextStyle::defaults();
}
if (!BooksTextStyle::equalLayout(oldTextStyle, iTextStyle)) {
startReset();
}
Q_EMIT settingsChanged();
}
}
void BooksBookModel::setCurrentPage(int aPage)
{
if (iCurrentPage != aPage) {
iCurrentPage = aPage;
if (iData &&
iCurrentPage >= 0 &&
iCurrentPage < iData->iPageMarks.count()) {
iBook->setLastPos(iData->iPageMarks.at(iCurrentPage));
HDEBUG(aPage << iBook->lastPos());
} else {
HDEBUG(aPage);
}
Q_EMIT currentPageChanged();
}
}
int BooksBookModel::pageCount() const
{
return iData ? iData->iPageMarks.count() : 0;
}
BooksPos::List BooksBookModel::pageMarks() const
{
return iData ? iData->iPageMarks : BooksPos::List();
}
BooksPos BooksBookModel::pageMark(int aPage) const
{
if (aPage >= 0 && iData) {
const int n = iData->iPageMarks.count();
if (aPage < n) {
return iData->iPageMarks.at(aPage);
}
}
return BooksPos();
}
shared_ptr<BookModel> BooksBookModel::bookModel() const
{
return iData ? iData->iBookModel : NULL;
}
shared_ptr<ZLTextModel> BooksBookModel::bookTextModel() const
{
shared_ptr<ZLTextModel> model;
if (iData && !iData->iBookModel.isNull()) {
model = iData->iBookModel->bookTextModel();
}
return model;
}
shared_ptr<ZLTextModel> BooksBookModel::contentsModel() const
{
shared_ptr<ZLTextModel> model;
if (iData && !iData->iBookModel.isNull()) {
model = iData->iBookModel->contentsModel();
}
return model;
}
void BooksBookModel::setLeftMargin(int aMargin)
{
if (iMargins.iLeft != aMargin) {
iMargins.iLeft = aMargin;
HDEBUG(aMargin);
startReset();
Q_EMIT leftMarginChanged();
}
}
void BooksBookModel::setRightMargin(int aMargin)
{
if (iMargins.iRight != aMargin) {
iMargins.iRight = aMargin;
HDEBUG(aMargin);
startReset();
Q_EMIT rightMarginChanged();
}
}
void BooksBookModel::setTopMargin(int aMargin)
{
if (iMargins.iTop != aMargin) {
iMargins.iTop = aMargin;
HDEBUG(aMargin);
startReset();
Q_EMIT topMarginChanged();
}
}
void BooksBookModel::setBottomMargin(int aMargin)
{
if (iMargins.iBottom != aMargin) {
iMargins.iBottom = aMargin;
HDEBUG(aMargin);
startReset();
Q_EMIT bottomMarginChanged();
}
}
void BooksBookModel::updateModel(int aPrevPageCount)
{
const int newPageCount = pageCount();
if (aPrevPageCount != newPageCount) {
if (newPageCount > aPrevPageCount) {
beginInsertRows(QModelIndex(), aPrevPageCount, newPageCount-1);
endInsertRows();
} else {
beginRemoveRows(QModelIndex(), newPageCount, aPrevPageCount-1);
endRemoveRows();
}
Q_EMIT pageCountChanged();
}
}
void BooksBookModel::setSize(QSize aSize)
{
if (iSize != aSize) {
iSize = aSize;
const int w = width();
const int h = height();
HDEBUG(aSize);
if (iData && iData->iWidth == w && iData->iHeight == h) {
HDEBUG("size didn't change");
} else if (iData2 && iData2->iWidth == w && iData2->iHeight == h) {
HDEBUG("switching to backup layout");
const int oldPageCount(pageCount());
BooksPos page1 = pageMark(iCurrentPage);
BooksPos page2 = pageMark(iCurrentPage+1);
Data* tmp = iData;
iData = iData2;
iData2 = tmp;
if (iData) {
// Cancel unnecessary paging task
if (iTask) {
iTask->release(this);
iTask = NULL;
}
updateModel(oldPageCount);
Q_EMIT pageMarksChanged();
Q_EMIT jumpToPage(iData->pickPage(page1, page2, oldPageCount));
} else {
startReset(false);
}
} else {
startReset(false);
}
Q_EMIT sizeChanged();
}
}
void BooksBookModel::onTextStyleChanged()
{
HDEBUG(iTitle);
iTextStyle = iSettings->textStyle();
startReset();
}
void BooksBookModel::startReset(bool aFullReset)
{
BooksLoadingSignalBlocker block(this);
const BooksPos thisPage = pageMark(iCurrentPage);
const BooksPos nextPage = pageMark(iCurrentPage+1);
if (iTask) {
iTask->release(this);
iTask = NULL;
}
const int oldPageCount(pageCount());
if (oldPageCount > 0) {
beginResetModel();
}
delete iData2;
if (aFullReset) {
delete iData;
iData2 = NULL;
} else {
iData2 = iData;
}
iData = NULL;
if (iBook && width() > 0 && height() > 0) {
iTask = new Task(this, iBook->bookRef(), thisPage, nextPage,
iBook->lastPos(), oldPageCount);
iTaskQueue->submit(iTask);
}
if (oldPageCount > 0) {
endResetModel();
Q_EMIT pageMarksChanged();
Q_EMIT pageCountChanged();
}
if (iCurrentPage != 0) {
iCurrentPage = 0;
Q_EMIT currentPageChanged();
}
if (iProgress != 0) {
iProgress = 0;
Q_EMIT progressChanged();
}
}
void BooksBookModel::onResetProgress(int aProgress)
{
if (iTask && aProgress > iProgress) {
iProgress = aProgress;
Q_EMIT progressChanged();
}
}
void BooksBookModel::onResetDone()
{
HASSERT(sender() == iTask);
HASSERT(iTask->iData);
HASSERT(!iData);
const int oldPageCount(pageCount());
shared_ptr<BookModel> oldBookModel(bookModel());
BooksLoadingSignalBlocker block(this);
int page = iTask->iPage;
iData = iTask->iData;
iTask->iData = NULL;
iTask->release(this);
iTask = NULL;
updateModel(oldPageCount);
Q_EMIT jumpToPage(page);
Q_EMIT pageMarksChanged();
if (oldBookModel != bookModel()) {
Q_EMIT bookModelChanged();
}
}
QHash<int,QByteArray> BooksBookModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles.insert(BooksBookModelPageIndex, "pageIndex");
return roles;
}
int BooksBookModel::rowCount(const QModelIndex&) const
{
return pageCount();
}
QVariant BooksBookModel::data(const QModelIndex& aIndex, int aRole) const
{
const int i = aIndex.row();
if (i >= 0 && i < pageCount()) {
switch (aRole) {
case BooksBookModelPageIndex: return i;
}
}
return QVariant();
}
#include "BooksBookModel.moc"

168
app/src/BooksBookModel.h Normal file
View file

@ -0,0 +1,168 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_BOOK_MODEL_H
#define BOOKS_BOOK_MODEL_H
#include "BooksBook.h"
#include "BooksTask.h"
#include "BooksTaskQueue.h"
#include "BooksTextView.h"
#include "BooksSettings.h"
#include "BooksPos.h"
#include "BooksPaintContext.h"
#include "BooksLoadingProperty.h"
#include "ZLTextStyle.h"
#include "bookmodel/BookModel.h"
#include <QHash>
#include <QList>
#include <QVariant>
#include <QByteArray>
#include <QAbstractListModel>
#include <QtQml>
#include <QQuickItem>
class BooksBookModel: public QAbstractListModel, private BooksLoadingProperty
{
Q_OBJECT
Q_PROPERTY(QString title READ title NOTIFY titleChanged)
Q_PROPERTY(QSize size READ size WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(int progress READ progress NOTIFY progressChanged)
Q_PROPERTY(int pageCount READ pageCount NOTIFY pageCountChanged)
Q_PROPERTY(int currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged)
Q_PROPERTY(int leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
Q_PROPERTY(int topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged)
Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
Q_PROPERTY(BooksBook* book READ book WRITE setBook NOTIFY bookChanged)
Q_PROPERTY(BooksSettings* settings READ settings WRITE setSettings NOTIFY settingsChanged)
public:
explicit BooksBookModel(QObject* aParent = NULL);
~BooksBookModel();
bool loading() const;
int pageCount() const;
int progress() const { return iProgress; }
QString title() const { return iTitle; }
int width() const { return iSize.width(); }
int height() const { return iSize.height(); }
QSize size() const { return iSize; }
void setSize(QSize aSize);
int currentPage() const { return iCurrentPage; }
void setCurrentPage(int aPage);
BooksBook* book() const { return iBook; }
void setBook(BooksBook* aBook);
BooksSettings* settings() const { return iSettings; }
void setSettings(BooksSettings* aModel);
int leftMargin() const { return iMargins.iLeft; }
int rightMargin() const { return iMargins.iRight; }
int topMargin() const { return iMargins.iTop; }
int bottomMargin() const { return iMargins.iBottom; }
void setLeftMargin(int aMargin);
void setRightMargin(int aMargin);
void setTopMargin(int aMargin);
void setBottomMargin(int aMargin);
BooksPos::List pageMarks() const;
BooksPos pageMark(int aPage) const;
BooksMargins margins() const { return iMargins; }
shared_ptr<Book> bookRef() const { return iBookRef; }
shared_ptr<BookModel> bookModel() const;
shared_ptr<ZLTextModel> bookTextModel() const;
shared_ptr<ZLTextModel> contentsModel() const;
shared_ptr<ZLTextStyle> textStyle() const { return iTextStyle; }
// QAbstractListModel
virtual QHash<int,QByteArray> roleNames() const;
virtual int rowCount(const QModelIndex& aParent) const;
virtual QVariant data(const QModelIndex& aIndex, int aRole) const;
private:
void updateSize();
void updateModel(int aPrevPageCount);
void startReset(bool aFullReset = true);
private Q_SLOTS:
void onResetProgress(int aProgress);
void onResetDone();
void onTextStyleChanged();
Q_SIGNALS:
void sizeChanged();
void bookChanged();
void bookModelChanged();
void titleChanged();
void loadingChanged();
void pageCountChanged();
void pageMarksChanged();
void progressChanged();
void currentPageChanged();
void settingsChanged();
void leftMarginChanged();
void rightMarginChanged();
void topMarginChanged();
void bottomMarginChanged();
void jumpToPage(int index);
private:
class Data;
class Task;
int iCurrentPage;
int iProgress;
QSize iSize;
QString iTitle;
BooksMargins iMargins;
BooksBook* iBook;
shared_ptr<Book> iBookRef;
BooksSettings* iSettings;
Task* iTask;
Data* iData;
Data* iData2;
shared_ptr<BooksTaskQueue> iTaskQueue;
shared_ptr<ZLTextStyle> iTextStyle;
};
QML_DECLARE_TYPE(BooksBookModel)
#endif // BOOKS_BOOK_MODEL_H

156
app/src/BooksConfig.cpp Normal file
View file

@ -0,0 +1,156 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksConfig.h"
#include "HarbourDebug.h"
// ==========================================================================
// BooksConfig::Private
// ==========================================================================
class BooksConfig::Private
{
public:
};
// ==========================================================================
// BooksConfig
// ==========================================================================
BooksConfig::BooksConfig() : iPrivate(new Private)
{
}
BooksConfig::~BooksConfig()
{
delete iPrivate;
}
void
BooksConfig::listOptionNames(
const std::string& aGroup,
std::vector<std::string>& names)
{
HDEBUG(aGroup.c_str());
}
void
BooksConfig::listOptionGroups(
std::vector<std::string>& aGroups)
{
HDEBUG("sorry...");
}
void
BooksConfig::removeGroup(
const std::string& aName)
{
HDEBUG(aName.c_str());
}
const std::string&
BooksConfig::getDefaultValue(
const std::string& aGroup,
const std::string& aName,
const std::string& aDefault) const
{
HDEBUG(aGroup.c_str() << aName.c_str() << "(" << aDefault.c_str() << ")");
return aDefault;
}
const std::string&
BooksConfig::getValue(
const std::string& aGroup,
const std::string& aName,
const std::string& aDefault) const
{
HDEBUG(aGroup.c_str() << aName.c_str() << "(" << aDefault.c_str() << ")");
return aDefault;
}
void
BooksConfig::setValue(
const std::string& aGroup,
const std::string& aName,
const std::string& aValue,
const std::string& aCategory)
{
HDEBUG(aGroup.c_str() << aName.c_str() << aValue.c_str() << aCategory.c_str());
}
void
BooksConfig::unsetValue(
const std::string& aGroup,
const std::string& aName)
{
HDEBUG(aGroup.c_str() << aName.c_str());
}
bool BooksConfig::isAutoSavingSupported() const
{
HDEBUG("NO");
return false;
}
void BooksConfig::startAutoSave(int aSeconds)
{
HDEBUG(aSeconds);
}
// ==========================================================================
// BooksConfigManager
// ==========================================================================
BooksConfigManager::BooksConfigManager()
{
HASSERT(!ourInstance);
HASSERT(!ourIsInitialised);
ourInstance = this;
}
BooksConfigManager::~BooksConfigManager()
{
HASSERT(ourInstance == this);
if (ourInstance == this) {
ourInstance = NULL;
ourIsInitialised = false;
}
}
ZLConfig* BooksConfigManager::createConfig() const
{
ZLConfig* config = new BooksConfig();
ourIsInitialised = true;
return config;
}

74
app/src/BooksConfig.h Normal file
View file

@ -0,0 +1,74 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_CONFIG_H
#define BOOKS_CONFIG_H
#include "BooksTypes.h"
#include <ZLConfig.h>
class BooksConfigManager : public ZLConfigManager {
public:
BooksConfigManager();
~BooksConfigManager();
ZLConfig* createConfig() const;
};
class BooksConfig : public ZLConfig
{
public:
BooksConfig();
~BooksConfig();
// ZLConfig
void listOptionNames(const std::string& aGroup, std::vector<std::string>& names);
void listOptionGroups(std::vector<std::string>& aGroups);
void removeGroup(const std::string& aName);
const std::string& getDefaultValue(const std::string& aGroup, const std::string& aName, const std::string& aDefault) const;
const std::string& getValue(const std::string& aGroup, const std::string& aName, const std::string& aDefault) const;
void setValue(const std::string& aGroup, const std::string& aName, const std::string& aValue, const std::string& aCategory);
void unsetValue(const std::string& aGroup, const std::string& aName);
bool isAutoSavingSupported() const;
void startAutoSave(int aSeconds);
private:
void updateRenderType();
private:
class Private;
Private* iPrivate;
};
#endif // BOOKS_CONFIG_H

138
app/src/BooksCoverModel.cpp Normal file
View file

@ -0,0 +1,138 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksCoverModel.h"
#include "BooksShelf.h"
#include "BooksBook.h"
#include "HarbourDebug.h"
BooksCoverModel::BooksCoverModel(QObject* aParent) :
QSortFilterProxyModel(aParent)
{
connect(this, SIGNAL(sourceModelChanged()), SIGNAL(sourceChanged()));
}
bool BooksCoverModel::filterAcceptsRow(int aRow, const QModelIndex& aParent) const
{
BooksShelf* shelf = qobject_cast<BooksShelf*>(sourceModel());
if (shelf) {
BooksBook* book = qobject_cast<BooksBook*>(shelf->get(aRow));
if (book) {
return book->hasCoverImage();
}
}
return true;
}
int BooksCoverModel::count() const
{
return rowCount(QModelIndex());
}
void BooksCoverModel::setSource(QObject* aSrc)
{
QAbstractItemModel* oldM = sourceModel();
if (aSrc != oldM) {
const int oldCount = count();
if (oldM) {
BooksShelf* oldShelf = qobject_cast<BooksShelf*>(oldM);
if (oldShelf) {
const int n = oldShelf->count();
for (int i=0; i<n; i++) {
onBookRemoved(oldShelf->bookAt(i));
}
}
oldM->disconnect(this);
}
if (aSrc) {
QAbstractItemModel* newM = qobject_cast<QAbstractItemModel*>(aSrc);
if (newM) {
setSourceModel(newM);
BooksShelf* newShelf = qobject_cast<BooksShelf*>(newM);
if (newShelf) {
const int n = newShelf->count();
for (int i=0; i<n; i++) {
onBookAdded(newShelf->bookAt(i));
}
connect(newShelf,
SIGNAL(bookAdded(BooksBook*)),
SLOT(onBookAdded(BooksBook*)));
connect(newShelf,
SIGNAL(bookRemoved(BooksBook*)),
SLOT(onBookRemoved(BooksBook*)));
}
connect(newM,
SIGNAL(rowsInserted(QModelIndex,int,int)),
SIGNAL(countChanged()));
connect(newM,
SIGNAL(rowsRemoved(QModelIndex,int,int)),
SIGNAL(countChanged()));
connect(newM,
SIGNAL(modelReset()),
SIGNAL(countChanged()));
} else {
HDEBUG("unexpected source" << aSrc);
setSourceModel(NULL);
}
} else {
setSourceModel(NULL);
}
if (oldCount != count()) {
Q_EMIT countChanged();
}
}
}
void BooksCoverModel::onCoverImageChanged()
{
beginResetModel();
endResetModel();
Q_EMIT countChanged();
}
void BooksCoverModel::onBookAdded(BooksBook* aBook)
{
if (aBook) {
connect(aBook,
SIGNAL(coverImageChanged()),
SLOT(onCoverImageChanged()));
}
}
void BooksCoverModel::onBookRemoved(BooksBook* aBook)
{
if (aBook) {
aBook->disconnect(this);
}
}

67
app/src/BooksCoverModel.h Normal file
View file

@ -0,0 +1,67 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_COVER_MODEL_H
#define BOOKS_COVER_MODEL_H
#include "BooksBook.h"
#include <QSortFilterProxyModel>
#include <QtQml>
class BooksCoverModel: public QSortFilterProxyModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QObject* source READ sourceModel WRITE setSource NOTIFY sourceChanged)
public:
explicit BooksCoverModel(QObject* aParent = NULL);
int count() const;
void setSource(QObject* aSource);
bool filterAcceptsRow(int aRow, const QModelIndex& aParent) const;
Q_SIGNALS:
void countChanged();
void sourceChanged();
private Q_SLOTS:
void onCoverImageChanged();
void onBookAdded(BooksBook* aBook);
void onBookRemoved(BooksBook* aBook);
};
QML_DECLARE_TYPE(BooksCoverModel)
#endif // BOOKS_COVER_MODEL_H

View file

@ -0,0 +1,463 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksCoverWidget.h"
#include "HarbourDebug.h"
#include "ZLibrary.h"
#include <QPainter>
// ==========================================================================
// BooksCoverWidget::ScaleTask
// ==========================================================================
class BooksCoverWidget::ScaleTask : public BooksTask
{
public:
ScaleTask(QImage aImage, int aWidth, int aHeight, bool aStretch);
static QImage scale(QImage aImage, int aWidth, int aHeight, bool aStretch);
void performTask();
public:
QImage iImage;
QImage iScaledImage;
int iWidth;
int iHeight;
bool iStretch;
};
BooksCoverWidget::ScaleTask::ScaleTask(QImage aImage, int aWidth, int aHeight,
bool aStretch) :
iImage(aImage),
iWidth(aWidth),
iHeight(aHeight),
iStretch(aStretch)
{
}
QImage BooksCoverWidget::ScaleTask::scale(QImage aImage,
int aWidth, int aHeight, bool aStretch)
{
if (aStretch){
return aImage.scaled(aWidth, aHeight, Qt::KeepAspectRatioByExpanding,
Qt::SmoothTransformation);
} else {
return (aWidth*aImage.height() > aImage.width()*aHeight) ?
aImage.scaledToHeight(aHeight, Qt::SmoothTransformation) :
aImage.scaledToWidth(aWidth, Qt::SmoothTransformation);
}
}
void BooksCoverWidget::ScaleTask::performTask()
{
if (!iImage.isNull() && !isCanceled()) {
iScaledImage = scale(iImage, iWidth, iHeight, iStretch);
}
}
// ==========================================================================
// BooksCoverWidget::DefaultImage
// ==========================================================================
// Image shared by all items in the bookshelf view
class BooksCoverWidget::DefaultImage
{
public:
static QImage scaled(int aWidth, int aHeight);
static QImage* retain();
static void release(QImage* aImage);
private:
static const char* iImageName;
static QImage* iImage;
static QImage* iScaledImage;
static int iRefCount;
static bool iMissing;
};
const char* BooksCoverWidget::DefaultImage::iImageName = "default-cover.jpg";
QImage* BooksCoverWidget::DefaultImage::iImage = NULL;
QImage* BooksCoverWidget::DefaultImage::iScaledImage = NULL;
int BooksCoverWidget::DefaultImage::iRefCount = 0;
bool BooksCoverWidget::DefaultImage::iMissing = false;
QImage* BooksCoverWidget::DefaultImage::retain()
{
if (!iImage && !iMissing) {
QString path(QString::fromStdString(
ZLibrary::DefaultFilesPathPrefix() + iImageName));
iImage = new QImage(path);
if (iImage->isNull() || !iImage->width() || !iImage->height()) {
HWARN("Failed to load" << qPrintable(path));
delete iImage;
iImage = NULL;
iMissing = true;
} else {
HDEBUG("loaded" << qPrintable(path));
}
}
if (iImage) {
iRefCount++;
}
return iImage;
}
QImage BooksCoverWidget::DefaultImage::scaled(int aWidth, int aHeight)
{
QImage scaled;
HASSERT(iImage);
if (iImage) {
const int iw = iImage->width();
const int ih = iImage->height();
if (aWidth*ih > iw*aHeight) {
// Scaling to height
if (iScaledImage && iScaledImage->height() != aHeight) {
delete iScaledImage;
iScaledImage = NULL;
}
if (iScaledImage) {
scaled = *iScaledImage;
} else {
HDEBUG("scaling to height" << aHeight);
scaled = iImage->scaledToHeight(aHeight,
Qt::SmoothTransformation);
iScaledImage = new QImage(scaled);
}
} else {
// Scaling to width
if (iScaledImage && iScaledImage->width() != aWidth) {
delete iScaledImage;
iScaledImage = NULL;
}
if (iScaledImage) {
scaled = *iScaledImage;
} else {
HDEBUG("scaling to width" << aHeight);
scaled = iImage->scaledToWidth(aWidth,
Qt::SmoothTransformation);
iScaledImage = new QImage(scaled);
}
}
}
return scaled;
}
void BooksCoverWidget::DefaultImage::release(QImage* aImage)
{
if (aImage) {
HASSERT(aImage == iImage);
if (!(--iRefCount)) {
HDEBUG("deleting cached image");
if (iImage) {
delete iImage;
iImage = NULL;
}
if (iScaledImage) {
delete iScaledImage;
iScaledImage = NULL;
}
}
}
}
// ==========================================================================
// BooksViewWidget
// ==========================================================================
BooksCoverWidget::BooksCoverWidget(QQuickItem* aParent) :
QQuickPaintedItem(aParent),
iTaskQueue(BooksTaskQueue::instance()),
iScaleTask(NULL),
iBook(NULL),
iDefaultImage(NULL),
iBorderWidth(0),
iBorderRadius(0),
iBorderColor(Qt::transparent),
iStretch(false),
iSynchronous(false)
{
setFlag(ItemHasContents, true);
setFillColor(Qt::transparent);
connect(this, SIGNAL(widthChanged()), SLOT(onSizeChanged()));
connect(this, SIGNAL(heightChanged()), SLOT(onSizeChanged()));
}
BooksCoverWidget::~BooksCoverWidget()
{
HDEBUG(iTitle);
DefaultImage::release(iDefaultImage);
if (iScaleTask) iScaleTask->release(this);
if (iBook) iBook->release();
}
void BooksCoverWidget::setBook(BooksBook* aBook)
{
if (iBook != aBook) {
const bool wasEmpty(empty());
const bool wasLoading = loading();
if (iBook) {
iBook->disconnect(this);
iBook->release();
}
if (aBook) {
(iBook = aBook)->retain();
iBook->requestCoverImage();
iBookRef = iBook->bookRef();
iCoverImage = iBook->coverImage();
iTitle = iBook->title();
connect(iBook,
SIGNAL(loadingCoverChanged()),
SIGNAL(loadingChanged()));
connect(iBook,
SIGNAL(coverImageChanged()),
SLOT(onCoverImageChanged()));
HDEBUG(iTitle);
} else {
iBook = NULL;
iBookRef.reset();
iCoverImage = QImage();
iTitle.clear();
HDEBUG("<none>");
}
scaleImage(wasEmpty);
Q_EMIT bookChanged();
if (wasLoading != loading()) {
Q_EMIT loadingChanged();
}
}
}
void BooksCoverWidget::onCoverImageChanged()
{
HDEBUG(iTitle);
const bool wasEmpty(empty());
iCoverImage = iBook->coverImage();
scaleImage(wasEmpty);
}
void BooksCoverWidget::setStretch(bool aValue)
{
if (iStretch != aValue) {
iStretch = aValue;
HDEBUG(aValue);
scaleImage();
Q_EMIT stretchChanged();
}
}
void BooksCoverWidget::setSynchronous(bool aValue)
{
if (iSynchronous != aValue) {
iSynchronous = aValue;
HDEBUG(aValue);
Q_EMIT synchronousChanged();
}
}
void BooksCoverWidget::setBorderWidth(qreal aWidth)
{
if (iBorderWidth != aWidth && aWidth >= 0) {
iBorderWidth = aWidth;
HDEBUG(iBorderWidth);
update();
Q_EMIT borderWidthChanged();
}
}
void BooksCoverWidget::setBorderRadius(qreal aRadius)
{
if (iBorderRadius != aRadius && aRadius >= 0) {
iBorderRadius = aRadius;
HDEBUG(iBorderRadius);
update();
Q_EMIT borderRadiusChanged();
}
}
void BooksCoverWidget::setBorderColor(QColor aColor)
{
if (iBorderColor != aColor) {
iBorderColor = aColor;
HDEBUG(iBorderColor);
update();
Q_EMIT borderColorChanged();
}
}
void BooksCoverWidget::setDefaultCover(QUrl aUrl)
{
if (iDefaultCover != aUrl) {
iDefaultCover = aUrl;
HDEBUG(iDefaultCover);
scaleImage();
Q_EMIT defaultCoverChanged();
}
}
void BooksCoverWidget::onSizeChanged()
{
scaleImage();
}
bool BooksCoverWidget::empty() const
{
return !iBook || !iBook->hasCoverImage() ||
iScaleTask || iScaledImage.isNull();
}
bool BooksCoverWidget::loading() const
{
return iBook && iBook->loadingCover();
}
void BooksCoverWidget::scaleImage(bool aWasEmpty)
{
const int w = width();
const int h = height();
iScaledImage = QImage();
if (iScaleTask) {
iScaleTask->release(this);
iScaleTask = NULL;
}
if (w > 0 && h > 0) {
if ((!iBook || !iBook->hasCoverImage()) && iDefaultCover.isValid()) {
QString path(iDefaultCover.toLocalFile());
if (!iCoverImage.load(path)) {
HWARN("Failed to load" << qPrintable(path));
}
}
if (iCoverImage.isNull()) {
if (!iDefaultImage) iDefaultImage = DefaultImage::retain();
if (iDefaultImage) iCoverImage = *iDefaultImage;
}
if (!iCoverImage.isNull()) {
if (iSynchronous) {
iScaledImage = ScaleTask::scale(iCoverImage, w, h, iStretch);
update();
} else {
iScaledImage = QImage();
iScaleTask = new ScaleTask(iCoverImage, w, h, iStretch);
connect(iScaleTask, SIGNAL(done()), SLOT(onScaleTaskDone()));
iTaskQueue->submit(iScaleTask);
}
} else {
update();
}
}
if (aWasEmpty != empty()) {
Q_EMIT emptyChanged();
}
}
void BooksCoverWidget::onScaleTaskDone()
{
const bool wasEmpty(empty());
HASSERT(iScaleTask == sender());
iScaledImage = iScaleTask->iScaledImage;
iScaleTask->release(this);
iScaleTask = NULL;
update();
if (wasEmpty != empty()) {
Q_EMIT emptyChanged();
}
}
void BooksCoverWidget::paint(QPainter* aPainter)
{
const qreal w = width();
const qreal h = height();
if (w > 0 && h > 0) {
qreal sw, sh;
if (!iScaledImage.isNull()) {
sw = iScaledImage.width();
sh = iScaledImage.height();
} else {
sw = w;
sh = h;
}
const int x = (w - sw)/2;
const int y = h - sh;
QPainterPath path;
qreal w1, h1, x1, y1;
if (iBorderRadius > 0) {
// The border rectangle is no less that 3*radius
// and no more than the size of the item.
const qreal d = 2*iBorderRadius;
w1 = qMin(w, qMax(sw, 2*d)) - iBorderWidth;
h1 = qMin(h, qMax(sh, 3*d)) - iBorderWidth;
x1 = (w - w1)/2;
y1 = h - h1 - iBorderWidth/2;
const qreal x2 = x1 + w1 - d;
const qreal y2 = y1 + h1 - d;
path.arcMoveTo(x1, y1, d, d, 180);
path.arcTo(x1, y1, d, d, 180, -90);
path.arcTo(x2, y1, d, d, 90, -90);
path.arcTo(x2, y2, d, d, 0, -90);
path.arcTo(x1, y2, d, d, 270, -90);
path.closeSubpath();
aPainter->setClipPath(path);
} else {
w1 = sw - iBorderWidth;
h1 = sh - iBorderWidth;
x1 = (w - w1)/2;
y1 = h - h1 - iBorderWidth/2;
}
if (!iScaledImage.isNull()) {
aPainter->drawImage(x, y, iScaledImage);
}
if (iBorderColor.alpha() && iBorderWidth > 0) {
aPainter->setRenderHint(QPainter::Antialiasing);
aPainter->setRenderHint(QPainter::HighQualityAntialiasing);
aPainter->setBrush(Qt::NoBrush);
aPainter->setPen(QPen(iBorderColor, iBorderWidth));
if (iBorderRadius > 0) {
aPainter->setClipping(false);
aPainter->drawPath(path);
} else {
aPainter->drawRect(x1, y1, w1, h1);
}
}
}
}

129
app/src/BooksCoverWidget.h Normal file
View file

@ -0,0 +1,129 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_COVER_WIDGET_H
#define BOOKS_COVER_WIDGET_H
#include "BooksTypes.h"
#include "BooksTask.h"
#include "BooksTaskQueue.h"
#include "BooksBook.h"
#include "ZLImage.h"
#include "image/ZLQtImageManager.h"
#include <QImage>
#include <QQuickPaintedItem>
class BooksCoverWidget: public QQuickPaintedItem
{
Q_OBJECT
Q_PROPERTY(bool empty READ empty NOTIFY emptyChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(bool stretch READ stretch WRITE setStretch NOTIFY stretchChanged)
Q_PROPERTY(bool synchronous READ synchronous WRITE setSynchronous NOTIFY synchronousChanged)
Q_PROPERTY(qreal borderWidth READ borderWidth WRITE setBorderWidth NOTIFY borderWidthChanged)
Q_PROPERTY(qreal borderRadius READ borderRadius WRITE setBorderRadius NOTIFY borderRadiusChanged)
Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor NOTIFY borderColorChanged)
Q_PROPERTY(QUrl defaultCover READ defaultCover WRITE setDefaultCover NOTIFY defaultCoverChanged)
Q_PROPERTY(BooksBook* book READ book WRITE setBook NOTIFY bookChanged)
public:
BooksCoverWidget(QQuickItem* aParent = NULL);
~BooksCoverWidget();
bool empty() const;
bool loading() const;
qreal borderWidth() const { return iBorderRadius; }
void setBorderWidth(qreal aWidth);
qreal borderRadius() const { return iBorderRadius; }
void setBorderRadius(qreal aRadius);
QColor borderColor() const { return iBorderColor; }
void setBorderColor(QColor aColor);
QUrl defaultCover() const { return iDefaultCover; }
void setDefaultCover(QUrl aUrl);
BooksBook* book() const { return iBook; }
void setBook(BooksBook* aBook);
bool stretch() const { return iStretch; }
void setStretch(bool aValue);
bool synchronous() const { return iSynchronous; }
void setSynchronous(bool aValue);
Q_SIGNALS:
void bookChanged();
void emptyChanged();
void loadingChanged();
void stretchChanged();
void synchronousChanged();
void borderWidthChanged();
void borderRadiusChanged();
void borderColorChanged();
void defaultCoverChanged();
private Q_SLOTS:
void onCoverImageChanged();
void onSizeChanged();
void onScaleTaskDone();
private:
void paint(QPainter *painter);
void scaleImage(bool aWasEmpty);
void scaleImage() { scaleImage(empty()); }
private:
class ScaleTask;
class DefaultImage;
shared_ptr<BooksTaskQueue> iTaskQueue;
ScaleTask* iScaleTask;
QImage iScaledImage;
QImage iCoverImage;
shared_ptr<Book> iBookRef;
BooksBook* iBook;
QImage* iDefaultImage;
qreal iBorderWidth;
qreal iBorderRadius;
QColor iBorderColor;
QUrl iDefaultCover;
QString iTitle;
bool iStretch;
bool iSynchronous;
};
#endif // BOOKS_COVER_WIDGET_H

65
app/src/BooksDefs.h Normal file
View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_DEFS_H
#define BOOKS_DEFS_H
#include <QString>
#define BOOKS_APP_NAME "harbour-books"
#define BOOKS_DATA_ROOT "usr/share/" BOOKS_APP_NAME
#define BOOKS_QML_DIR BOOKS_DATA_ROOT "/qml"
#define BOOKS_ICONS_DIR BOOKS_DATA_ROOT "/icons"
#define BOOKS_DATA_DIR BOOKS_DATA_ROOT "/data"
#define BOOKS_QML_FILE BOOKS_QML_DIR "/BooksMain.qml"
#define BOOKS_ROOT_SHELF_DIR "Books"
#define BOOKS_QML_PLUGIN "harbour.books"
#define BOOKS_QML_PLUGIN_V1 1
#define BOOKS_QML_PLUGIN_V2 0
#define BOOKS_QML_REGISTER(klass,name) \
qmlRegisterType<klass>(BOOKS_QML_PLUGIN, BOOKS_QML_PLUGIN_V1, \
BOOKS_QML_PLUGIN_V2, name)
#define BOOKS_STATE_FILE_SUFFIX ".state"
#if defined(__i386__)
# define BOOKS_PPI (330) // Tablet 1536x2048
#elif defined(__arm__)
# define BOOKS_PPI (245) // Jolla1 540x960
#else
# error Unexpected architechture
#endif
#endif // BOOKS_DEFS_H

View file

@ -0,0 +1,142 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksDialogManager.h"
#include "HarbourDebug.h"
#include "ZLDialog.h"
#include "ZLOptionsDialog.h"
#include "ZLProgressDialog.h"
#include "image/ZLQtImageManager.h"
#include <QGuiApplication>
#include <QClipboard>
void BooksDialogManager::createApplicationWindow(ZLApplication* aApp) const
{
HDEBUG("THIS IS NOT SUPPOSED TO HAPPEN!");
}
shared_ptr<ZLDialog>
BooksDialogManager::createDialog(
const ZLResourceKey& aKey) const
{
HDEBUG(aKey.Name.c_str());
return NULL;
}
shared_ptr<ZLOptionsDialog>
BooksDialogManager::createOptionsDialog(
const ZLResourceKey& aKey,
shared_ptr<ZLRunnable> aApplyAction,
bool aApplyButton) const
{
HDEBUG(aKey.Name.c_str());
return NULL;
}
shared_ptr<ZLOpenFileDialog>
BooksDialogManager::createOpenFileDialog(
const ZLResourceKey& aKey,
const std::string& aDirPath,
const std::string& aFilePath,
const ZLOpenFileDialog::Filter& aFilter) const
{
HDEBUG(aKey.Name.c_str());
return NULL;
}
void
BooksDialogManager::informationBox(
const std::string& title,
const std::string& message) const
{
HDEBUG(QString::fromStdString(title) << message.c_str());
}
void
BooksDialogManager::errorBox(
const ZLResourceKey& key,
const std::string& message) const
{
HDEBUG(QString::fromStdString(key.Name) << message.c_str());
}
int
BooksDialogManager::questionBox(
const ZLResourceKey& key,
const std::string& message,
const ZLResourceKey& button0,
const ZLResourceKey& button1,
const ZLResourceKey& button2) const
{
HDEBUG(QString::fromStdString(key.Name) << message.c_str());
return -1;
}
shared_ptr<ZLProgressDialog>
BooksDialogManager::createProgressDialog(
const ZLResourceKey& aKey) const
{
HDEBUG(aKey.Name.c_str());
return NULL;
}
bool
BooksDialogManager::isClipboardSupported(
ClipboardType aType) const
{
return true;
}
void
BooksDialogManager::setClipboardText(
const std::string& text,
ClipboardType type) const
{
if (!text.empty()) {
QGuiApplication::clipboard()->setText(QString::fromStdString(text),
(type == CLIPBOARD_MAIN) ? QClipboard::Clipboard : QClipboard::Selection);
}
}
void
BooksDialogManager::setClipboardImage(
const ZLImageData &imageData,
ClipboardType type) const
{
QGuiApplication::clipboard()->setImage(
*static_cast<const ZLQtImageData&>(imageData).image(),
(type == CLIPBOARD_MAIN) ? QClipboard::Clipboard : QClipboard::Selection);
}

View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_DIALOG_MANAGER_H
#define BOOKS_DIALOG_MANAGER_H
#include <ZLDialogManager.h>
class BooksDialogManager: public ZLDialogManager {
public:
static void createInstance() { ourInstance = new BooksDialogManager(); }
private:
BooksDialogManager() {}
public:
static const ZLResource& resource(const ZLResourceKey& aKey)
{ return ZLDialogManager::resource()[aKey]; }
void createApplicationWindow(ZLApplication *application) const;
shared_ptr<ZLDialog> createDialog(const ZLResourceKey &key) const;
shared_ptr<ZLOptionsDialog> createOptionsDialog(const ZLResourceKey &key, shared_ptr<ZLRunnable> applyAction, bool showApplyButton) const;
shared_ptr<ZLOpenFileDialog> createOpenFileDialog(const ZLResourceKey &key, const std::string &directoryPath, const std::string &filePath, const ZLOpenFileDialog::Filter &filter) const;
void informationBox(const std::string &title, const std::string &message) const;
void errorBox(const ZLResourceKey &key, const std::string &message) const;
int questionBox(const ZLResourceKey &key, const std::string &message, const ZLResourceKey &button0, const ZLResourceKey &button1, const ZLResourceKey &button2) const;
shared_ptr<ZLProgressDialog> createProgressDialog(const ZLResourceKey &key) const;
bool isClipboardSupported(ClipboardType type) const;
void setClipboardText(const std::string &text, ClipboardType type) const;
void setClipboardImage(const ZLImageData &imageData, ClipboardType type) const;
};
#endif /* BOOKS_DIALOG_MANAGER_H */

View file

@ -0,0 +1,403 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksImportModel.h"
#include "BooksStorage.h"
#include "BooksTask.h"
#include "HarbourDebug.h"
#include <QDir>
#include <QCryptographicHash>
enum BooksImportRole {
BooksImportRoleTitle = Qt::UserRole,
BooksImportRoleBook,
BooksImportRolePath,
BooksImportRoleFileName,
BooksImportRoleSelected
};
// ==========================================================================
// BooksImportModel::Data
// ==========================================================================
class BooksImportModel::Data {
public:
Data(BooksBook* iBook);
~Data();
QString title() { return iBook->title(); }
QString path() { return iBook->path(); }
QString fileName() { return iBook->fileName(); }
public:
BooksBook* iBook;
bool iSelected;
};
BooksImportModel::Data::Data(BooksBook* aBook) :
iBook(aBook),
iSelected(false)
{
iBook->retain();
}
BooksImportModel::Data::~Data()
{
iBook->release();
}
// ==========================================================================
// BooksImportModel::Task
// ==========================================================================
class BooksImportModel::Task : public BooksTask
{
Q_OBJECT
public:
Task(QString aDest) : iDestDir(aDest), iBufSize(0x1000), iBuf(NULL) {}
~Task();
void performTask();
void scanDir(QDir aDir);
bool isDuplicate(QString aPath, QFileInfoList aFileList);
QByteArray calculateFileHash(QString aPath);
QByteArray getFileHash(QString aPath);
Q_SIGNALS:
void bookFound(BooksBook* aBook) const;
public:
QList<BooksBook*> iBooks;
QHash<QString,QByteArray> iFileHash;
QHash<QByteArray,QString> iHashFile;
QFileInfoList iDestFiles;
QFileInfoList iSrcFiles;
QString iDestDir;
qint64 iBufSize;
char* iBuf;
};
BooksImportModel::Task::~Task()
{
const int n = iBooks.count();
for (int i=0; i<n; i++) iBooks.at(i)->release();
delete [] iBuf;
}
QByteArray BooksImportModel::Task::calculateFileHash(QString aPath)
{
QByteArray result;
QFile file(aPath);
if (file.open(QIODevice::ReadOnly)) {
qint64 len;
QCryptographicHash hash(QCryptographicHash::Md5);
hash.reset();
if (!iBuf) iBuf = new char[iBufSize];
while (!isCanceled() && (len = file.read(iBuf, iBufSize)) > 0) {
hash.addData(iBuf, len);
}
if (len == 0) {
if (!isCanceled()) {
result = hash.result();
HDEBUG(qPrintable(aPath) << QString(result.toHex()));
}
} else {
HWARN("error reading" << qPrintable(aPath));
}
file.close();
}
return result;
}
QByteArray BooksImportModel::Task::getFileHash(QString aPath)
{
if (iFileHash.contains(aPath)) {
return iFileHash.value(aPath);
} else {
QByteArray hash = calculateFileHash(aPath);
iFileHash.insert(aPath, hash);
iHashFile.insert(hash, aPath);
return hash;
}
}
bool BooksImportModel::Task::isDuplicate(QString aPath, QFileInfoList aList)
{
const int n = aList.count();
if (n > 0) {
QFileInfo file(aPath);
QByteArray fileHash;
for (int i=0; i<n && !isCanceled(); i++) {
QFileInfo other = aList.at(i);
if (other.size() == file.size()) {
QByteArray otherHash(getFileHash(other.filePath()));
if (!otherHash.isEmpty() && !isCanceled()) {
if (fileHash.isEmpty()) fileHash = getFileHash(aPath);
if (fileHash == otherHash) {
HDEBUG(qPrintable(aPath) << "and" <<
qPrintable(other.filePath()) <<
"are identical");
return true;
}
}
}
}
}
return false;
}
void BooksImportModel::Task::performTask()
{
if (!isCanceled()) {
if (!iDestDir.isEmpty()) {
iDestFiles = QDir(iDestDir).entryInfoList(QDir::Files);
}
scanDir(QDir(QDir::homePath() + "/Downloads"));
}
}
void BooksImportModel::Task::scanDir(QDir aDir)
{
// Files first
if (!isCanceled()) {
HDEBUG("checking" << aDir.canonicalPath());
QFileInfoList fileList = aDir.entryInfoList(QDir::Files |
QDir::Readable, QDir::Time);
const int n = fileList.count();
for (int i=0; i<n && !isCanceled(); i++) {
QFileInfo fileInfo(fileList.at(i));
QString filePath(fileInfo.canonicalFilePath());
std::string path(filePath.toStdString());
ZLFile file(path);
shared_ptr<Book> book = Book::loadFromFile(file);
if (!book.isNull()) {
if (!isDuplicate(filePath, iDestFiles) &&
!isDuplicate(filePath, iSrcFiles)) {
BooksBook* newBook = new BooksBook(BooksStorage(), book);
newBook->moveToThread(thread());
iBooks.append(newBook);
iSrcFiles.append(fileInfo);
HDEBUG("found" << path.c_str() << newBook->title());
Q_EMIT bookFound(newBook);
}
} else {
HDEBUG("not a book:" << path.c_str());
}
}
}
// Then directories
if (!isCanceled()) {
QFileInfoList dirList = aDir.entryInfoList(QDir::Dirs |
QDir::NoDotAndDotDot | QDir::Readable, QDir::Time);
const int n = dirList.count();
for (int i=0; i<n && !isCanceled(); i++) {
QString dirPath(dirList.at(i).canonicalFilePath());
HDEBUG(dirPath);
if (!dirPath.isEmpty()) {
scanDir(QDir(dirPath));
}
}
}
}
// ==========================================================================
// BooksImportModel
// ==========================================================================
BooksImportModel::BooksImportModel(QObject* aParent) :
QAbstractListModel(aParent),
iSelectedCount(0),
iAutoRefresh(false),
iTaskQueue(BooksTaskQueue::instance()),
iTask(NULL)
{
iSelectedRole.append(BooksImportRoleSelected);
HDEBUG("created");
#if QT_VERSION < 0x050000
setRoleNames(roleNames());
#endif
}
BooksImportModel::~BooksImportModel()
{
HDEBUG("destroyed");
qDeleteAll(iList);
if (iTask) iTask->release(this);
}
void BooksImportModel::setDestination(QString aDestination)
{
if (iDestination != aDestination) {
iDestination = aDestination;
HDEBUG(aDestination);
Q_EMIT destinationChanged();
if (iAutoRefresh) {
if (iTask) {
iTask->release(this);
iTask = NULL;
}
refresh();
}
}
}
void BooksImportModel::refresh()
{
iAutoRefresh = true;
if (!iTask) {
HDEBUG("refreshing the model");
if (!iList.isEmpty()) {
beginResetModel();
qDeleteAll(iList);
iList.clear();
endInsertRows();
Q_EMIT countChanged();
}
iTask = new Task(iDestination);
connect(iTask, SIGNAL(bookFound(BooksBook*)),
SLOT(onBookFound(BooksBook*)), Qt::QueuedConnection);
connect(iTask, SIGNAL(done()), SLOT(onTaskDone()));
iTaskQueue->submit(iTask);
Q_EMIT busyChanged();
}
}
void BooksImportModel::setSelected(int aIndex, bool aSelected)
{
if (validIndex(aIndex)) {
Data* data = iList.at(aIndex);
if (data->iSelected != aSelected) {
HDEBUG(data->path() << aSelected);
if (data->iSelected) iSelectedCount--;
if (aSelected) iSelectedCount++;
data->iSelected = aSelected;
QModelIndex index(createIndex(aIndex, 0));
Q_EMIT dataChanged(index, index, iSelectedRole);
Q_EMIT selectedCountChanged();
}
}
}
QObject* BooksImportModel::selectedBook(int aIndex)
{
const int n = iList.count();
for (int i=0, k=0; i<n; i++) {
Data* data = iList.at(i);
if (data->iSelected) {
if (k == aIndex) {
return data->iBook;
}
k++;
}
}
return NULL;
}
void BooksImportModel::onBookFound(BooksBook* aBook)
{
if (iTask) {
// When we find the first book, we add two items. The second item
// is the "virtual" that will stay at the end of the list and will
// be removed by onTaskDone() after scanning is finished. The idea
// is to show the busy indicator at the end of the list (that's how
// QML represents the dummy item) while we keep on scanning.
const int n1 = iList.count();
beginInsertRows(QModelIndex(), n1, n1 ? n1 : 1);
iList.append(new Data(aBook));
endInsertRows();
Q_EMIT countChanged();
}
}
void BooksImportModel::onTaskDone()
{
HASSERT(iTask);
HASSERT(iTask == sender());
iTask->release(this);
iTask = NULL;
if (iList.count() > 0) {
// Remove the "virtual" item at the end of the list
beginRemoveRows(QModelIndex(),iList.count(), iList.count());
endRemoveRows();
}
Q_EMIT busyChanged();
}
QHash<int,QByteArray> BooksImportModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles.insert(BooksImportRoleTitle, "title");
roles.insert(BooksImportRoleBook, "book");
roles.insert(BooksImportRolePath, "path");
roles.insert(BooksImportRoleFileName, "fileName");
roles.insert(BooksImportRoleSelected, "selected");
return roles;
}
int BooksImportModel::rowCount(const QModelIndex&) const
{
return iTask ? (iList.count() + 1) : iList.count();
}
QVariant BooksImportModel::data(const QModelIndex& aIndex, int aRole) const
{
const int i = aIndex.row();
if (validIndex(i)) {
Data* data = iList.at(i);
switch (aRole) {
case BooksImportRoleTitle: return data->title();
case BooksImportRoleBook: return QVariant::fromValue(data->iBook);
case BooksImportRolePath: return data->path();
case BooksImportRoleFileName: return data->fileName();
case BooksImportRoleSelected: return data->iSelected;
}
} else if (i == iList.count()) {
switch (aRole) {
case BooksImportRoleTitle:
case BooksImportRolePath:
case BooksImportRoleFileName: return QString();
case BooksImportRoleBook: return QVariant::fromValue((QObject*)NULL);
case BooksImportRoleSelected: return false;
}
}
return QVariant();
}
#include "BooksImportModel.moc"

103
app/src/BooksImportModel.h Normal file
View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_IMPORT_MODEL_H
#define BOOKS_IMPORT_MODEL_H
#include "BooksBook.h"
#include "BooksTaskQueue.h"
#include <QHash>
#include <QVariant>
#include <QByteArray>
#include <QAbstractListModel>
#include <QtQml>
class BooksImportModel: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(bool busy READ busy NOTIFY busyChanged)
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(int selectedCount READ selectedCount NOTIFY selectedCountChanged)
Q_PROPERTY(QString destination READ destination WRITE setDestination NOTIFY destinationChanged)
public:
explicit BooksImportModel(QObject* aParent = NULL);
~BooksImportModel();
bool busy() const { return iTask != NULL; }
int count() const { return iList.count(); }
int selectedCount() const { return iSelectedCount; }
QString destination() const { return iDestination; }
void setDestination(QString aDestination);
Q_INVOKABLE void refresh();
Q_INVOKABLE void setSelected(int aIndex, bool aSelected);
Q_INVOKABLE QObject* selectedBook(int aIndex);
// QAbstractListModel
virtual QHash<int,QByteArray> roleNames() const;
virtual int rowCount(const QModelIndex& aParent) const;
virtual QVariant data(const QModelIndex& aIndex, int aRole) const;
Q_SIGNALS:
void countChanged();
void busyChanged();
void selectedCountChanged();
void destinationChanged();
private Q_SLOTS:
void onBookFound(BooksBook* aBook);
void onTaskDone();
private:
bool validIndex(int aIndex) const;
private:
class Task;
class Data;
QString iDestination;
QList<Data*> iList;
QVector<int> iSelectedRole;
int iSelectedCount;
bool iAutoRefresh;
shared_ptr<BooksTaskQueue> iTaskQueue;
Task* iTask;
};
QML_DECLARE_TYPE(BooksImportModel)
inline bool BooksImportModel::validIndex(int aIndex) const
{ return aIndex >= 0 && aIndex < iList.count(); }
#endif // BOOKS_IMPORT_MODEL_H

57
app/src/BooksItem.h Normal file
View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_ITEM_H
#define BOOKS_ITEM_H
#include "BooksTypes.h"
#include <QString>
#include <QObject>
class BooksItem
{
public:
virtual ~BooksItem() {}
virtual BooksItem* retain() = 0;
virtual void release() = 0;
virtual QObject* object() = 0;
virtual BooksShelf* shelf() = 0;
virtual BooksBook* book() = 0;
virtual QString name() const = 0;
virtual QString fileName() const = 0;
};
#endif // BOOKS_ITEM_H

View file

@ -0,0 +1,200 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksListWatcher.h"
#include "HarbourDebug.h"
#define LISTVIEW_CONTENT_X "contentX"
#define LISTVIEW_CONTENT_Y "contentY"
#define LISTVIEW_INDEX_AT "indexAt"
BooksListWatcher::BooksListWatcher(QObject* aParent) :
QObject(aParent),
iCurrentIndex(0),
iContentX(0),
iContentY(0),
iListView(NULL),
iResizeTimer(new QTimer(this))
{
iResizeTimer->setSingleShot(true);
iResizeTimer->setInterval(0);
connect(iResizeTimer, SIGNAL(timeout()), SLOT(onResizeTimeout()));
}
void BooksListWatcher::setListView(QQuickItem* aView)
{
if (iListView != aView) {
const QSize oldSize(iSize);
if (iListView) iListView->disconnect(this);
iListView = aView;
if (iListView) {
connect(iListView,
SIGNAL(widthChanged()),
SLOT(onWidthChanged()));
connect(iListView,
SIGNAL(heightChanged()),
SLOT(onHeightChanged()));
connect(iListView,
SIGNAL(contentXChanged()),
SLOT(onContentXChanged()));
connect(iListView,
SIGNAL(contentYChanged()),
SLOT(onContentYChanged()));
connect(iListView,
SIGNAL(contentWidthChanged()),
SLOT(onContentSizeChanged()));
connect(iListView,
SIGNAL(contentHeightChanged()),
SLOT(onContentSizeChanged()));
iContentX = contentX();
iContentY = contentY();
updateCurrentIndex();
} else {
iContentX = iContentY = 0;
iSize = QSize(0,0);
}
Q_EMIT listViewChanged();
if (oldSize != iSize) {
Q_EMIT sizeChanged();
}
if (oldSize.width() != iSize.width()) {
Q_EMIT widthChanged();
}
if (oldSize.height() != iSize.height()) {
Q_EMIT heightChanged();
}
}
}
qreal BooksListWatcher::getRealProperty(const char *name, qreal defaultValue)
{
QVariant value = iListView->property(name);
bool ok = false;
if (value.isValid()) {
ok = false;
qreal r = value.toReal(&ok);
if (ok) return r;
}
return defaultValue;
}
qreal BooksListWatcher::contentX()
{
return getRealProperty(LISTVIEW_CONTENT_X);
}
qreal BooksListWatcher::contentY()
{
return getRealProperty(LISTVIEW_CONTENT_Y);
}
void BooksListWatcher::updateCurrentIndex()
{
int index = -1;
if (QMetaObject::invokeMethod(iListView, LISTVIEW_INDEX_AT,
Q_RETURN_ARG(int,index), Q_ARG(qreal,iContentX+width()/2),
Q_ARG(qreal,iContentY+height()/2))) {
if (iCurrentIndex != index) {
HDEBUG(index);
iCurrentIndex = index;
Q_EMIT currentIndexChanged();
}
}
}
void BooksListWatcher::updateSize()
{
const QSize size(iListView->width(), iListView->height());
HDEBUG(size);
if (iSize != size) {
const QSize oldSize(iSize);
iSize = size;
Q_EMIT sizeChanged();
if (oldSize.width() != iSize.width()) {
Q_EMIT widthChanged();
}
if (oldSize.height() != iSize.height()) {
Q_EMIT heightChanged();
}
}
}
void BooksListWatcher::onWidthChanged()
{
HASSERT(sender() == iListView);
HDEBUG(iListView->width());
// Width change will probably be followed by height change
iResizeTimer->start();
}
void BooksListWatcher::onHeightChanged()
{
HASSERT(sender() == iListView);
HDEBUG(iListView->height());
if (iResizeTimer->isActive()) {
// Height is usually changed after width
iResizeTimer->stop();
updateSize();
} else {
iResizeTimer->start();
}
}
void BooksListWatcher::onResizeTimeout()
{
// This can only happen if only width or height has changed. Normally,
// width change is followed by height change and view is reset from the
// setHeight() method
updateSize();
}
void BooksListWatcher::onContentXChanged()
{
HASSERT(sender() == iListView);
iContentX = contentX();
updateCurrentIndex();
}
void BooksListWatcher::onContentYChanged()
{
HASSERT(sender() == iListView);
iContentY = contentY();
updateCurrentIndex();
}
void BooksListWatcher::onContentSizeChanged()
{
HASSERT(sender() == iListView);
updateCurrentIndex();
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_LIST_WATCHER_H
#define BOOKS_LIST_WATCHER_H
#include "BooksTypes.h"
#include <QQuickItem>
#include <QtQml>
class BooksListWatcher: public QObject
{
Q_OBJECT
Q_PROPERTY(int currentIndex READ currentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(QQuickItem* listView READ listView WRITE setListView NOTIFY listViewChanged)
Q_PROPERTY(qreal width READ width NOTIFY widthChanged)
Q_PROPERTY(qreal height READ height NOTIFY heightChanged)
Q_PROPERTY(QSize size READ size NOTIFY sizeChanged)
public:
explicit BooksListWatcher(QObject* aParent = NULL);
int currentIndex() const { return iCurrentIndex; }
QSize size() const { return iSize; }
qreal width() const { return iSize.width(); }
qreal height() const { return iSize.height(); }
QQuickItem* listView() const { return iListView; }
void setListView(QQuickItem* aView);
private:
qreal contentX();
qreal contentY();
qreal getRealProperty(const char *name, qreal defaultValue = 0.0);
void updateCurrentIndex();
void updateSize();
private Q_SLOTS:
void onResizeTimeout();
void onWidthChanged();
void onHeightChanged();
void onContentXChanged();
void onContentYChanged();
void onContentSizeChanged();
Q_SIGNALS:
void listViewChanged();
void sizeChanged();
void widthChanged();
void heightChanged();
void currentIndexChanged();
private:
QSize iSize;
int iCurrentIndex;
qreal iContentX;
qreal iContentY;
QQuickItem* iListView;
QTimer* iResizeTimer;
};
QML_DECLARE_TYPE(BooksListWatcher)
#endif // BOOKS_LIST_WATCHER_H

View file

@ -0,0 +1,53 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksLoadingProperty.h"
#include "HarbourDebug.h"
void BooksLoadingProperty::suspendLoadingStateChanges()
{
if (!(iChangesBlocked++)) {
iSavedState = loading();
}
}
void BooksLoadingProperty::resumeLoadingStateChanges()
{
HASSERT(iChangesBlocked > 0);
if (!(--iChangesBlocked)) {
if (iSavedState != loading()) {
Q_EMIT loadingChanged();
}
}
}

View file

@ -0,0 +1,69 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_LOADING_PROPERTY_H
#define BOOKS_LOADING_PROPERTY_H
#include "BooksTypes.h"
class BooksLoadingSignalBlocker;
class BooksLoadingProperty
{
friend class BooksLoadingSignalBlocker;
public:
BooksLoadingProperty() : iChangesBlocked(0), iSavedState(false) {}
virtual bool loading() const = 0;
virtual void loadingChanged() = 0;
private:
void suspendLoadingStateChanges();
void resumeLoadingStateChanges();
int iChangesBlocked;
bool iSavedState;
};
class BooksLoadingSignalBlocker {
public:
BooksLoadingSignalBlocker(BooksLoadingProperty* aProperty) : iProperty(aProperty)
{ iProperty->suspendLoadingStateChanges(); }
~BooksLoadingSignalBlocker()
{ iProperty->resumeLoadingStateChanges(); }
private:
BooksLoadingProperty* iProperty;
};
#endif // BOOKS_LOADING_PROPERTY_H

474
app/src/BooksPageWidget.cpp Normal file
View file

@ -0,0 +1,474 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksPageWidget.h"
#include "BooksTextStyle.h"
#include "BooksDefs.h"
#include "HarbourDebug.h"
#include <QPainter>
// ==========================================================================
// BooksPageWidget::Data
// ==========================================================================
class BooksPageWidget::Data {
public:
Data(shared_ptr<ZLTextModel> aModel, int aWidth, int aHeight) :
iModel(aModel), iPaintContext(aWidth, aHeight) {}
bool paint(QPainter* aPainter);
public:
shared_ptr<ZLView> iView;
shared_ptr<ZLTextModel> iModel;
BooksPaintContext iPaintContext;
};
bool BooksPageWidget::Data::paint(QPainter* aPainter)
{
if (!iView.isNull()) {
iPaintContext.beginPaint(aPainter);
iView->paint();
iPaintContext.endPaint();
return true;
}
return false;
}
// ==========================================================================
// BooksPageWidget::ResetTask
// ==========================================================================
class BooksPageWidget::ResetTask : public BooksTask
{
public:
ResetTask(shared_ptr<ZLTextModel> aModel,
shared_ptr<ZLTextStyle> aTextStyle, int aWidth, int aHeight,
const BooksMargins& aMargins, const BooksPos& aPosition);
~ResetTask();
void performTask();
public:
BooksPageWidget::Data* iData;
shared_ptr<ZLTextStyle> iTextStyle;
BooksMargins iMargins;
BooksPos iPosition;
};
BooksPageWidget::ResetTask::ResetTask(shared_ptr<ZLTextModel> aModel,
shared_ptr<ZLTextStyle> aTextStyle, int aWidth, int aHeight,
const BooksMargins& aMargins, const BooksPos& aPosition) :
iData(new BooksPageWidget::Data(aModel, aWidth, aHeight)),
iTextStyle(aTextStyle),
iMargins(aMargins),
iPosition(aPosition)
{
}
BooksPageWidget::ResetTask::~ResetTask()
{
delete iData;
}
void BooksPageWidget::ResetTask::performTask()
{
if (!isCanceled()) {
BooksTextView* view = new BooksTextView(iData->iPaintContext,
iTextStyle, iMargins);
if (!isCanceled()) {
view->setModel(iData->iModel);
if (!isCanceled()) {
view->gotoPosition(iPosition);
if (!isCanceled()) {
iData->iView = view;
} else {
delete view;
}
}
}
}
}
// ==========================================================================
// BooksPageWidget::RenderTask
// ==========================================================================
class BooksPageWidget::RenderTask : public BooksTask {
public:
RenderTask(shared_ptr<BooksPageWidget::Data> aData, int aWidth, int aHeight) :
iData(aData), iWidth(aWidth), iHeight(aHeight), iImage(NULL) {}
void performTask();
public:
shared_ptr<BooksPageWidget::Data> iData;
int iWidth;
int iHeight;
shared_ptr<QImage> iImage;
};
void BooksPageWidget::RenderTask::performTask()
{
if (!isCanceled() && !iData.isNull() && !iData->iView.isNull() &&
iWidth > 0 && iHeight > 0) {
iImage = new QImage(iWidth, iHeight, QImage::Format_RGB32);
if (!isCanceled()) {
QPainter painter(&*iImage);
iData->paint(&painter);
}
}
}
// ==========================================================================
// BooksPageWidget
// ==========================================================================
BooksPageWidget::BooksPageWidget(QQuickItem* aParent) :
QQuickPaintedItem(aParent),
iTaskQueue(BooksTaskQueue::instance()),
iTextStyle(BooksTextStyle::defaults()),
iResizeTimer(new QTimer(this)),
iModel(NULL),
iSettings(NULL),
iResetTask(NULL),
iRenderTask(NULL),
iPaintRequest(false),
iEmpty(false),
iPage(-1)
{
setFlag(ItemHasContents, true);
setFillColor(Qt::white);
iResizeTimer->setSingleShot(true);
iResizeTimer->setInterval(0);
connect(iResizeTimer, SIGNAL(timeout()), SLOT(onResizeTimeout()));
connect(this, SIGNAL(widthChanged()), SLOT(onWidthChanged()));
connect(this, SIGNAL(heightChanged()), SLOT(onHeightChanged()));
}
BooksPageWidget::~BooksPageWidget()
{
HDEBUG("page" << iPage);
if (iResetTask) iResetTask->release(this);
if (iRenderTask) iRenderTask->release(this);
}
void BooksPageWidget::setModel(BooksBookModel* aModel)
{
if (iModel != aModel) {
if (iModel) {
iModel->disconnect(this);
iModel = NULL;
}
iModel = aModel;
if (iModel) {
#if HARBOUR_DEBUG
if (iPage >= 0) {
HDEBUG(iModel->title() << iPage);
} else {
HDEBUG(iModel->title());
}
#endif // HARBOUR_DEBUG
iPageMark = iModel->pageMark(iPage);
connect(iModel, SIGNAL(bookModelChanged()),
SLOT(onBookModelChanged()));
connect(iModel, SIGNAL(destroyed()),
SLOT(onBookModelDestroyed()));
connect(iModel, SIGNAL(pageMarksChanged()),
SLOT(onBookModelPageMarksChanged()));
connect(iModel, SIGNAL(loadingChanged()),
SLOT(onBookModelLoadingChanged()));
} else {
iPageMark.invalidate();
}
resetView();
Q_EMIT modelChanged();
}
}
void BooksPageWidget::setSettings(BooksSettings* aSettings)
{
if (iSettings != aSettings) {
shared_ptr<ZLTextStyle> oldTextStyle(iTextStyle);
if (iSettings) iSettings->disconnect(this);
iSettings = aSettings;
if (iSettings) {
iTextStyle = iSettings->textStyle();
connect(iSettings, SIGNAL(textStyleChanged()), SLOT(onTextStyleChanged()));
} else {
iTextStyle = BooksTextStyle::defaults();
}
if (!BooksTextStyle::equalLayout(oldTextStyle, iTextStyle)) {
resetView();
}
Q_EMIT settingsChanged();
}
}
void BooksPageWidget::onTextStyleChanged()
{
HDEBUG(iPage);
iTextStyle = iSettings->textStyle();
resetView();
}
void BooksPageWidget::onBookModelChanged()
{
HDEBUG(iModel->title());
BooksLoadingSignalBlocker block(this);
iPageMark = iModel->pageMark(iPage);
resetView();
}
void BooksPageWidget::onBookModelDestroyed()
{
HDEBUG("model destroyed");
HASSERT(iModel == sender());
BooksLoadingSignalBlocker block(this);
iModel = NULL;
Q_EMIT modelChanged();
resetView();
}
void BooksPageWidget::onBookModelPageMarksChanged()
{
const BooksPos pos = iModel->pageMark(iPage);
if (iPageMark != pos) {
BooksLoadingSignalBlocker block(this);
iPageMark = pos;
HDEBUG("page" << iPage);
resetView();
}
}
void BooksPageWidget::onBookModelLoadingChanged()
{
BooksLoadingSignalBlocker block(this);
if (!iModel->loading()) {
HDEBUG("page" << iPage);
const BooksPos pos = iModel->pageMark(iPage);
if (iPageMark != pos) {
iPageMark = pos;
resetView();
}
}
}
void BooksPageWidget::setPage(int aPage)
{
if (iPage != aPage) {
BooksLoadingSignalBlocker block(this);
iPage = aPage;
HDEBUG(iPage);
const BooksPos pos = iModel->pageMark(iPage);
if (iPageMark != pos) {
iPageMark = pos;
resetView();
}
resetView();
Q_EMIT pageChanged();
}
}
void BooksPageWidget::setLeftMargin(int aMargin)
{
if (iMargins.iLeft != aMargin) {
iMargins.iLeft = aMargin;
HDEBUG(aMargin);
resetView();
Q_EMIT leftMarginChanged();
}
}
void BooksPageWidget::setRightMargin(int aMargin)
{
if (iMargins.iRight != aMargin) {
iMargins.iRight = aMargin;
HDEBUG(aMargin);
resetView();
Q_EMIT rightMarginChanged();
}
}
void BooksPageWidget::setTopMargin(int aMargin)
{
if (iMargins.iTop != aMargin) {
iMargins.iTop = aMargin;
HDEBUG(aMargin);
resetView();
Q_EMIT topMarginChanged();
}
}
void BooksPageWidget::setBottomMargin(int aMargin)
{
if (iMargins.iBottom != aMargin) {
iMargins.iBottom = aMargin;
HDEBUG(aMargin);
resetView();
Q_EMIT bottomMarginChanged();
}
}
void BooksPageWidget::paint(QPainter* aPainter)
{
if (!iImage.isNull()) {
HDEBUG("page" << iPage);
aPainter->drawImage(0, 0, *iImage);
iEmpty = false;
} else if (iPage >= 0 && iPageMark.valid()) {
if (!iPaintRequest) {
iPaintRequest = true;
HDEBUG("page" << iPage << "(scheduled)");
scheduleRepaint();
} else {
HDEBUG("page" << iPage << "(not yet ready)");
}
iEmpty = true;
} else {
HDEBUG("page" << iPage << "(empty)");
iEmpty = true;
}
}
bool BooksPageWidget::loading() const
{
return iPage >= 0 && iResetTask && iRenderTask;
}
void BooksPageWidget::resetView()
{
BooksLoadingSignalBlocker block(this);
if (iResetTask) {
iResetTask->release(this);
iResetTask = NULL;
}
iData.reset();
if (iPage >= 0 && iPageMark.valid() &&
width() > 0 && height() > 0 && iModel) {
shared_ptr<ZLTextModel> textModel = iModel->bookTextModel();
if (!textModel.isNull()) {
iResetTask = new ResetTask(textModel, iTextStyle,
width(), height(), iMargins, iPageMark);
iTaskQueue->submit(iResetTask, this, SLOT(onResetTaskDone()));
cancelRepaint();
}
}
if (!iResetTask && !iEmpty) {
update();
}
}
void BooksPageWidget::cancelRepaint()
{
BooksLoadingSignalBlocker block(this);
if (iRenderTask) {
iRenderTask->release(this);
iRenderTask = NULL;
}
}
void BooksPageWidget::scheduleRepaint()
{
BooksLoadingSignalBlocker block(this);
cancelRepaint();
if (width() > 0 && height() > 0 && !iData.isNull()) {
iRenderTask = new RenderTask(iData, width(), height());
iTaskQueue->submit(iRenderTask, this, SLOT(onRenderTaskDone()));
} else {
update();
}
}
void BooksPageWidget::onResetTaskDone()
{
BooksLoadingSignalBlocker block(this);
HASSERT(sender() == iResetTask);
iData = iResetTask->iData;
iResetTask->iData = NULL;
iResetTask->release(this);
iResetTask = NULL;
if (iPaintRequest) {
scheduleRepaint();
} else {
update();
}
}
void BooksPageWidget::onRenderTaskDone()
{
BooksLoadingSignalBlocker block(this);
HASSERT(sender() == iRenderTask);
iImage = iRenderTask->iImage;
iRenderTask->release(this);
iRenderTask = NULL;
iPaintRequest = false;
update();
}
void BooksPageWidget::updateSize()
{
HDEBUG(QSize(width(), height()));
resetView();
}
void BooksPageWidget::onWidthChanged()
{
const int w = width();
HDEBUG(w);
// Width change will probably be followed by height change
iResizeTimer->start();
}
void BooksPageWidget::onHeightChanged()
{
const int h = height();
HDEBUG(h);
if (iResizeTimer->isActive()) {
// Height is usually changed after width, repaint right away
iResizeTimer->stop();
updateSize();
} else {
iResizeTimer->start();
}
}
void BooksPageWidget::onResizeTimeout()
{
// This can only happen if only width or height has changed. Normally,
// width change is followed by height change and view is reset from the
// setHeight() method
updateSize();
}

142
app/src/BooksPageWidget.h Normal file
View file

@ -0,0 +1,142 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_PAGE_WIDGET_H
#define BOOKS_PAGE_WIDGET_H
#include "BooksTypes.h"
#include "BooksTask.h"
#include "BooksTaskQueue.h"
#include "BooksSettings.h"
#include "BooksBookModel.h"
#include "BooksPos.h"
#include "BooksPaintContext.h"
#include "BooksLoadingProperty.h"
#include "ZLTextStyle.h"
#include <QQuickPaintedItem>
#include <QTimer>
class BooksPageWidget: public QQuickPaintedItem, private BooksLoadingProperty
{
Q_OBJECT
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
Q_PROPERTY(int leftMargin READ leftMargin WRITE setLeftMargin NOTIFY leftMarginChanged)
Q_PROPERTY(int rightMargin READ rightMargin WRITE setRightMargin NOTIFY rightMarginChanged)
Q_PROPERTY(int topMargin READ topMargin WRITE setTopMargin NOTIFY topMarginChanged)
Q_PROPERTY(int bottomMargin READ bottomMargin WRITE setBottomMargin NOTIFY bottomMarginChanged)
Q_PROPERTY(BooksBookModel* model READ model WRITE setModel NOTIFY modelChanged)
Q_PROPERTY(BooksSettings* settings READ settings WRITE setSettings NOTIFY settingsChanged)
public:
class Data;
BooksPageWidget(QQuickItem* aParent = NULL);
~BooksPageWidget();
bool loading() const;
int page() const { return iPage; }
void setPage(int aPage);
BooksBookModel* model() const { return iModel; }
void setModel(BooksBookModel* aModel);
BooksSettings* settings() const { return iSettings; }
void setSettings(BooksSettings* aSettings);
int leftMargin() const { return iMargins.iLeft; }
int rightMargin() const { return iMargins.iRight; }
int topMargin() const { return iMargins.iTop; }
int bottomMargin() const { return iMargins.iBottom; }
void setLeftMargin(int aMargin);
void setRightMargin(int aMargin);
void setTopMargin(int aMargin);
void setBottomMargin(int aMargin);
BooksMargins margins() const { return iMargins; }
shared_ptr<ZLTextStyle> textStyle() const { return iTextStyle; }
Q_SIGNALS:
void loadingChanged();
void pageChanged();
void modelChanged();
void settingsChanged();
void leftMarginChanged();
void rightMarginChanged();
void topMarginChanged();
void bottomMarginChanged();
private Q_SLOTS:
void onWidthChanged();
void onHeightChanged();
void onResizeTimeout();
void onBookModelChanged();
void onBookModelDestroyed();
void onBookModelPageMarksChanged();
void onBookModelLoadingChanged();
void onTextStyleChanged();
void onResetTaskDone();
void onRenderTaskDone();
private:
void paint(QPainter *painter);
void updateSize();
void resetView();
void scheduleRepaint();
void cancelRepaint();
private:
class ResetTask;
class RenderTask;
shared_ptr<BooksTaskQueue> iTaskQueue;
shared_ptr<ZLTextStyle> iTextStyle;
BooksPos iPageMark;
QTimer* iResizeTimer;
BooksBookModel* iModel;
BooksSettings* iSettings;
BooksMargins iMargins;
shared_ptr<Data> iData;
shared_ptr<QImage> iImage;
ResetTask* iResetTask;
RenderTask* iRenderTask;
bool iPaintRequest;
bool iEmpty;
int iPage;
};
#endif // BOOKS_PAGE_WIDGET_H

View file

@ -0,0 +1,257 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksPaintContext.h"
#include "HarbourDebug.h"
#include "ZLImage.h"
#include "image/ZLQtImageManager.h"
#include <QPainter>
#include <QFontMetrics>
#include <QFontDatabase>
#include <QImage>
static const std::string HELVETICA = "Helvetica";
BooksPaintContext::BooksPaintContext() :
iPainter(NULL), iWidth(0), iHeight(0),
iSpaceWidth(0), iDescent(0)
{
}
BooksPaintContext::BooksPaintContext(int aWidth, int aHeight) :
iPainter(NULL), iWidth(aWidth), iHeight(aHeight),
iSpaceWidth(0), iDescent(0)
{
}
BooksPaintContext::~BooksPaintContext()
{
}
void BooksPaintContext::beginPaint(QPainter *aPainter)
{
iPainter = aPainter;
iPainter->setFont(iFont);
}
void BooksPaintContext::endPaint()
{
iPainter = NULL;
}
void BooksPaintContext::fillFamiliesList(std::vector<std::string> &families) const
{
QFontDatabase db;
const QStringList qFamilies = db.families();
bool helveticaFlag = false;
for (QStringList::ConstIterator it = qFamilies.begin(); it != qFamilies.end(); ++it) {
std::string family = it->toUtf8().constData();
helveticaFlag |= (family == HELVETICA);
families.push_back(family);
}
if (!helveticaFlag) {
families.push_back(HELVETICA);
}
}
const std::string BooksPaintContext::realFontFamilyName(std::string &fontFamily) const
{
QString fullName = QFontInfo(QFont(QString::fromUtf8(fontFamily.c_str()))).family();
if (fullName.isNull() || fullName.isEmpty()) {
return HELVETICA;
}
return fullName.toStdString();
}
void BooksPaintContext::setFont(const std::string& family, int size, bool bold, bool italic)
{
bool fontChanged = false;
if (!family.empty()) {
QString qtFamily(QString::fromStdString(family));
if (iFont.family() != qtFamily) {
iFont.setFamily(qtFamily);
fontChanged = true;
}
}
if (iFont.pointSize() != size) {
iFont.setPointSize(size);
fontChanged = true;
}
if ((iFont.weight() != (bold ? QFont::Bold : QFont::Normal))) {
iFont.setWeight(bold ? QFont::Bold : QFont::Normal);
fontChanged = true;
}
if (iFont.italic() != italic) {
iFont.setItalic(italic);
fontChanged = true;
}
if (fontChanged) {
QFontMetrics fontMetrics(iFont);
iSpaceWidth = fontMetrics.width(QString(" "));
iDescent = fontMetrics.descent();
if (iPainter) {
iPainter->setFont(iFont);
}
}
}
void BooksPaintContext::setColor(ZLColor color, LineStyle style)
{
if (iPainter) {
iPainter->setPen(QPen(qtColor(color), 1, (style == SOLID_LINE) ?
Qt::SolidLine : Qt::DashLine));
}
}
void BooksPaintContext::setFillColor(ZLColor color, FillStyle style)
{
if (iPainter) {
iPainter->setBrush(QBrush(qtColor(color), (style == SOLID_FILL) ?
Qt::SolidPattern : Qt::Dense4Pattern));
}
}
int BooksPaintContext::stringWidth(const char *str, int len, bool) const
{
QFontMetrics fontMetrics(iFont);
return fontMetrics.width(QString::fromUtf8(str, len));
}
int BooksPaintContext::spaceWidth() const
{
return iSpaceWidth;
}
int BooksPaintContext::descent() const
{
return iDescent;
}
int BooksPaintContext::stringHeight() const
{
return iFont.pointSize() + 2;
}
void BooksPaintContext::drawString(int x, int y, const char* str, int len, bool rtl)
{
if (iPainter) {
QString qStr = QString::fromUtf8(str, len);
iPainter->setLayoutDirection(rtl ? Qt::RightToLeft : Qt::LeftToRight);
iPainter->drawText(x, y, qStr);
}
}
void BooksPaintContext::drawImage(int x, int y, const ZLImageData& image)
{
if (iPainter) {
const QImage* qImage = ((ZLQtImageData&)image).image();
if (qImage) {
iPainter->drawImage(x, y - image.height(), *qImage);
}
}
}
void BooksPaintContext::drawImage(int x, int y, const ZLImageData& image,
int width, int height, ScalingType type)
{
if (iPainter) {
const QImage* qImage = ((ZLQtImageData&)image).image();
if (qImage) {
const QImage scaled = qImage->scaled(
QSize(imageWidth(image, width, height, type),
imageHeight(image, width, height, type)),
Qt::KeepAspectRatio,
Qt::SmoothTransformation
);
iPainter->drawImage(x, y - scaled.height(), scaled);
}
}
}
void BooksPaintContext::drawLine(int x0, int y0, int x1, int y1)
{
if (iPainter) {
iPainter->drawPoint(x0, y0);
iPainter->drawLine(x0, y0, x1, y1);
iPainter->drawPoint(x1, y1);
}
}
void BooksPaintContext::fillRectangle(int x0, int y0, int x1, int y1)
{
if (iPainter) {
if (x1 < x0) {
int tmp = x1;
x1 = x0;
x0 = tmp;
}
if (y1 < y0) {
int tmp = y1;
y1 = y0;
y0 = tmp;
}
iPainter->fillRect(x0, y0, x1-x0+1, y1-y0+1, iPainter->brush());
}
}
void BooksPaintContext::drawFilledCircle(int x, int y, int r)
{
if (iPainter) {
iPainter->drawEllipse(x - r, y - r, 2 * r + 1, 2 * r + 1);
}
}
void BooksPaintContext::clear(ZLColor aColor)
{
if (iPainter) {
iPainter->fillRect(0, 0, iWidth, iHeight, qtColor(aColor));
}
}
int BooksPaintContext::width() const
{
return iWidth;
}
int BooksPaintContext::height() const
{
return iHeight;
}

112
app/src/BooksPaintContext.h Normal file
View file

@ -0,0 +1,112 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_PAINT_CONTEXT_H
#define BOOKS_PAINT_CONTEXT_H
#include "BooksTypes.h"
#include "ZLPaintContext.h"
#include <QFont>
#include <QSize>
#include <QColor>
class QPainter;
class BooksPaintContext : public ZLPaintContext {
public:
BooksPaintContext(int aWidth, int aHeight);
BooksPaintContext();
~BooksPaintContext();
void setWidth(int aWidth);
void setHeight(int aHeight);
void setSize(int aWidth, int aHeight);
bool isEmpty() const;
QSize size() const;
// ZLPaintContext
void beginPaint(QPainter* painter);
void endPaint();
int width() const;
int height() const;
void clear(ZLColor color);
void fillFamiliesList(std::vector<std::string>& families) const;
const std::string realFontFamilyName(std::string& fontFamily) const;
void setFont(const std::string& family, int size, bool bold, bool italic);
void setColor(ZLColor color, LineStyle style);
void setFillColor(ZLColor color, FillStyle style);
int stringWidth(const char* str, int len, bool rtl) const;
int spaceWidth() const;
int stringHeight() const;
int descent() const;
void drawString(int x, int y, const char* str, int len, bool rtl);
void drawImage(int x, int y, const ZLImageData& image);
void drawImage(int x, int y, const ZLImageData& image, int width, int height, ScalingType type);
void drawLine(int x0, int y0, int x1, int y1);
void fillRectangle(int x0, int y0, int x1, int y1);
void drawFilledCircle(int x, int y, int r);
private:
QPainter* iPainter;
int iWidth;
int iHeight;
mutable int iSpaceWidth;
int iDescent;
QFont iFont;
};
inline void BooksPaintContext::setWidth(int aWidth)
{ iWidth = aWidth; }
inline void BooksPaintContext::setHeight(int aHeight)
{ iHeight = aHeight; }
inline void BooksPaintContext::setSize(int aWidth, int aHeight)
{ iWidth = aWidth; iHeight = aHeight; }
inline bool BooksPaintContext::isEmpty() const
{ return (iWidth <= 0 || iHeight <= 0); }
inline QSize BooksPaintContext::size() const
{ return QSize(iWidth, iHeight); }
inline QColor qtColor(const ZLColor& aColor)
{ return QColor(aColor.Red, aColor.Green, aColor.Blue); }
#endif /* BOOKS_PAINT_CONTEXT_H */

167
app/src/BooksPos.h Normal file
View file

@ -0,0 +1,167 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_POSITION_H
#define BOOKS_POSITION_H
#include <QVariant>
#include <QDebug>
#include <QList>
#include "ZLTextParagraphCursor.h"
struct BooksPos {
int iParagraphIndex, iElementIndex, iCharIndex;
typedef QList<BooksPos> List;
typedef QList<BooksPos>::iterator Iterator;
typedef QList<BooksPos>::const_iterator ConstIterator;
BooksPos() :
iParagraphIndex(-1),
iElementIndex(-1),
iCharIndex(-1) {}
BooksPos(const ZLTextWordCursor& aCursor) :
iElementIndex(aCursor.elementIndex()),
iCharIndex(aCursor.charIndex())
{
ZLTextParagraphCursorPtr ptr(aCursor.paragraphCursorPtr());
iParagraphIndex = ptr.isNull() ? 0 : ptr->index();
}
BooksPos(int aParagraphIndex, int aElementIndex, int aCharIndex) :
iParagraphIndex(aParagraphIndex),
iElementIndex(aElementIndex),
iCharIndex(aCharIndex) {}
BooksPos(const BooksPos& aPos) :
iParagraphIndex(aPos.iParagraphIndex),
iElementIndex(aPos.iElementIndex),
iCharIndex(aPos.iCharIndex) {}
void invalidate()
{
iParagraphIndex = iElementIndex = iCharIndex = -1;
}
bool valid() const
{
return iParagraphIndex >= 0 && iElementIndex >= 0 && iCharIndex >= 0;
}
QVariant toVariant() const
{
QVariantList list;
list.append(iParagraphIndex);
list.append(iElementIndex);
list.append(iCharIndex);
return QVariant::fromValue(list);
}
static BooksPos fromVariant(QVariant aVariant)
{
if (aVariant.isValid()) {
QVariantList list = aVariant.toList();
if (list.count() == 3) {
bool ok = false;
int paraIndex = list.at(0).toInt(&ok);
if (ok) {
int elemIndex = list.at(1).toInt(&ok);
if (ok) {
int charIndex = list.at(2).toInt(&ok);
if (ok) {
return BooksPos(paraIndex, elemIndex, charIndex);
}
}
}
}
}
return BooksPos();
}
const BooksPos& operator = (const BooksPos& aPos)
{
iParagraphIndex = aPos.iParagraphIndex;
iElementIndex = aPos.iElementIndex;
iCharIndex = aPos.iCharIndex;
return *this;
}
bool operator < (const BooksPos& aPos) const
{
return (iParagraphIndex < aPos.iParagraphIndex) ? true :
(iParagraphIndex > aPos.iParagraphIndex) ? false :
(iElementIndex < aPos.iElementIndex) ? true :
(iElementIndex > aPos.iElementIndex) ? false :
(iCharIndex < aPos.iCharIndex) ? true :
(iCharIndex > aPos.iCharIndex) ? false : true;
}
bool operator > (const BooksPos& aPos) const
{
return (aPos < *this);
}
bool operator <= (const BooksPos& aPos) const
{
return !(*this > aPos);
}
bool operator >= (const BooksPos& aPos) const
{
return !(*this < aPos);
}
bool operator == (const BooksPos& aPos) const
{
return iParagraphIndex == aPos.iParagraphIndex &&
iElementIndex == aPos.iElementIndex &&
iCharIndex == aPos.iCharIndex;
}
bool operator != (const BooksPos& aPos) const
{
return iParagraphIndex != aPos.iParagraphIndex ||
iElementIndex != aPos.iElementIndex ||
iCharIndex != aPos.iCharIndex;
}
};
inline QDebug& operator<<(QDebug& aDebug, const BooksPos& aPosition)
{ aDebug << qPrintable(QString("BooksPos(%1,%2,%3)").
arg(aPosition.iParagraphIndex).arg(aPosition.iElementIndex).
arg(aPosition.iCharIndex)); return aDebug.maybeSpace(); }
#endif /* BOOKS_POSITION_H */

View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksSaveTimer.h"
#include <QCoreApplication>
#include <QTimer>
#define BOOKS_DEFAULT_INACTIVITY_TIMEOUT (1000)
#define BOOKS_DEFAULT_MANDATORY_SAVE_TIMEOUT (30000)
BooksSaveTimer::BooksSaveTimer(QObject* aParent) :
QObject(aParent),
iInactivityTimer(new QTimer(this)),
iMandatarySaveTimer(new QTimer(this)),
iSaveRequested(false)
{
connect(qApp, SIGNAL(aboutToQuit()), SLOT(onAboutToQuit()));
iInactivityTimer->setInterval(BOOKS_DEFAULT_INACTIVITY_TIMEOUT);
iInactivityTimer->setSingleShot(true);
connect(iInactivityTimer, SIGNAL(timeout()), SLOT(onTimeout()));
iMandatarySaveTimer->setInterval(BOOKS_DEFAULT_MANDATORY_SAVE_TIMEOUT);
iMandatarySaveTimer->setSingleShot(true);
connect(iMandatarySaveTimer, SIGNAL(timeout()), SLOT(onTimeout()));
}
void BooksSaveTimer::requestSave()
{
if (!iMandatarySaveTimer->isActive()) {
iMandatarySaveTimer->start();
}
iInactivityTimer->start();
}
void BooksSaveTimer::cancelSave()
{
iMandatarySaveTimer->stop();
iInactivityTimer->stop();
}
bool BooksSaveTimer::saveRequested() const
{
return iMandatarySaveTimer->isActive();
}
void BooksSaveTimer::onTimeout()
{
iMandatarySaveTimer->stop();
iInactivityTimer->stop();
Q_EMIT save();
}
void BooksSaveTimer::onAboutToQuit()
{
if (saveRequested()) {
onTimeout();
}
}

65
app/src/BooksSaveTimer.h Normal file
View file

@ -0,0 +1,65 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_SAVE_TIMER_H
#define BOOKS_SAVE_TIMER_H
#include <QObject>
class QTimer;
class BooksSaveTimer : public QObject
{
Q_OBJECT
public:
BooksSaveTimer(QObject* aParent = NULL);
void requestSave();
void cancelSave();
bool saveRequested() const;
Q_SIGNALS:
void save();
private Q_SLOTS:
void onAboutToQuit();
void onTimeout();
private:
QTimer* iInactivityTimer;
QTimer* iMandatarySaveTimer;
bool iSaveRequested;
};
#endif // BOOKS_SAVE_TIMER_H

305
app/src/BooksSettings.cpp Normal file
View file

@ -0,0 +1,305 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksSettings.h"
#include "BooksTextStyle.h"
#include "BooksBook.h"
#include "BooksDefs.h"
#include "HarbourDebug.h"
#include <MGConfItem>
#define DCONF_PATH "/apps/" BOOKS_APP_NAME "/"
#define KEY_FONT_SIZE "fontSize"
#define KEY_PAGE_DETAILS "pageDetails"
#define KEY_CURRENT_BOOK "currentBook"
#define DEFAULT_FONT_SIZE 0
#define DEFAULT_PAGE_DETAILS 0
#define DEFAULT_CURRENT_BOOK QString()
// ==========================================================================
// BooksSettings::TextStyle
// ==========================================================================
class BooksSettings::TextStyle : public ZLTextStyle
{
public:
TextStyle(int aFontSizeModifier) :
iDefaultStyle(BooksTextStyle::defaults()),
iFontSize(iDefaultStyle->fontSize() + 2*aFontSizeModifier)
{ HDEBUG(iFontSize); }
bool isDecorated() const;
const std::string &fontFamily() const;
int fontSize() const;
bool bold() const;
bool italic() const;
const std::string &colorStyle() const;
short spaceBefore(const ZLTextStyleEntry::Metrics& aMetrics) const;
short spaceAfter(const ZLTextStyleEntry::Metrics& aMetrics) const;
short lineStartIndent(const ZLTextStyleEntry::Metrics& aMetrics, bool aRtl) const;
short lineEndIndent(const ZLTextStyleEntry::Metrics& aMetrics, bool aRtl) const;
short firstLineIndentDelta(const ZLTextStyleEntry::Metrics& aMetrics) const;
int verticalShift() const;
ZLTextAlignmentType alignment() const;
double lineSpace() const;
bool allowHyphenations() const;
private:
shared_ptr<ZLTextStyle> iDefaultStyle;
int iFontSize;
};
const std::string&
BooksSettings::TextStyle::colorStyle() const
{
return iDefaultStyle->colorStyle();
}
bool
BooksSettings::TextStyle::isDecorated() const
{
return iDefaultStyle->isDecorated();
}
const std::string&
BooksSettings::TextStyle::fontFamily() const
{
return iDefaultStyle->fontFamily();
}
int
BooksSettings::TextStyle::fontSize() const
{
return iFontSize;
}
bool
BooksSettings::TextStyle::bold() const
{
return iDefaultStyle->bold();
}
bool
BooksSettings::TextStyle::italic() const
{
return iDefaultStyle->italic();
}
short
BooksSettings::TextStyle::spaceBefore(
const ZLTextStyleEntry::Metrics& aMetrics) const
{
return iDefaultStyle->spaceBefore(aMetrics);
}
short
BooksSettings::TextStyle::spaceAfter(
const ZLTextStyleEntry::Metrics& aMetrics) const
{
return iDefaultStyle->spaceAfter(aMetrics);
}
short
BooksSettings::TextStyle::lineStartIndent(
const ZLTextStyleEntry::Metrics& aMetrics,
bool aRtl) const
{
return iDefaultStyle->lineStartIndent(aMetrics, aRtl);
}
short
BooksSettings::TextStyle::lineEndIndent(
const ZLTextStyleEntry::Metrics& aMetrics,
bool aRtl) const
{
return iDefaultStyle->lineEndIndent(aMetrics, aRtl);
}
short
BooksSettings::TextStyle::firstLineIndentDelta(
const ZLTextStyleEntry::Metrics& aMetrics) const
{
return iDefaultStyle->firstLineIndentDelta(aMetrics);
}
int
BooksSettings::TextStyle::verticalShift() const
{
return iDefaultStyle->verticalShift();
}
ZLTextAlignmentType
BooksSettings::TextStyle::alignment() const
{
return iDefaultStyle->alignment();
}
double
BooksSettings::TextStyle::lineSpace() const
{
return iDefaultStyle->lineSpace();
}
bool
BooksSettings::TextStyle::allowHyphenations() const
{
return iDefaultStyle->allowHyphenations();
}
// ==========================================================================
// BooksSettings
// ==========================================================================
BooksSettings::BooksSettings(QObject* aParent) :
QObject(aParent),
iFontSize(new MGConfItem(DCONF_PATH KEY_FONT_SIZE, this)),
iPageDetails(new MGConfItem(DCONF_PATH KEY_PAGE_DETAILS, this)),
iCurrentBookPath(new MGConfItem(DCONF_PATH KEY_CURRENT_BOOK, this)),
iCurrentBook(NULL)
{
iTextStyle = new TextStyle(fontSize());
updateCurrentBook();
connect(iFontSize, SIGNAL(valueChanged()), SLOT(onFontSizeValueChanged()));
connect(iPageDetails, SIGNAL(valueChanged()), SIGNAL(pageDetailsChanged()));
connect(iCurrentBookPath, SIGNAL(valueChanged()), SLOT(onCurrentBookPathChanged()));
}
int
BooksSettings::fontSize() const
{
return iFontSize->value(DEFAULT_FONT_SIZE).toInt();
}
void
BooksSettings::setFontSize(
int aValue)
{
HDEBUG(aValue);
iFontSize->set(aValue);
}
void
BooksSettings::onFontSizeValueChanged()
{
const int newSize = fontSize();
HDEBUG(newSize);
iTextStyle = new TextStyle(newSize);
Q_EMIT fontSizeChanged();
Q_EMIT textStyleChanged();
}
int
BooksSettings::pageDetails() const
{
return iPageDetails->value(DEFAULT_PAGE_DETAILS).toInt();
}
void
BooksSettings::setPageDetails(
int aValue)
{
HDEBUG(aValue);
iPageDetails->set(aValue);
}
QObject*
BooksSettings::currentBook() const
{
return iCurrentBook;
}
void
BooksSettings::setCurrentBook(QObject* aBook)
{
BooksBook* book = qobject_cast<BooksBook*>(aBook);
if (iCurrentBook != book) {
if (iCurrentBook) iCurrentBook->release();
if (book) {
HDEBUG(book->path());
(iCurrentBook = book)->retain();
iCurrentBookPath->set(book->path());
} else {
iCurrentBook = NULL;
iCurrentBookPath->set(QString());
}
Q_EMIT currentBookChanged();
}
}
bool
BooksSettings::updateCurrentBook()
{
QString path = iCurrentBookPath->value(DEFAULT_CURRENT_BOOK).toString();
if (path.isEmpty()) {
if (iCurrentBook) {
iCurrentBook->release();
iCurrentBook = NULL;
return true;
}
} else if (!iCurrentBook || iCurrentBook->path() != path) {
ZLFile file(path.toStdString());
shared_ptr<Book> book = Book::loadFromFile(file);
if (!book.isNull()) {
QFileInfo info(path);
BooksStorageManager* mgr = BooksStorageManager::instance();
BooksStorage storage = mgr->storageForPath(info.path());
if (storage.isValid()) {
if (iCurrentBook) iCurrentBook->release();
iCurrentBook = new BooksBook(storage, book);
iCurrentBook->requestCoverImage();
return true;
}
}
if (iCurrentBook) {
iCurrentBook->release();
iCurrentBook = NULL;
return true;
}
}
return false;
}
void
BooksSettings::onCurrentBookPathChanged()
{
if (updateCurrentBook()) {
Q_EMIT currentBookChanged();
}
}

96
app/src/BooksSettings.h Normal file
View file

@ -0,0 +1,96 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_SETTINGS_H
#define BOOKS_SETTINGS_H
#include "BooksTypes.h"
#include "ZLTextStyle.h"
#include <QtQml>
class MGConfItem;
class BooksSettings : public QObject
{
Q_OBJECT
Q_ENUMS(FontSize)
Q_PROPERTY(int fontSize READ fontSize WRITE setFontSize NOTIFY fontSizeChanged)
Q_PROPERTY(int pageDetails READ pageDetails WRITE setPageDetails NOTIFY pageDetailsChanged)
Q_PROPERTY(QObject* currentBook READ currentBook WRITE setCurrentBook NOTIFY currentBookChanged)
class TextStyle;
public:
enum FontSize {
MinFontSize = -5,
DefaultFontSize = 0,
MaxFontSize = 5
};
explicit BooksSettings(QObject* aParent = NULL);
int fontSize() const;
void setFontSize(int aValue);
int pageDetails() const;
void setPageDetails(int aValue);
shared_ptr<ZLTextStyle> textStyle() const { return iTextStyle; }
QObject* currentBook() const;
void setCurrentBook(QObject* aBook);
signals:
void fontSizeChanged();
void textStyleChanged();
void pageDetailsChanged();
void currentBookChanged();
private Q_SLOTS:
void onFontSizeValueChanged();
void onCurrentBookPathChanged();
private:
void updateRenderType();
bool updateCurrentBook();
private:
MGConfItem* iFontSize;
MGConfItem* iPageDetails;
MGConfItem* iCurrentBookPath;
shared_ptr<ZLTextStyle> iTextStyle;
BooksBook* iCurrentBook;
};
QML_DECLARE_TYPE(BooksSettings)
#endif // BOOKS_SETTINGS_H

1071
app/src/BooksShelf.cpp Normal file

File diff suppressed because it is too large Load diff

172
app/src/BooksShelf.h Normal file
View file

@ -0,0 +1,172 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_SHELF_MODEL_H
#define BOOKS_SHELF_MODEL_H
#include "BooksItem.h"
#include "BooksStorage.h"
#include "BooksSaveTimer.h"
#include "BooksTask.h"
#include "BooksTaskQueue.h"
#include <QHash>
#include <QVariant>
#include <QByteArray>
#include <QAbstractListModel>
#include <QtQml>
class BooksShelf: public QAbstractListModel, public BooksItem
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
Q_PROPERTY(QString path READ path NOTIFY pathChanged)
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
Q_PROPERTY(QString device READ device WRITE setDevice NOTIFY deviceChanged)
Q_PROPERTY(QString relativePath READ relativePath WRITE setRelativePath NOTIFY relativePathChanged)
Q_PROPERTY(bool editMode READ editMode WRITE setEditMode NOTIFY editModeChanged)
Q_PROPERTY(bool hasDummyItem READ hasDummyItem WRITE setHasDummyItem NOTIFY hasDummyItemChanged)
Q_PROPERTY(int dummyItemIndex READ dummyItemIndex WRITE setDummyItemIndex NOTIFY dummyItemIndexChanged)
Q_PROPERTY(BooksBook* book READ book CONSTANT)
public:
explicit BooksShelf(QObject* aParent = NULL);
~BooksShelf();
Q_INVOKABLE QObject* get(int aIndex) const;
Q_INVOKABLE bool drop(QObject* aItem);
Q_INVOKABLE void move(int aFrom, int aTo);
Q_INVOKABLE void remove(int aIndex);
Q_INVOKABLE void removeAll();
Q_INVOKABLE bool deleteRequested(int aIndex) const;
Q_INVOKABLE void setDeleteRequested(int aIndex, bool aValue);
Q_INVOKABLE void cancelAllDeleteRequests();
Q_INVOKABLE void importBook(QObject* aBook);
bool loading() const { return iLoadTask != NULL; }
int count() const;
QString path() const { return iPath; }
QString relativePath() const { return iRelativePath; }
void setRelativePath(QString aPath);
BooksBook* bookAt(int aIndex) const;
bool editMode() const { return iEditMode; }
void setEditMode(bool aEditMode);
bool hasDummyItem() const { return iDummyItemIndex >= 0; }
void setHasDummyItem(bool aHasDummyItem);
int dummyItemIndex() const { return iDummyItemIndex; }
void setDummyItemIndex(int aIndex);
QString device() { return iStorage.device(); }
void setDevice(QString aDevice);
// QAbstractListModel
virtual QHash<int,QByteArray> roleNames() const;
virtual int rowCount(const QModelIndex& aParent) const;
virtual QVariant data(const QModelIndex& aIndex, int aRole) const;
// BooksListItem
virtual BooksItem* retain();
virtual void release();
virtual QObject* object();
virtual BooksShelf* shelf();
virtual BooksBook* book();
virtual QString name() const;
virtual QString fileName() const;
Q_SIGNALS:
void loadingChanged();
void countChanged();
void pathChanged();
void nameChanged();
void deviceChanged();
void relativePathChanged();
void editModeChanged();
void hasDummyItemChanged();
void dummyItemIndexChanged();
void bookAdded(BooksBook* aBook);
void bookRemoved(BooksBook* aBook);
private Q_SLOTS:
void onLoadTaskDone();
void onBookFound(BooksBook* aBook);
void onBookAccessibleChanged();
void onBookCopyingOutChanged();
void onBookMovedAway();
void onCopyTaskPercentChanged();
void onCopyTaskDone();
void onDeleteTaskDone();
void saveState();
private:
QString stateFileName() const;
int bookIndex(BooksBook* aBook) const;
int itemIndex(QString aFileName, int aStartIndex = 0) const;
bool validIndex(int aIndex) const;
void emitDataChangedSignal(int aRow, int aRole);
void queueStateSave();
void loadBookList();
void updatePath();
void removeAllBooks();
BooksBook* removeBook(int aIndex);
private:
class Data;
class CopyTask;
class LoadTask;
class ImportTask;
class DeleteTask;
QList<Data*> iList;
QList<DeleteTask*> iDeleteTasks;
LoadTask* iLoadTask;
QString iName;
QString iFileName;
QString iPath;
QString iRelativePath;
BooksStorage iStorage;
int iDummyItemIndex;
bool iEditMode;
QAtomicInt iRef;
BooksSaveTimer* iSaveTimer;
shared_ptr<BooksTaskQueue> iTaskQueue;
};
QML_DECLARE_TYPE(BooksShelf)
inline bool BooksShelf::validIndex(int aIndex) const
{ return aIndex >= 0 && aIndex < iList.count(); }
#endif // BOOKS_SHELF_MODEL_H

519
app/src/BooksStorage.cpp Normal file
View file

@ -0,0 +1,519 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksStorage.h"
#include "BooksDefs.h"
#include "HarbourDebug.h"
#include "ZLibrary.h"
#include <QHash>
#include <QFile>
#include <QTimer>
#include <QDateTime>
#include <QFileInfo>
#include <QStringList>
#include <QTextStream>
#include <QSocketNotifier>
#include <unistd.h>
#include <libudev.h>
#define INTERNAL_STATE_DIR "internal"
#define REMOVABLE_STATE_DIR "removable"
// ==========================================================================
// BooksStorage::Data
// ==========================================================================
class BooksStorage::Private: public QObject
{
Q_OBJECT
public:
Private(QString aDevice, QDir aBooksDir, bool aInternal);
bool isRemoved() const;
bool equal(const Private& aData) const;
static QString mountPoint(QString aPath);
static bool isMountPoint(QString aPath);
Q_SIGNALS:
void removed();
public:
QAtomicInt iRef;
QString iDevice;
QDir iBooksDir;
QDir iConfigDir;
bool iInternal;
bool iPresent;
};
BooksStorage::Private::Private(QString aDevice, QDir aBooksDir, bool aInternal) :
iRef(1), iDevice(aDevice), iBooksDir(aBooksDir), iInternal(aInternal),
iPresent(true)
{
QString cfgDir;
cfgDir = QString::fromStdString(ZLibrary::ApplicationWritableDirectory());
if (!cfgDir.endsWith('/')) cfgDir += '/';
if (aInternal) {
cfgDir += INTERNAL_STATE_DIR;
QString subDir(aDevice);
if (subDir.startsWith("/dev")) subDir.remove(0,4);
if (!subDir.startsWith('/')) cfgDir += '/';
cfgDir += subDir;
} else {
cfgDir += REMOVABLE_STATE_DIR "/";
QString label = QDir(Private::mountPoint(aBooksDir.path())).dirName();
if (label.isEmpty()) label = "sdcard";
cfgDir += label;
}
iConfigDir.setPath(cfgDir);
}
bool BooksStorage::Private::equal(const BooksStorage::Private& aData) const
{
return iInternal == iInternal &&
iPresent == iPresent &&
iDevice == aData.iDevice &&
iBooksDir == aData.iBooksDir;
}
bool BooksStorage::Private::isMountPoint(QString aPath)
{
std::string path = aPath.toStdString();
std::string parent = path + "/..";
struct stat stPath, stParent;
return stat(path.c_str(), &stPath) == 0 &&
stat(parent.c_str(), &stParent) == 0 &&
stPath.st_dev != stParent.st_dev;
}
QString BooksStorage::Private::mountPoint(QString aPath)
{
QFileInfo info(aPath);
QDir dir = info.isDir() ? QDir(aPath) : info.dir();
dir.makeAbsolute();
while (!isMountPoint(dir.path()) && !dir.isRoot()) {
info.setFile(dir.path());
dir = info.dir();
}
return dir.path();
}
// ==========================================================================
// BooksStorage
// ==========================================================================
BooksStorage::BooksStorage() :
QObject(NULL),
iPrivate(NULL),
iPassThrough(false)
{
}
BooksStorage::BooksStorage(const BooksStorage& aStorage) :
QObject(NULL),
iPrivate(aStorage.iPrivate),
iPassThrough(false)
{
if (iPrivate) iPrivate->iRef.ref();
}
BooksStorage::BooksStorage(QString aDevice, QDir aBooksDir, bool aInternal) :
QObject(NULL),
iPrivate(new Private(aDevice, aBooksDir, aInternal)),
iPassThrough(false)
{
HDEBUG("config dir" << qPrintable(configDir().path()));
}
BooksStorage::~BooksStorage()
{
if (iPrivate && !iPrivate->iRef.deref()) delete iPrivate;
}
void BooksStorage::connectNotify(const QMetaMethod& aSignal)
{
if (iPrivate && !iPassThrough) {
iPassThrough = true;
connect(iPrivate, SIGNAL(removed()), SIGNAL(removed()));
}
// Prototype of this method has changed in Qt5. Make sure that
// compilation breaks on Qt with older version of Qt:
QObject::connectNotify(aSignal);
}
QString BooksStorage::device() const
{
return iPrivate ? iPrivate->iDevice : QString();
}
QDir BooksStorage::booksDir() const
{
return iPrivate ? iPrivate->iBooksDir : QDir();
}
QDir BooksStorage::configDir() const
{
return iPrivate ? iPrivate->iConfigDir : QDir();
}
bool BooksStorage::isInternal() const
{
return iPrivate && iPrivate->iInternal;
}
bool BooksStorage::isPresent() const
{
return iPrivate && iPrivate->iPresent;
}
bool BooksStorage::equal(const BooksStorage& aStorage) const
{
if (iPrivate == aStorage.iPrivate) {
return true;
} else if (!iPrivate || !aStorage.iPrivate) {
return false;
} else {
return iPrivate->equal(*aStorage.iPrivate);
}
}
BooksStorage& BooksStorage::operator = (const BooksStorage& aStorage)
{
if (iPrivate != aStorage.iPrivate) {
if (iPrivate && !iPrivate->iRef.deref()) delete iPrivate;
iPrivate = aStorage.iPrivate;
if (iPrivate) iPrivate->iRef.ref();
}
return *this;
}
// ==========================================================================
// BooksStorageManager::Private
// ==========================================================================
#define STORAGE_MOUNTS_FILE "/proc/mounts"
#define STORAGE_SUBSYSTEM "block"
#define STORAGE_DISK "disk"
#define STORAGE_PARTITION "partition"
#define STORAGE_MOUNT_PREFIX "/media/"
#define STORAGE_ACTION_ADD "add"
#define STORAGE_ACTION_REMOVE "remove"
#define STORAGE_SCAN_INTERVAL 100
#define STORAGE_SCAN_TIMEOUT 5000
class BooksStorageManager::Private {
public:
static BooksStorageManager* gInstance;
Private();
~Private();
int findDevice(QString aDevice) const;
int findPath(QString aPath) const;
public:
QList<BooksStorage> iStorageList;
struct udev* iUdev;
struct udev_monitor* iMonitor;
int iDescriptor;
QSocketNotifier* iNotifier;
QTimer* iScanMountsTimer;
QDateTime iScanDeadline;
};
BooksStorageManager* BooksStorageManager::Private::gInstance = NULL;
BooksStorageManager::Private::Private() :
iUdev(udev_new()),
iMonitor(NULL),
iDescriptor(-1),
iNotifier(NULL),
iScanMountsTimer(NULL)
{
QString homeDevice;
QString homeBooks = QDir::homePath();
QString homeMount(BooksStorage::Private::mountPoint(homeBooks));
if (!homeBooks.endsWith('/')) homeBooks += '/';
homeBooks += QLatin1String(BOOKS_ROOT_SHELF_DIR);
HDEBUG("home mount" << qPrintable(homeMount));
HDEBUG("home books path" << qPrintable(homeBooks));
QFile mounts(STORAGE_MOUNTS_FILE);
if (mounts.open(QIODevice::ReadOnly | QIODevice::Text)) {
// For some reason QTextStream can't read /proc/mounts line by line
QByteArray contents = mounts.readAll();
QTextStream in(&contents);
QString mediaPrefix(STORAGE_MOUNT_PREFIX);
while (!in.atEnd()) {
QString line = in.readLine();
QStringList entries = line.split(' ', QString::SkipEmptyParts);
if (entries.count() > 2) {
QString mount(entries.at(1));
if (mount == homeMount) {
homeDevice = entries.at(0);
HDEBUG("home device" << homeDevice);
} else if (mount.startsWith(mediaPrefix)) {
QString dev = entries.at(0);
QString path = mount;
if (!path.endsWith('/')) path += '/';
path += QLatin1String(BOOKS_ROOT_SHELF_DIR);
HDEBUG("removable device" << dev << path);
iStorageList.append(BooksStorage(dev, path, false));
}
}
}
mounts.close();
}
iStorageList.insert(0, BooksStorage(homeDevice, homeBooks, true));
if (iUdev) {
iMonitor = udev_monitor_new_from_netlink(iUdev, "udev");
if (iMonitor) {
udev_monitor_filter_add_match_subsystem_devtype(iMonitor,
STORAGE_SUBSYSTEM, NULL);
udev_monitor_enable_receiving(iMonitor);
iDescriptor = udev_monitor_get_fd(iMonitor);
if (iDescriptor >= 0) {
iNotifier = new QSocketNotifier(iDescriptor,
QSocketNotifier::Read);
}
}
}
}
int BooksStorageManager::Private::findDevice(QString aDevice) const
{
const int n = iStorageList.count();
for (int i=0; i<n; i++) {
BooksStorage::Private* data = iStorageList.at(i).iPrivate;
if (data->iDevice == aDevice) {
return i;
}
}
return -1;
}
int BooksStorageManager::Private::findPath(QString aPath) const
{
if (!aPath.isEmpty()) {
const int n = iStorageList.count();
for (int i=0; i<n; i++) {
BooksStorage::Private* data = iStorageList.at(i).iPrivate;
if (aPath.startsWith(data->iBooksDir.path())) {
return i;
}
}
}
return -1;
}
BooksStorageManager::Private::~Private()
{
if (iUdev) {
if (iMonitor) {
if (iDescriptor >= 0) {
delete iNotifier;
close(iDescriptor);
}
udev_monitor_unref(iMonitor);
}
udev_unref(iUdev);
}
}
// ==========================================================================
// BooksStorageManager
// ==========================================================================
BooksStorageManager* BooksStorageManager::instance()
{
if (!Private::gInstance) {
Private::gInstance = new BooksStorageManager;
}
return Private::gInstance;
}
void BooksStorageManager::deleteInstance()
{
delete Private::gInstance;
HASSERT(!Private::gInstance);
}
BooksStorageManager::BooksStorageManager() :
iPrivate(new Private)
{
if (iPrivate->iNotifier) {
connect(iPrivate->iNotifier, SIGNAL(activated(int)),
SLOT(onDeviceEvent(int)));
}
}
BooksStorageManager::~BooksStorageManager()
{
if (Private::gInstance == this) {
Private::gInstance = NULL;
}
delete iPrivate;
}
int BooksStorageManager::count() const
{
return iPrivate->iStorageList.count();
}
QList<BooksStorage> BooksStorageManager::storageList() const
{
return iPrivate->iStorageList;
}
BooksStorage BooksStorageManager::storageForDevice(QString aDevice) const
{
int index = iPrivate->findDevice(aDevice);
return (index >= 0) ? iPrivate->iStorageList.at(index) : BooksStorage();
}
BooksStorage BooksStorageManager::storageForPath(QString aPath) const
{
int index = iPrivate->findPath(aPath);
return (index >= 0) ? iPrivate->iStorageList.at(index) : BooksStorage();
}
void BooksStorageManager::onDeviceEvent(int)
{
struct udev_device* dev = udev_monitor_receive_device(iPrivate->iMonitor);
if (dev) {
const char* devnode = udev_device_get_devnode(dev);
const char* action = udev_device_get_action(dev);
HDEBUG("got device");
HDEBUG(" node:" << devnode);
HDEBUG(" subsystem:" << udev_device_get_subsystem(dev));
HDEBUG(" devtype:" << udev_device_get_devtype(dev));
HDEBUG(" action:" << action);
if (devnode && action) {
if (!(strcmp(action, STORAGE_ACTION_ADD))) {
// Mount list isn't updated yet when we receive this
// notification. It takes hundreds of milliseconds until
// it gets mounted and becomes accessible.
if (!scanMounts()) {
HDEBUG("no new mounts found");
if (!iPrivate->iScanMountsTimer) {
QTimer* timer = new QTimer(this);
timer->setSingleShot(false);
timer->setInterval(STORAGE_SCAN_INTERVAL);
connect(timer, SIGNAL(timeout()), SLOT(onScanMounts()));
iPrivate->iScanMountsTimer = timer;
}
iPrivate->iScanMountsTimer->start();
iPrivate->iScanDeadline = QDateTime::currentDateTime().
addMSecs(STORAGE_SCAN_TIMEOUT);
}
} else if (!(strcmp(action, STORAGE_ACTION_REMOVE))) {
int pos = iPrivate->findDevice(devnode);
if (pos >= 0) {
HDEBUG("removable device is gone");
BooksStorage storage = iPrivate->iStorageList.takeAt(pos);
storage.iPrivate->iPresent = false;
Q_EMIT storage.iPrivate->removed();
Q_EMIT storageRemoved(storage);
}
}
}
udev_device_unref(dev);
} else {
HWARN("no device!");
}
}
bool BooksStorageManager::scanMounts()
{
bool newStorageFound = false;
QFile mounts(STORAGE_MOUNTS_FILE);
if (mounts.open(QIODevice::ReadOnly | QIODevice::Text)) {
// For some reason QTextStream can't read /proc/mounts line by line
QByteArray contents = mounts.readAll();
QTextStream in(&contents);
QString mediaPrefix(STORAGE_MOUNT_PREFIX);
while (!in.atEnd()) {
QString line = in.readLine();
QStringList entries = line.split(' ', QString::SkipEmptyParts);
if (entries.count() > 2) {
QString mount(entries.at(1));
if (mount.startsWith(mediaPrefix)) {
QString dev = entries.at(0);
int index = iPrivate->findDevice(dev);
if (index < 0) {
QString path = mount;
if (!path.endsWith('/')) path += '/';
path += QLatin1String(BOOKS_ROOT_SHELF_DIR);
HDEBUG("new removable device" << dev << path);
BooksStorage storage(dev, path, false);
iPrivate->iStorageList.append(storage);
Q_EMIT storageAdded(storage);
newStorageFound = true;
}
}
}
}
mounts.close();
}
return newStorageFound;
}
void BooksStorageManager::onScanMounts()
{
if (scanMounts()) {
iPrivate->iScanMountsTimer->stop();
} else {
QDateTime now = QDateTime::currentDateTime();
if (now > iPrivate->iScanDeadline) {
HDEBUG("timeout waiting for new mount to appear");
iPrivate->iScanMountsTimer->stop();
} else {
HDEBUG("no new mounts found");
}
}
}
#include "BooksStorage.moc"

115
app/src/BooksStorage.h Normal file
View file

@ -0,0 +1,115 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_STORAGE_H
#define BOOKS_STORAGE_H
#include <QObject>
#include <QList>
#include <QDir>
class BooksStorageManager;
class BooksStorage: public QObject
{
Q_OBJECT
Q_PROPERTY(QString root READ root CONSTANT)
public:
BooksStorage();
BooksStorage(const BooksStorage& aStorage);
~BooksStorage();
QString device() const;
QDir booksDir() const;
QDir configDir() const;
QString label() const { return booksDir().dirName(); }
QString root() const { return booksDir().path(); }
bool isValid() const { return iPrivate != NULL; }
bool isInternal() const;
bool isPresent() const;
bool equal(const BooksStorage& aStorage) const;
BooksStorage& operator = (const BooksStorage& aStorage);
bool operator == (const BooksStorage& aStorage) const
{ return equal(aStorage); }
Q_SIGNALS:
void removed();
private:
friend class BooksStorageManager;
BooksStorage(QString, QDir, bool);
void connectNotify(const QMetaMethod& aSignal);
private:
class Private;
Private* iPrivate;
bool iPassThrough;
};
class BooksStorageManager: public QObject
{
Q_OBJECT
public:
static BooksStorageManager* instance();
static void deleteInstance();
~BooksStorageManager();
int count() const;
QList<BooksStorage> storageList() const;
BooksStorage storageForDevice(QString aDevice) const;
BooksStorage storageForPath(QString aPath) const;
Q_SIGNALS:
void storageAdded(BooksStorage aStorage);
void storageRemoved(BooksStorage aStorage);
private:
BooksStorageManager();
bool scanMounts();
private Q_SLOTS:
void onDeviceEvent(int);
void onScanMounts();
private:
class Private;
Private* iPrivate;
};
Q_DECLARE_METATYPE(BooksStorage)
#endif // BOOKS_STORAGE_H

View file

@ -0,0 +1,198 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksStorageModel.h"
#include "HarbourDebug.h"
enum BooksStorageRole {
BooksStorageRoot = Qt::UserRole,
BooksStorageDevice,
BooksStorageRemovable,
BooksStorageDeleteAllRequest
};
// ==========================================================================
// BooksStorageModel::Data
// ==========================================================================
class BooksStorageModel::Data {
public:
Data(const BooksStorage& aStorage);
QString device() const { return iStorage.device(); }
QString root() const { return iStorage.root(); }
bool isRemovable() const { return !iStorage.isInternal(); }
public:
BooksStorage iStorage;
bool iDeleteAllRequest;
};
BooksStorageModel::Data::Data(const BooksStorage& aStorage) :
iStorage(aStorage),
iDeleteAllRequest(false)
{
// Pointers to these objects can be returned to QML
QQmlEngine::setObjectOwnership(&iStorage, QQmlEngine::CppOwnership);
}
// ==========================================================================
// BooksStorageModel
// ==========================================================================
BooksStorageModel::BooksStorageModel(QObject* aParent) :
QAbstractListModel(aParent)
{
#if QT_VERSION < 0x050000
setRoleNames(roleNames());
#endif
BooksStorageManager* mgr = BooksStorageManager::instance();
QList<BooksStorage> list = mgr->storageList();
const int n = list.count();
for (int i=0; i<n; i++) iList.append(new Data(list.at(i)));
iInternalStorage = list.at(0);
QQmlEngine::setObjectOwnership(&iInternalStorage, QQmlEngine::CppOwnership);
connect(mgr,
SIGNAL(storageAdded(BooksStorage)),
SLOT(onStorageAdded(BooksStorage)));
connect(mgr,
SIGNAL(storageRemoved(BooksStorage)),
SLOT(onStorageRemoved(BooksStorage)));
}
BooksStorageModel::~BooksStorageModel()
{
HDEBUG("destroyed");
}
int BooksStorageModel::count() const
{
return iList.count();
}
void BooksStorageModel::setDeleteAllRequest(int aIndex, bool aValue)
{
if (validIndex(aIndex)) {
Data* data = iList.at(aIndex);
if (data->iDeleteAllRequest != aValue) {
data->iDeleteAllRequest = aValue;
QModelIndex index(createIndex(aIndex, 0));
QVector<int> roles;
roles.append(BooksStorageDeleteAllRequest);
HDEBUG(aValue << data->root());
Q_EMIT dataChanged(index, index, roles);
}
}
}
void BooksStorageModel::cancelDeleteAllRequests()
{
for (int i=iList.count()-1; i>=0; i--) {
setDeleteAllRequest(i, false);
}
}
QObject* BooksStorageModel::get(int aIndex) const
{
if (validIndex(aIndex)) {
return &(iList.at(aIndex)->iStorage);
}
return NULL;
}
QHash<int,QByteArray> BooksStorageModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles.insert(BooksStorageRemovable, "removable");
roles.insert(BooksStorageRoot, "root");
roles.insert(BooksStorageDevice, "device");
roles.insert(BooksStorageDeleteAllRequest, "deleteAllRequest");
return roles;
}
int BooksStorageModel::rowCount(const QModelIndex&) const
{
return iList.count();
}
QVariant BooksStorageModel::data(const QModelIndex& aIndex, int aRole) const
{
const int i = aIndex.row();
if (validIndex(i)) {
Data* data = iList.at(i);
switch (aRole) {
case BooksStorageRoot: return data->root();
case BooksStorageDevice: return data->device();
case BooksStorageRemovable: return data->isRemovable();
case BooksStorageDeleteAllRequest: return data->iDeleteAllRequest;
}
}
return QVariant();
}
int BooksStorageModel::find(const BooksStorage& aStorage) const
{
const int n = iList.count();
for (int i=0; i<n; i++) {
if (iList.at(i)->iStorage.equal(aStorage)) {
return i;
}
}
return -1;
}
void BooksStorageModel::onStorageAdded(BooksStorage aStorage)
{
HDEBUG(aStorage.device() << "found");
const int index = iList.count();
beginInsertRows(QModelIndex(), index, index);
iList.append(new Data(aStorage));
endInsertRows();
Q_EMIT countChanged();
Q_EMIT newStorage(index);
}
void BooksStorageModel::onStorageRemoved(BooksStorage aStorage)
{
int index = find(aStorage);
if (index >=0) {
beginRemoveRows(QModelIndex(), index, index);
delete iList.takeAt(index);
endRemoveRows();
Q_EMIT countChanged();
} else {
HWARN("device not dfound on the list");
}
}

View file

@ -0,0 +1,89 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_STORAGE_MODEL_H
#define BOOKS_STORAGE_MODEL_H
#include "BooksStorage.h"
#include <QHash>
#include <QVariant>
#include <QByteArray>
#include <QAbstractListModel>
#include <QtQml>
class BooksStorageModel: public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(int count READ count NOTIFY countChanged)
Q_PROPERTY(QObject* internalStorage READ internalStorage CONSTANT)
public:
explicit BooksStorageModel(QObject* aParent = NULL);
~BooksStorageModel();
Q_INVOKABLE int count() const;
Q_INVOKABLE void setDeleteAllRequest(int aIndex, bool aValue);
Q_INVOKABLE void cancelDeleteAllRequests();
Q_INVOKABLE QObject* get(int aIndex) const;
QObject* internalStorage() { return &iInternalStorage; }
// QAbstractListModel
virtual QHash<int,QByteArray> roleNames() const;
virtual int rowCount(const QModelIndex& aParent) const;
virtual QVariant data(const QModelIndex& aIndex, int aRole) const;
Q_SIGNALS:
void countChanged();
void newStorage(int index);
private Q_SLOTS:
void onStorageAdded(BooksStorage aStorage);
void onStorageRemoved(BooksStorage aStorage);
private:
bool validIndex(int aIndex) const;
int find(const BooksStorage& aStorage) const;
private:
class Data;
QList<Data*> iList;
BooksStorage iInternalStorage;
};
QML_DECLARE_TYPE(BooksStorageModel)
inline bool BooksStorageModel::validIndex(int aIndex) const
{ return aIndex >= 0 && aIndex < iList.count(); }
#endif // BOOKS_STORAGE_MODEL_H

90
app/src/BooksTask.cpp Normal file
View file

@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksTask.h"
#include "BooksTaskQueue.h"
#include "HarbourDebug.h"
#include <QCoreApplication>
BooksTask::BooksTask() :
iAboutToQuit(false),
iReleased(false),
iStarted(false),
iDone(false)
{
setAutoDelete(false);
connect(qApp, SIGNAL(aboutToQuit()), SLOT(onAboutToQuit()));
connect(this, SIGNAL(runFinished()), SLOT(onRunFinished()),
Qt::QueuedConnection);
}
BooksTask::~BooksTask()
{
HASSERT(iReleased);
if (iStarted) wait();
}
void BooksTask::release(QObject* aHandler)
{
disconnect(aHandler);
iReleased = true;
if (!iStarted || iDone) {
delete this;
}
}
void BooksTask::run()
{
performTask();
Q_EMIT runFinished();
}
void BooksTask::onRunFinished()
{
HASSERT(!iDone);
if (!iReleased) {
Q_EMIT done();
}
iDone = true;
if (iReleased) {
delete this;
}
}
void BooksTask::onAboutToQuit()
{
HDEBUG("OK");
iAboutToQuit = true;
}

76
app/src/BooksTask.h Normal file
View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_TASK_H
#define BOOKS_TASK_H
#include <QObject>
#include <QRunnable>
class BooksTaskQueue;
class BooksTask : public QObject, public QRunnable
{
Q_OBJECT
friend class BooksTaskQueue;
protected:
BooksTask();
public:
virtual ~BooksTask();
void release(QObject* aHandler);
protected:
bool isCanceled() const { return iReleased || iAboutToQuit; }
virtual void run();
virtual void performTask() = 0;
Q_SIGNALS:
void runFinished();
void done();
private Q_SLOTS:
void onAboutToQuit();
void onRunFinished();
private:
bool iAboutToQuit;
bool iReleased;
bool iStarted;
bool iDone;
};
#endif // BOOKS_TASK_H

View file

@ -0,0 +1,87 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksTaskQueue.h"
#include "BooksTask.h"
#include "HarbourDebug.h"
static weak_ptr<BooksTaskQueue> booksTaskQueueInstance;
shared_ptr<BooksTaskQueue> BooksTaskQueue::instance()
{
shared_ptr<BooksTaskQueue> worker;
if (booksTaskQueueInstance.isNull()) {
booksTaskQueueInstance = (worker = new BooksTaskQueue());
} else {
worker = booksTaskQueueInstance;
}
return worker;
}
BooksTaskQueue::BooksTaskQueue() :
iPool(new QThreadPool)
{
HDEBUG("created");
iPool->setMaxThreadCount(1);
}
void BooksTaskQueue::waitForDone(int aMsecs)
{
shared_ptr<BooksTaskQueue> worker = booksTaskQueueInstance;
if (!worker.isNull()) {
worker->iPool->waitForDone(aMsecs);
}
}
BooksTaskQueue::~BooksTaskQueue()
{
HDEBUG("deleting");
iPool->waitForDone();
delete iPool;
HDEBUG("deleted");
}
void BooksTaskQueue::submit(BooksTask* aTask)
{
HASSERT(!aTask->iStarted);
aTask->iStarted = true;
iPool->start(aTask);
}
void BooksTaskQueue::submit(BooksTask* aTask, QObject* aTarget,
const char* aSlot)
{
QObject::connect(aTask, SIGNAL(done()), aTarget, aSlot);
submit(aTask);
}

62
app/src/BooksTaskQueue.h Normal file
View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_TASK_QUEUE_H
#define BOOKS_TASK_QUEUE_H
#include "shared_ptr.h"
#include <QThreadPool>
class BooksTask;
class BooksTaskQueue
{
friend class shared_ptr_storage<BooksTaskQueue>;
public:
static shared_ptr<BooksTaskQueue> instance();
static void waitForDone(int aMsecs = -1);
void submit(BooksTask* aTask);
void submit(BooksTask* aTask, QObject* aTarget, const char* aSlot);
private:
BooksTaskQueue();
~BooksTaskQueue();
private:
QThreadPool* iPool;
};
#endif // BOOKS_TASK_QUEUE_H

140
app/src/BooksTextStyle.cpp Normal file
View file

@ -0,0 +1,140 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksTextStyle.h"
#include "BooksDefs.h"
class BooksTextStyle::Default
{
public:
static weak_ptr<ZLTextStyle> instance;
static const std::string FONT_FAMILY;
};
weak_ptr<ZLTextStyle> BooksTextStyle::Default::instance;
const std::string BooksTextStyle::Default::FONT_FAMILY("Times");
shared_ptr<ZLTextStyle> BooksTextStyle::defaults()
{
shared_ptr<ZLTextStyle> style = Default::instance;
if (style.isNull()) {
style = new BooksTextStyle;
Default::instance = style;
}
return style;
}
bool
BooksTextStyle::equalLayout(
shared_ptr<ZLTextStyle> aStyle1,
shared_ptr<ZLTextStyle> aStyle2)
{
if (aStyle1.isNull()) aStyle1 = defaults();
if (aStyle2.isNull()) aStyle2 = defaults();
return aStyle1->fontSize() == aStyle2->fontSize();
}
const std::string& BooksTextStyle::colorStyle() const
{
return REGULAR_TEXT;
}
bool BooksTextStyle::isDecorated() const
{
return false;
}
const std::string& BooksTextStyle::fontFamily() const
{
return Default::FONT_FAMILY;
}
int BooksTextStyle::fontSize() const
{
return (26 * BOOKS_PPI / 330) & (~1); // Make sure it's divisible by 2
}
bool BooksTextStyle::bold() const
{
return false;
}
bool BooksTextStyle::italic() const
{
return false;
}
short BooksTextStyle::spaceBefore(const ZLTextStyleEntry::Metrics&) const
{
return 0;
}
short BooksTextStyle::spaceAfter(const ZLTextStyleEntry::Metrics&) const
{
return 0;
}
short BooksTextStyle::lineStartIndent(const ZLTextStyleEntry::Metrics&, bool) const
{
return 0;
}
short BooksTextStyle::lineEndIndent(const ZLTextStyleEntry::Metrics&, bool) const
{
return 0;
}
short BooksTextStyle::firstLineIndentDelta(const ZLTextStyleEntry::Metrics&) const
{
return 0;
}
int BooksTextStyle::verticalShift() const
{
return 0;
}
ZLTextAlignmentType BooksTextStyle::alignment() const
{
return ALIGN_JUSTIFY;
}
double BooksTextStyle::lineSpace() const
{
return 1.4;
}
bool BooksTextStyle::allowHyphenations() const
{
return true;
}

78
app/src/BooksTextStyle.h Normal file
View file

@ -0,0 +1,78 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_TEXT_STYLE_H
#define BOOKS_TEXT_STYLE_H
#include "BooksTypes.h"
#include <ZLTextStyle.h>
class BooksTextStyle : public ZLTextStyle
{
class Default;
public:
static shared_ptr<ZLTextStyle> defaults();
static bool equalLayout(shared_ptr<ZLTextStyle> aStyle1, shared_ptr<ZLTextStyle> aStyle2);
private:
static weak_ptr<ZLTextStyle> gInstance;
private:
BooksTextStyle() {}
public:
bool isDecorated() const;
const std::string &fontFamily() const;
int fontSize() const;
bool bold() const;
bool italic() const;
const std::string &colorStyle() const;
short spaceBefore(const ZLTextStyleEntry::Metrics& aMetrics) const;
short spaceAfter(const ZLTextStyleEntry::Metrics& aMetrics) const;
short lineStartIndent(const ZLTextStyleEntry::Metrics& aMetrics, bool aRtl) const;
short lineEndIndent(const ZLTextStyleEntry::Metrics& aMetrics, bool aRtl) const;
short firstLineIndentDelta(const ZLTextStyleEntry::Metrics& aMetrics) const;
int verticalShift() const;
ZLTextAlignmentType alignment() const;
double lineSpace() const;
bool allowHyphenations() const;
};
#endif // BOOKS_TEXT_STYLE_H

141
app/src/BooksTextView.cpp Normal file
View file

@ -0,0 +1,141 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksTextView.h"
#include "BooksTextStyle.h"
#include "options/FBOptions.h"
#define SUPER ZLTextView
BooksTextView::BooksTextView(
ZLPaintContext& aContext,
shared_ptr<ZLTextStyle> aTextStyle,
BooksMargins aMargins) :
SUPER(aContext),
iMargins(aMargins),
iBackgroundColor(255, 255, 255),
iTextStyle(aTextStyle)
{
}
void BooksTextView::paint()
{
SUPER::paint();
}
const std::string& BooksTextView::caption() const
{
return iCaption;
}
int BooksTextView::leftMargin() const
{
return iMargins.iLeft + iTextStyle->fontSize();
}
int BooksTextView::rightMargin() const
{
return iMargins.iRight + iTextStyle->fontSize();
}
int BooksTextView::topMargin() const
{
return iMargins.iTop;
}
int BooksTextView::bottomMargin() const
{
return iMargins.iBottom;
}
ZLColor BooksTextView::backgroundColor() const
{
return iBackgroundColor;
}
ZLColor BooksTextView::color(const std::string &colorStyle) const
{
return FBOptions::Instance().colorOption(colorStyle).value();
}
shared_ptr<ZLTextStyle> BooksTextView::baseStyle() const
{
return iTextStyle;
}
bool BooksTextView::isSelectionEnabled() const
{
return false;
}
int BooksTextView::doubleClickDelay() const
{
return isSelectionEnabled() ? 200 : 0;
}
shared_ptr<ZLTextPositionIndicatorInfo> BooksTextView::indicatorInfo() const
{
return NULL;
}
const BooksPos BooksTextView::rewind()
{
SUPER::gotoPosition(0, 0, 0);
preparePaintInfo();
if (!textArea().isVisible()) nextPage();
return position();
}
bool BooksTextView::nextPage()
{
BooksPos saved(position());
BooksPos current(saved);
do {
scrollPage(true, ZLTextAreaController::NO_OVERLAPPING, 1);
preparePaintInfo();
const BooksPos pos = position();
if (pos == current) {
gotoPosition(saved);
return false;
}
current = pos;
} while (!textArea().isVisible());
return true;
}
void BooksTextView::gotoPosition(const BooksPos& aPos)
{
SUPER::gotoPosition(aPos.iParagraphIndex, aPos.iElementIndex,
aPos.iCharIndex);
}

89
app/src/BooksTextView.h Normal file
View file

@ -0,0 +1,89 @@
/*
Copyright (C) 2015 Jolla Ltd.
Contact: Slava Monich <slava.monich@jolla.com>
You may use this file under the terms of the BSD license as follows:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Nemo Mobile nor the names of its contributors
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_TEXT_VIEW_H
#define BOOKS_TEXT_VIEW_H
#include "BooksTypes.h"
#include "BooksPos.h"
#include "ZLColor.h"
#include "ZLTextView.h"
#include "ZLTextModel.h"
#include "ZLTextStyle.h"
#include "ZLPaintContext.h"
class BooksTextView: public ZLTextView
{
public:
BooksTextView(ZLPaintContext& aContext,
shared_ptr<ZLTextStyle> aTextStyle,
BooksMargins aMargin);
public:
BooksPos position() const;
const BooksPos rewind();
void gotoPosition(const BooksPos& aPos);
bool nextPage();
void paint();
// ZLView
virtual const std::string &caption() const;
virtual ZLColor backgroundColor() const;
// ZLTextView
virtual shared_ptr<ZLTextPositionIndicatorInfo> indicatorInfo() const;
virtual int doubleClickDelay() const;
virtual int leftMargin() const;
virtual int rightMargin() const;
virtual int topMargin() const;
virtual int bottomMargin() const;
// ZLTextArea::Properties
virtual shared_ptr<ZLTextStyle> baseStyle() const;
virtual ZLColor color(const std::string &style = std::string()) const;
virtual bool isSelectionEnabled() const;
public:
BooksMargins iMargins;
ZLColor iBackgroundColor;
private:
std::string iCaption;
shared_ptr<ZLTextStyle> iTextStyle;
};
inline BooksPos BooksTextView::position() const
{ return BooksPos(textArea().startCursor()); }
#endif // BOOKS_TEXT_VIEW_H

50
app/src/BooksTypes.h Normal file
View file

@ -0,0 +1,50 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BOOKS_TYPES_H
#define BOOKS_TYPES_H
#include "shared_ptr.h"
class BooksBook;
class BooksShelf;
struct BooksMargins {
int iLeft;
int iRight;
int iTop;
int iBottom;
BooksMargins() : iLeft(0), iRight(0), iTop(0), iBottom(0) {}
};
#endif // BOOKS_TYPES_H

150
app/src/ZLApplication.cpp Normal file
View file

@ -0,0 +1,150 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksDefs.h"
#include "HarbourDebug.h"
#include "ZLApplication.h"
#include "ZLApplicationWindow.h"
#include "ZLPaintContext.h"
#include "ZLViewWidget.h"
#include "ZLKeyBindings.h"
#include "ZLPopupData.h"
#include "ZLToolbar.h"
#include "ZLMenu.h"
// We are not currently using ZLApplication and ZLApplicationWindow but
// there are some references to it from the fbreader code which we must
// resolve.
ZLApplication *ZLApplication::ourInstance = NULL;
static const std::string ROTATION = "Rotation";
static const std::string ANGLE = "Angle";
static const std::string STATE = "State";
static const std::string KEYBOARD = "Keyboard";
static const std::string FULL_CONTROL = "FullControl";
static const std::string CONFIG = "Config";
static const std::string AUTO_SAVE = "AutoSave";
static const std::string TIMEOUT = "Timeout";
ZLApplication &ZLApplication::Instance()
{
if (!ourInstance) new ZLApplication(BOOKS_APP_NAME);
return *ourInstance;
}
void ZLApplication::deleteInstance()
{
if (ourInstance) {
delete ourInstance;
ourInstance = NULL;
}
}
ZLApplication::ZLApplication(const std::string &name) :
ZLApplicationBase(name),
RotationAngleOption(ZLCategoryKey::CONFIG, ROTATION, ANGLE, ZLView::DEGREES90),
AngleStateOption(ZLCategoryKey::CONFIG, STATE, ANGLE, ZLView::DEGREES0),
KeyboardControlOption(ZLCategoryKey::CONFIG, KEYBOARD, FULL_CONTROL, false),
ConfigAutoSavingOption(ZLCategoryKey::CONFIG, CONFIG, AUTO_SAVE, true),
ConfigAutoSaveTimeoutOption(ZLCategoryKey::CONFIG, CONFIG, TIMEOUT, 1, 6000, 30),
KeyDelayOption(ZLCategoryKey::CONFIG, "Options", "KeyDelay", 0, 5000, 250)
{
ourInstance = this;
}
ZLApplication::~ZLApplication()
{
}
void ZLApplication::initWindow()
{
}
shared_ptr<ZLKeyBindings> ZLApplication::keyBindings()
{
return NULL;
}
bool ZLApplication::closeView()
{
HDEBUG("NO");
return false;
}
void ZLApplication::openFile(ZLFile const&)
{
}
bool ZLApplication::canDragFiles(std::vector<std::string, std::allocator<std::string> > const&) const
{
HDEBUG("NO");
return false;
}
void ZLApplication::dragFiles(std::vector<std::string, std::allocator<std::string> > const&)
{
HDEBUG("WHAT?");
}
bool ZLApplication::isViewFinal() const
{
HDEBUG("YES");
return true;
}
void ZLApplication::refreshWindow()
{
HDEBUG("OK");
}
void ZLApplication::setHyperlinkCursor(bool aHyperLink)
{
HDEBUG(aHyperLink);
}
ZLApplicationWindow &ZLApplicationWindow::Instance()
{
HDEBUG("THIS IS NOT SUPPOSED TO HAPPEN!");
return *((ZLApplicationWindow*)NULL);
}
void ZLApplicationWindow::init()
{
}
void ZLApplicationWindow::refresh()
{
}

217
app/src/ZLibrary.cpp Normal file
View file

@ -0,0 +1,217 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksDefs.h"
#include "BooksStorage.h"
#include "BooksPaintContext.h"
#include "BooksDialogManager.h"
#include "HarbourDebug.h"
#include "ZLibrary.h"
#include "ZLApplication.h"
#include "ZLLanguageUtil.h"
#include "filesystem/ZLQtFSManager.h"
#include "time/ZLQtTime.h"
#include "image/ZLQtImageManager.h"
#include "iconv/IConvEncodingConverter.h"
#include <sailfishapp.h>
#include <QGuiApplication>
#include <QStandardPaths>
#include <QQuickView>
#include <QQmlContext>
#include <execinfo.h>
#include <dlfcn.h>
bool ZLibrary::ourLocaleIsInitialized = false;
std::string ZLibrary::ourLanguage;
std::string ZLibrary::ourCountry;
std::string ZLibrary::ourZLibraryDirectory;
std::string ZLibrary::ourApplicationName;
std::string ZLibrary::ourImageDirectory;
std::string ZLibrary::ourApplicationImageDirectory;
std::string ZLibrary::ourApplicationDirectory;
std::string ZLibrary::ourApplicationWritableDirectory;
std::string ZLibrary::ourDefaultFilesPathPrefix;
const std::string ZLibrary::FileNameDelimiter("/");
const std::string ZLibrary::PathDelimiter(":");
const std::string ZLibrary::EndOfLine("\n");
const std::string ZLibrary::BaseDirectory;
void ZLibrary::initLocale()
{
const char* locale = setlocale(LC_MESSAGES, "");
HDEBUG(locale);
if (locale) {
std::string sLocale = locale;
const int dotIndex = sLocale.find('.');
if (dotIndex != -1) sLocale = sLocale.substr(0, dotIndex);
const int dashIndex = std::min(sLocale.find('_'), sLocale.find('-'));
if (dashIndex == -1) {
ourLanguage = sLocale;
} else {
ourLanguage = sLocale.substr(0, dashIndex);
ourCountry = sLocale.substr(dashIndex + 1);
}
}
}
ZLPaintContext* ZLibrary::createContext()
{
HDEBUG("creating context");
return new BooksPaintContext();
}
bool ZLibrary::init(int& aArgc, char** &aArgv)
{
HDEBUG("initializing");
ZLibrary::parseArguments(aArgc, aArgv);
std::string rootDir("/");
Dl_info info;
void* addr = NULL;
if (backtrace(&addr, 1) && dladdr(addr, &info)) {
// Step two levels up. For an application deployed from QtCreator
// it's going to be /opt/sdk/<app-name> directory, for a normally
// installed app - the root directory.
HDEBUG("app path" << info.dli_fname);
char* slash = (char*)strrchr(info.dli_fname, '/');
if (slash) {
slash[0] = 0;
HDEBUG("app dir" << info.dli_fname);
ourApplicationDirectory = info.dli_fname;
slash = (char*)strrchr(info.dli_fname, '/');
if (slash) {
slash[0] = 0;
slash = (char*)strrchr(info.dli_fname, '/');
if (slash) {
slash[1] = 0;
HDEBUG("root dir" << info.dli_fname);
rootDir = info.dli_fname;
}
}
}
}
((std::string&)BaseDirectory) = rootDir;
ourApplicationName = BOOKS_APP_NAME;
ourZLibraryDirectory = BaseDirectory + BOOKS_DATA_DIR;
ourApplicationDirectory = BaseDirectory + BOOKS_DATA_ROOT;
ourImageDirectory = BaseDirectory + BOOKS_ICONS_DIR;
ourDefaultFilesPathPrefix = ourZLibraryDirectory + "/";
ourApplicationImageDirectory = ourImageDirectory;
ourApplicationWritableDirectory =
(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) +
QLatin1String("/" BOOKS_APP_NAME)).toStdString();
HDEBUG("zlibrary dir" << ourZLibraryDirectory.c_str());
HDEBUG("image dir" << ourImageDirectory.c_str());
HDEBUG("writable dir" << ourApplicationWritableDirectory.c_str());
BooksStorageManager::instance();
ZLQtTimeManager::createInstance();
ZLQtFSManager::createInstance();
BooksDialogManager::createInstance();
ZLQtImageManager::createInstance();
ZLEncodingCollection::Instance().registerProvider(new IConvEncodingConverterProvider());
ZLApplication::Instance();
return true;
}
void ZLibrary::run(ZLApplication* aApp)
{
if (ZLLanguageUtil::isRTLLanguage(ZLibrary::Language())) {
qApp->setLayoutDirection(Qt::RightToLeft);
}
QString qml(QString::fromStdString(BaseDirectory + BOOKS_QML_FILE));
HDEBUG("qml file" << qPrintable(qml));
QQuickView* view = SailfishApp::createView();
view->setSource(QUrl::fromLocalFile(qml));
view->rootContext()->setContextProperty("PointsPerInch", BOOKS_PPI);
view->show();
HDEBUG("started");
qApp->exec();
HDEBUG("exiting...");
}
void ZLibrary::parseArguments(int &argc, char **&argv)
{
}
void ZLibrary::shutdown()
{
ZLApplication::deleteInstance();
ZLImageManager::deleteInstance();
ZLDialogManager::deleteInstance();
ZLFSManager::deleteInstance();
ZLTimeManager::deleteInstance();
BooksStorageManager::deleteInstance();
}
void ZLibrary::initApplication(const std::string& aName)
{
HDEBUG(aName.c_str());
}
std::string ZLibrary::Language()
{
if (ourLanguage.empty()) {
if (!ourLocaleIsInitialized) {
initLocale();
ourLocaleIsInitialized = true;
}
}
if (ourLanguage.empty()) {
ourLanguage = "en";
}
return ourLanguage;
}
std::string ZLibrary::Country()
{
if (ourCountry.empty() && !ourLocaleIsInitialized) {
initLocale();
ourLocaleIsInitialized = true;
}
return ourCountry;
}

122
app/src/libudev.c Normal file
View file

@ -0,0 +1,122 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <libudev.h>
#include <stdlib.h>
#include <dlfcn.h>
#define LIBUDEV_FUNCTIONS(f) \
f(struct udev*, udev_unref,\
(struct udev* udev), \
(udev)) \
f(struct udev_monitor*, udev_monitor_new_from_netlink, \
(struct udev* udev, const char* name), \
(udev, name)) \
f(struct udev_device*, udev_monitor_receive_device, \
(struct udev_monitor* mon), \
(mon)) \
f(int, udev_monitor_filter_add_match_subsystem_devtype, \
(struct udev_monitor* mon, const char* sub, const char* dev), \
(mon, sub, dev)) \
f(int, udev_monitor_enable_receiving, \
(struct udev_monitor* mon), \
(mon)) \
f(int, udev_monitor_get_fd, \
(struct udev_monitor* mon), \
(mon)) \
f(struct udev_monitor*, udev_monitor_unref, \
(struct udev_monitor* mon), \
(mon)) \
f(const char*, udev_device_get_devnode, \
(struct udev_device* dev), \
(dev)) \
f(const char*, udev_device_get_devpath, \
(struct udev_device* dev), \
(dev)) \
f(const char*, udev_device_get_subsystem, \
(struct udev_device* dev), \
(dev)) \
f(const char*, udev_device_get_devtype, \
(struct udev_device* dev), \
(dev)) \
f(const char*, udev_device_get_action, \
(struct udev_device* dev), \
(dev)) \
f(struct udev_device*, udev_device_unref, \
(struct udev_device* dev), \
(dev))
static struct {
void* handle;
union {
struct libudev_proc {
struct udev* (*udev_new)(void);
#define FN_POINTER(ret,name,params,args) ret (* name) params;
LIBUDEV_FUNCTIONS(FN_POINTER)
} udev;
void* entry[1];
} fn;
} libudev;
static const char* libudev_names[] = {
"udev_new",
#define FN_NAME(ret,name,params,args) #name,
LIBUDEV_FUNCTIONS(FN_NAME)
};
#define LIBUDEV_NUM_FUNCTIONS (sizeof(libudev_names)/sizeof(libudev_names[0]))
#define LIBUDEV_NO_HANDLE ((void*)-1)
#define LIBUDEV_SO "/usr/lib/libudev.so.1"
/* udev_new() is the special function where we load the library */
struct udev*
udev_new()
{
if (!libudev.handle) {
libudev.handle = dlopen(LIBUDEV_SO, RTLD_LAZY);
if (libudev.handle) {
unsigned int i;
for (i=0; i<LIBUDEV_NUM_FUNCTIONS; i++) {
libudev.fn.entry[i] = dlsym(libudev.handle, libudev_names[i]);
}
} else {
libudev.handle = LIBUDEV_NO_HANDLE;
}
}
return libudev.fn.udev.udev_new ? libudev.fn.udev.udev_new() : NULL;
}
/* All other wrappers are generated by the preprocessor */
#define FN_IMPL(ret,name,params,args) ret name params \
{ return libudev.fn.udev.name ? libudev.fn.udev.name args : 0; }
LIBUDEV_FUNCTIONS(FN_IMPL)

94
app/src/main.cpp Normal file
View file

@ -0,0 +1,94 @@
/*
* Copyright (C) 2015 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* You may use this file under the terms of the BSD license as follows:
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Nemo Mobile nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "BooksDefs.h"
#include "BooksShelf.h"
#include "BooksBook.h"
#include "BooksBookModel.h"
#include "BooksCoverModel.h"
#include "BooksConfig.h"
#include "BooksSettings.h"
#include "BooksImportModel.h"
#include "BooksStorageModel.h"
#include "BooksPageWidget.h"
#include "BooksListWatcher.h"
#include "BooksCoverWidget.h"
#include "BooksTaskQueue.h"
#include "HarbourDebug.h"
#include "HarbourLib.h"
#include "ZLibrary.h"
#include <sailfishapp.h>
#include <QGuiApplication>
int main(int argc, char **argv)
{
QGuiApplication* app = SailfishApp::application(argc, argv);
BOOKS_QML_REGISTER(BooksShelf, "Shelf");
BOOKS_QML_REGISTER(BooksBook, "Book");
BOOKS_QML_REGISTER(BooksBookModel, "BookModel");
BOOKS_QML_REGISTER(BooksCoverModel, "CoverModel");
BOOKS_QML_REGISTER(BooksImportModel, "BooksImportModel");
BOOKS_QML_REGISTER(BooksStorageModel, "BookStorage");
BOOKS_QML_REGISTER(BooksPageWidget, "PageWidget");
BOOKS_QML_REGISTER(BooksListWatcher, "ListWatcher");
BOOKS_QML_REGISTER(BooksCoverWidget, "BookCover");
BOOKS_QML_REGISTER(BooksSettings, "Settings");
HarbourLib::registerTypes(BOOKS_QML_PLUGIN,
BOOKS_QML_PLUGIN_V1, BOOKS_QML_PLUGIN_V2);
QLocale locale;
QTranslator* translator = new QTranslator(app);
QString transDir = SailfishApp::pathTo("translations").toLocalFile();
QString transFile(BOOKS_APP_NAME);
if (translator->load(locale, transFile, "-", transDir) ||
translator->load(transFile, transDir)) {
app->installTranslator(translator);
} else {
HDEBUG("Failed to load translator for" << locale);
delete translator;
}
BooksConfigManager configManager;
if (ZLibrary::init(argc, argv)) {
ZLibrary::run(NULL);
BooksTaskQueue::waitForDone();
ZLibrary::shutdown();
}
delete app;
return 0;
}

View file

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="ru">
<context>
<name></name>
<message id="book-font-default">
<source>Use default fonts</source>
<translation>Шрифты по умолчанию</translation>
</message>
<message id="book-font-smaller">
<source>Use smaller fonts</source>
<translation>Шрифты поменьше</translation>
</message>
<message id="book-font-larger">
<source>Use larger fonts</source>
<translation>Шрифты побольше</translation>
</message>
<message id="book-view-back">
<source>Back to library</source>
<translation>Вернуться в библиотеку</translation>
</message>
<message id="book-view-loading">
<source>Loading...</source>
<translation>Загрузка...</translation>
</message>
<message id="shelf-view-about-to-delete-all">
<source>Deleting all books</source>
<translation>Удаляем все книги</translation>
</message>
<message id="shelf-view-no-books">
<source>No books</source>
<translation>Здесь пусто, нет ничего вообще</translation>
</message>
<message id="storage-removable">
<source>Memory card</source>
<translation>Карта памяти</translation>
</message>
<message id="storage-internal">
<source>Internal storage</source>
<translation>Встроенная память</translation>
</message>
<message id="storage-book-count" numerus="yes">
<source>%0 book(s)</source>
<translation>
<numerusform>%0 книга</numerusform>
<numerusform>%0 книги</numerusform>
<numerusform>%0 книг</numerusform>
</translation>
</message>
<message id="storage-view-scan-downloads">
<source>Scan downloads</source>
<translation>Проверить загрузки</translation>
</message>
<message id="storage-view-delete-everything">
<source>Delete all books</source>
<translation>Удалить все книги</translation>
</message>
<message id="import-view-import-n-books" numerus="yes">
<source>Import %0 book(s)</source>
<translation>
<numerusform>Взять %0 книгу</numerusform>
<numerusform>Взять %0 книги</numerusform>
<numerusform>Взять %0 книг</numerusform>
</translation>
</message>
<message id="import-view-select-books">
<source>Select books</source>
<translation type="unfinished">Выбирайте</translation>
</message>
<message id="import-view-no-new-books-found">
<source>No new books found</source>
<translation>Новых книг не найдено, вообще ни одной</translation>
</message>
</context>
</TS>

View file

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en">
<context>
<name></name>
<message id="book-font-default">
<source>Use default fonts</source>
<translation>Use default fonts</translation>
</message>
<message id="book-font-smaller">
<source>Use smaller fonts</source>
<translation>Use smaller fonts</translation>
</message>
<message id="book-font-larger">
<source>Use larger fonts</source>
<translation>Use larger fonts</translation>
</message>
<message id="book-view-back">
<source>Back to library</source>
<translation>Back to library</translation>
</message>
<message id="book-view-loading">
<source>Loading...</source>
<translation>Loading...</translation>
</message>
<message id="shelf-view-about-to-delete-all">
<source>Deleting all books</source>
<translation>Deleting all books</translation>
</message>
<message id="shelf-view-no-books">
<source>No books</source>
<translation>No books</translation>
</message>
<message id="storage-removable">
<source>Memory card</source>
<translation>Memory card</translation>
</message>
<message id="storage-internal">
<source>Internal storage</source>
<translation>Internal storage</translation>
</message>
<message id="storage-book-count" numerus="yes">
<source>%0 book(s)</source>
<translation>
<numerusform>%0 book</numerusform>
<numerusform>%0 books</numerusform>
</translation>
</message>
<message id="storage-view-scan-downloads">
<source>Scan downloads</source>
<translation>Check downloads</translation>
</message>
<message id="storage-view-delete-everything">
<source>Delete all books</source>
<translation>Delete all books</translation>
</message>
<message id="import-view-import-n-books" numerus="yes">
<source>Import %0 book(s)</source>
<translation>
<numerusform>Import %0 book</numerusform>
<numerusform>Import %0 books</numerusform>
</translation>
</message>
<message id="import-view-select-books">
<source>Select books</source>
<translation>Select books</translation>
</message>
<message id="import-view-no-new-books-found">
<source>No new books found</source>
<translation>No new books found</translation>
</message>
</context>
</TS>

484
fbreader/fbreader.pro Normal file
View file

@ -0,0 +1,484 @@
TEMPLATE = lib
TARGET = fbreader
CONFIG += staticlib object_parallel_to_source link_pkgconfig
PKGCONFIG += expat glib-2.0
!include(../common.pri)
# Directories
FRIBIDI_DIR = $$_PRO_FILE_PWD_/../fribidi
LINEBREAK_DIR = $$_PRO_FILE_PWD_/../linebreak
FBREADER_DIR = fbreader
DEFINES += \
VERSION="\\\"0.99\\\"" \
FBREADER_DISABLE_ZLFILE_PLAIN_STREAM_CACHE=1 \
FBREADER_DISABLE_BOOKS_DB=1
# Core
CORE = $$FBREADER_DIR/zlibrary/core
CORE_SRC = $$CORE/src
INCLUDEPATH += $$CORE/include
SOURCES += \
$$CORE_SRC/encoding/MyEncodingConverter.cpp \
$$CORE_SRC/encoding/EncodingCollectionReader.cpp \
$$CORE_SRC/encoding/DummyEncodingConverter.cpp \
$$CORE_SRC/encoding/ZLEncodingSet.cpp \
$$CORE_SRC/encoding/ZLEncodingConverter.cpp \
$$CORE_SRC/encoding/ZLEncodingCollection.cpp \
$$CORE_SRC/typeId/ZLTypeId.cpp \
$$CORE_SRC/language/ZLLanguageDetector.cpp \
$$CORE_SRC/language/ZLStatistics.cpp \
$$CORE_SRC/language/ZLStatisticsGenerator.cpp \
$$CORE_SRC/language/ZLStatisticsXMLReader.cpp \
$$CORE_SRC/language/ZLStatisticsXMLWriter.cpp \
$$CORE_SRC/language/ZLLanguageList.cpp \
$$CORE_SRC/language/ZLCharSequence.cpp \
$$CORE_SRC/language/ZLStatisticsItem.cpp \
$$CORE_SRC/language/ZLLanguageMatcher.cpp \
$$CORE_SRC/options/ZLConfig.cpp \
$$CORE_SRC/options/ZLOptions.cpp \
$$CORE_SRC/options/ZLCategoryKey.cpp \
$$CORE_SRC/runnable/ZLRunnable.cpp \
$$CORE_SRC/runnable/ZLExecutionData.cpp \
$$CORE_SRC/application/ZLApplicationBase.cpp \
$$CORE_SRC/application/ZLKeyBindings.cpp \
$$CORE_SRC/util/ZLUnicodeUtil.cpp \
$$CORE_SRC/util/ZLKeyUtil.cpp \
$$CORE_SRC/util/ZLUserData.cpp \
$$CORE_SRC/util/ZLFileUtil.cpp \
$$CORE_SRC/util/ZLSearchUtil.cpp \
$$CORE_SRC/util/ZLStringUtil.cpp \
$$CORE_SRC/util/ZLLanguageUtil.cpp \
$$CORE_SRC/dialogs/ZLDialogManager.cpp \
$$CORE_SRC/dialogs/ZLDialog.cpp \
$$CORE_SRC/dialogs/ZLOptionEntry.cpp \
$$CORE_SRC/dialogs/ZLOptionsDialog.cpp \
$$CORE_SRC/dialogs/ZLProgressDialog.cpp \
$$CORE_SRC/dialogs/ZLOptionView.cpp \
$$CORE_SRC/dialogs/ZLDialogContent.cpp \
$$CORE_SRC/dialogs/ZLDialogContentBuilder.cpp \
$$CORE_SRC/dialogs/ZLOpenFileDialog.cpp \
$$CORE_SRC/time/ZLTimeManager.cpp \
$$CORE_SRC/time/ZLTime.cpp \
$$CORE_SRC/logger/ZLLogger.cpp \
$$CORE_SRC/optionEntries/ZLSimpleOptionEntry.cpp \
$$CORE_SRC/xml/ZLXMLWriter.cpp \
$$CORE_SRC/xml/ZLXMLReader.cpp \
$$CORE_SRC/xml/expat/ZLXMLReaderInternal.cpp \
$$CORE_SRC/view/ZLPaintContext.cpp \
$$CORE_SRC/view/ZLView.cpp \
$$CORE_SRC/view/ZLMirroredPaintContext.cpp \
$$CORE_SRC/filesystem/tar/ZLTar.cpp \
$$CORE_SRC/filesystem/ZLInputStreamDecorator.cpp \
$$CORE_SRC/filesystem/ZLFile.cpp \
$$CORE_SRC/filesystem/ZLDir.cpp \
$$CORE_SRC/filesystem/ZLFSManager.cpp \
$$CORE_SRC/filesystem/zip/ZLZipInputStream.cpp \
$$CORE_SRC/filesystem/zip/ZLZipDir.cpp \
$$CORE_SRC/filesystem/zip/ZLZipEntryCache.cpp \
$$CORE_SRC/filesystem/zip/ZLZipHeader.cpp \
$$CORE_SRC/filesystem/zip/ZLZDecompressor.cpp \
$$CORE_SRC/filesystem/zip/ZLGzipInputStream.cpp \
$$CORE_SRC/filesystem/bzip2/ZLBzip2InputStream.cpp \
$$CORE_SRC/resources/ZLResource.cpp \
$$CORE_SRC/image/ZLBase64EncodedImage.cpp \
$$CORE_SRC/image/ZLNetworkImage.cpp \
$$CORE_SRC/image/ZLImage.cpp \
$$CORE_SRC/image/ZLFileImage.cpp \
$$CORE_SRC/image/ZLStreamImage.cpp \
$$CORE_SRC/image/ZLImageManager.cpp \
$$CORE_SRC/unix/iconv/IConvEncodingConverter.cpp \
$$CORE_SRC/unix/time/ZLUnixTime.cpp \
$$CORE_SRC/unix/filesystem/ZLUnixFSManager.cpp \
$$CORE_SRC/unix/filesystem/ZLUnixFileInputStream.cpp \
$$CORE_SRC/unix/filesystem/ZLUnixFSDir.cpp \
$$CORE_SRC/unix/filesystem/ZLUnixFileOutputStream.cpp \
$$CORE_SRC/network/ZLAsynchronousInputStream.cpp \
$$CORE_SRC/network/ZLPlainAsynchronousInputStream.cpp \
$$CORE_SRC/constants/ZLXMLNamespace.cpp \
$$CORE_SRC/constants/ZLMimeType.cpp
HEADERS += \
$$CORE_SRC/encoding/EncodingCollectionReader.h \
$$CORE_SRC/encoding/DummyEncodingConverter.h \
$$CORE_SRC/encoding/ZLEncodingConverterProvider.h \
$$CORE_SRC/encoding/MyEncodingConverter.h \
$$CORE_SRC/encoding/ZLEncodingConverter.h \
$$CORE_SRC/typeId/ZLTypeId.h \
$$CORE_SRC/language/ZLLanguageList.h \
$$CORE_SRC/language/ZLStatistics.h \
$$CORE_SRC/language/ZLStatisticsGenerator.h \
$$CORE_SRC/language/ZLLanguageMatcher.h \
$$CORE_SRC/language/ZLLanguageDetector.h \
$$CORE_SRC/language/ZLStatisticsXMLWriter.h \
$$CORE_SRC/language/ZLStatisticsItem.h \
$$CORE_SRC/language/ZLCharSequence.h \
$$CORE_SRC/language/ZLStatisticsXMLReader.h \
$$CORE_SRC/options/ZLOptions.h \
$$CORE_SRC/options/ZLConfig.h \
$$CORE_SRC/runnable/ZLExecutionData.h \
$$CORE_SRC/runnable/ZLRunnable.h \
$$CORE_SRC/application/ZLApplication.h \
$$CORE_SRC/application/ZLKeyBindings.h \
$$CORE_SRC/application/ZLApplicationWindow.h \
$$CORE_SRC/util/ZLStringUtil.h \
$$CORE_SRC/util/ZLUserData.h \
$$CORE_SRC/util/ZLBoolean3.h \
$$CORE_SRC/util/shared_ptr.h \
$$CORE_SRC/util/ZLFileUtil.h \
$$CORE_SRC/util/ZLColor.h \
$$CORE_SRC/util/ZLUnicodeUtil.h \
$$CORE_SRC/util/ZLSearchUtil.h \
$$CORE_SRC/util/allocator.h \
$$CORE_SRC/util/ZLKeyUtil.h \
$$CORE_SRC/util/ZLLanguageUtil.h \
$$CORE_SRC/dialogs/ZLOptionsDialog.h \
$$CORE_SRC/dialogs/ZLDialogManager.h \
$$CORE_SRC/dialogs/ZLOptionView.h \
$$CORE_SRC/dialogs/ZLDialog.h \
$$CORE_SRC/dialogs/ZLProgressDialog.h \
$$CORE_SRC/dialogs/ZLOptionEntry.h \
$$CORE_SRC/dialogs/ZLDialogContent.h \
$$CORE_SRC/dialogs/ZLDialogContentBuilder.h \
$$CORE_SRC/dialogs/ZLOpenFileDialog.h \
$$CORE_SRC/library/ZLibrary.h \
$$CORE_SRC/time/ZLTime.h \
$$CORE_SRC/time/ZLTimeManager.h \
$$CORE_SRC/logger/ZLLogger.h \
$$CORE_SRC/optionEntries/ZLSimpleOptionEntry.h \
$$CORE_SRC/xml/ZLXMLWriter.h \
$$CORE_SRC/xml/ZLXMLReader.h \
$$CORE_SRC/xml/expat/ZLXMLReaderInternal.h \
$$CORE_SRC/view/ZLPaintContext.h \
$$CORE_SRC/view/ZLMirroredPaintContext.h \
$$CORE_SRC/view/ZLViewWidget.h \
$$CORE_SRC/view/ZLView.h \
$$CORE_SRC/filesystem/tar/ZLTar.h \
$$CORE_SRC/filesystem/ZLInputStream.h \
$$CORE_SRC/filesystem/ZLFSManager.h \
$$CORE_SRC/filesystem/ZLDir.h \
$$CORE_SRC/filesystem/ZLFileInfo.h \
$$CORE_SRC/filesystem/zip/ZLZDecompressor.h \
$$CORE_SRC/filesystem/zip/ZLZipHeader.h \
$$CORE_SRC/filesystem/zip/ZLZip.h \
$$CORE_SRC/filesystem/ZLOutputStream.h \
$$CORE_SRC/filesystem/ZLFile.h \
$$CORE_SRC/filesystem/ZLFSDir.h \
$$CORE_SRC/filesystem/bzip2/ZLBzip2InputStream.h \
$$CORE_SRC/resources/ZLResource.h \
$$CORE_SRC/image/ZLImage.h \
$$CORE_SRC/image/ZLBase64EncodedImage.h \
$$CORE_SRC/image/ZLFileImage.h \
$$CORE_SRC/image/ZLStreamImage.h \
$$CORE_SRC/image/ZLImageManager.h \
$$CORE_SRC/image/ZLNetworkImage.h \
$$CORE_SRC/unix/iconv/IConvEncodingConverter.h \
$$CORE_SRC/unix/library/ZLibraryImplementation.h \
$$CORE_SRC/unix/time/ZLUnixTime.h \
$$CORE_SRC/unix/filesystem/ZLUnixFSManager.h \
$$CORE_SRC/unix/filesystem/ZLUnixFileInputStream.h \
$$CORE_SRC/unix/filesystem/ZLUnixFileOutputStream.h \
$$CORE_SRC/unix/filesystem/ZLUnixFSDir.h \
$$CORE_SRC/network/ZLAsynchronousInputStream.h \
$$CORE_SRC/network/ZLPlainAsynchronousInputStream.h \
$$CORE_SRC/constants/ZLMimeType.h \
$$CORE_SRC/constants/ZLXMLNamespace.h
# Text
TEXT = $$FBREADER_DIR/zlibrary/text
TEXT_SRC = $$TEXT/src
INCLUDEPATH += \
$$TEXT/include \
$$LINEBREAK_DIR/include \
$$FRIBIDI_DIR/include
SOURCES += \
$$TEXT_SRC/style/ZLTextStyle.cpp \
$$TEXT_SRC/style/ZLTextDecoratedStyle.cpp \
$$TEXT_SRC/style/ZLTextStyleCollection.cpp \
$$TEXT_SRC/model/ZLTextModel.cpp \
$$TEXT_SRC/model/ZLTextParagraph.cpp \
$$TEXT_SRC/model/ZLTextRowMemoryAllocator.cpp \
$$TEXT_SRC/hyphenation/ZLTextTeXHyphenator.cpp \
$$TEXT_SRC/hyphenation/ZLTextHyphenator.cpp \
$$TEXT_SRC/hyphenation/ZLTextHyphenationReader.cpp \
$$TEXT_SRC/view/ZLTextPositionIndicator.cpp \
$$TEXT_SRC/view/ZLTextSelectionScroller.cpp \
$$TEXT_SRC/view/ZLTextView_paint.cpp \
$$TEXT_SRC/view/ZLTextView.cpp \
$$TEXT_SRC/area/ZLTextArea.cpp \
$$TEXT_SRC/area/ZLTextArea_processTextLine.cpp \
$$TEXT_SRC/area/ZLTextArea_drawTextLine.cpp \
$$TEXT_SRC/area/ZLTextAreaController.cpp \
$$TEXT_SRC/area/ZLTextParagraphBuilder.cpp \
$$TEXT_SRC/area/ZLTextParagraphCursor.cpp \
$$TEXT_SRC/area/ZLTextWord.cpp \
$$TEXT_SRC/area/ZLTextElement.cpp \
$$TEXT_SRC/area/ZLTextAreaStyle.cpp \
$$TEXT_SRC/area/ZLTextArea_drawWord.cpp \
$$TEXT_SRC/area/ZLTextArea_drawTreeLines.cpp \
$$TEXT_SRC/area/ZLTextArea_prepareTextLine.cpp \
$$TEXT_SRC/area/ZLTextSelectionModel.cpp
HEADERS += \
$$TEXT_SRC/style/ZLTextStyleCollection.h \
$$TEXT_SRC/style/ZLTextStyle.h \
$$TEXT_SRC/style/ZLTextDecoratedStyle.h \
$$TEXT_SRC/model/ZLTextAlignmentType.h \
$$TEXT_SRC/model/ZLTextFontModifier.h \
$$TEXT_SRC/model/ZLTextParagraph.h \
$$TEXT_SRC/model/ZLTextModel.h \
$$TEXT_SRC/model/ZLTextMark.h \
$$TEXT_SRC/model/ZLTextRowMemoryAllocator.h \
$$TEXT_SRC/model/ZLTextKind.h \
$$TEXT_SRC/hyphenation/ZLTextHyphenator.h \
$$TEXT_SRC/hyphenation/ZLTextHyphenationReader.h \
$$TEXT_SRC/hyphenation/ZLTextTeXHyphenator.h \
$$TEXT_SRC/view/ZLTextView.h \
$$TEXT_SRC/view/ZLTextPositionIndicatorInfo.h \
$$TEXT_SRC/view/ZLTextPositionIndicator.h \
$$TEXT_SRC/view/ZLTextSelectionScroller.h \
$$TEXT_SRC/area/ZLTextWord.h \
$$TEXT_SRC/area/ZLTextElement.h \
$$TEXT_SRC/area/ZLTextRectangle.h \
$$TEXT_SRC/area/ZLTextAreaStyle.h \
$$TEXT_SRC/area/ZLTextParagraphBuilder.h \
$$TEXT_SRC/area/ZLTextAreaController.h \
$$TEXT_SRC/area/ZLTextLineInfo.h \
$$TEXT_SRC/area/ZLTextSelectionModel.h \
$$TEXT_SRC/area/ZLTextParagraphCursor.h \
$$TEXT_SRC/area/ZLTextArea.h
# UI
UI_SRC = $$FBREADER_DIR/zlibrary/ui/src/qt4
SOURCES += \
$$UI_SRC/time/ZLQtTime.cpp \
$$UI_SRC/filesystem/ZLQtFSManager.cpp \
$$UI_SRC/image/ZLQtImageManager.cpp
HEADERS += \
$$UI_SRC/time/ZLQtTime.h \
$$UI_SRC/filesystem/ZLQtFSManager.h \
$$UI_SRC/image/ZLQtImageManager.h
# FBReader
FBREADER_SRC = $$FBREADER_DIR/fbreader/src
SOURCES += \
$$FBREADER_SRC/migration/BookInfo.cpp \
$$FBREADER_SRC/options/FBOptions.cpp \
$$FBREADER_SRC/options/FBCategoryKey.cpp \
$$FBREADER_SRC/libraryActions/AuthorInfoDialog.cpp \
$$FBREADER_SRC/libraryActions/LibraryTagActions.cpp \
$$FBREADER_SRC/libraryActions/LibraryBookActions.cpp \
$$FBREADER_SRC/libraryActions/LibraryAuthorActions.cpp \
$$FBREADER_SRC/libraryActions/BooksUtil.cpp \
$$FBREADER_SRC/fbreader/PreferencesPopupData.cpp \
$$FBREADER_SRC/fbreader/FBReaderActions.cpp \
$$FBREADER_SRC/fbreader/TimeUpdater.cpp \
$$FBREADER_SRC/fbreader/SearchActions.cpp \
$$FBREADER_SRC/fbreader/ContentsView.cpp \
$$FBREADER_SRC/fbreader/BooksOrderAction.cpp \
$$FBREADER_SRC/fbreader/BookTextView.cpp \
$$FBREADER_SRC/fbreader/FBReaderActionCode.cpp \
$$FBREADER_SRC/fbreader/RecentBooksPopupData.cpp \
$$FBREADER_SRC/fbreader/SearchOnNetworkAction.cpp \
$$FBREADER_SRC/fbreader/ScrollingAction.cpp \
$$FBREADER_SRC/fbreader/AddBookAction.cpp \
$$FBREADER_SRC/library/Book.cpp \
$$FBREADER_SRC/library/Comparators.cpp \
$$FBREADER_SRC/library/Library.cpp \
$$FBREADER_SRC/library/Tag.cpp \
$$FBREADER_SRC/library/Author.cpp \
$$FBREADER_SRC/formats/dummy/DummyBookReader.cpp \
$$FBREADER_SRC/formats/dummy/DummyMetaInfoReader.cpp \
$$FBREADER_SRC/formats/fb2/FB2MetaInfoReader.cpp \
$$FBREADER_SRC/formats/fb2/FB2TagManager.cpp \
$$FBREADER_SRC/formats/fb2/FB2Plugin.cpp \
$$FBREADER_SRC/formats/fb2/FB2Reader.cpp \
$$FBREADER_SRC/formats/fb2/FB2BookReader.cpp \
$$FBREADER_SRC/formats/fb2/FB2CoverReader.cpp \
$$FBREADER_SRC/formats/openreader/OpenReaderPlugin.cpp \
$$FBREADER_SRC/formats/openreader/ORDescriptionReader.cpp \
$$FBREADER_SRC/formats/openreader/ORBookReader.cpp \
$$FBREADER_SRC/formats/util/TextFormatDetector.cpp \
$$FBREADER_SRC/formats/util/EntityFilesCollector.cpp \
$$FBREADER_SRC/formats/util/MiscUtil.cpp \
$$FBREADER_SRC/formats/util/XMLTextStream.cpp \
$$FBREADER_SRC/formats/util/MergedStream.cpp \
$$FBREADER_SRC/formats/FormatPlugin.cpp \
$$FBREADER_SRC/formats/EncodedTextReader.cpp \
$$FBREADER_SRC/formats/css/StyleSheetParser.cpp \
$$FBREADER_SRC/formats/css/StyleSheetTable.cpp \
$$FBREADER_SRC/formats/tcr/TcrPlugin.cpp \
$$FBREADER_SRC/formats/tcr/TcrStream.cpp \
$$FBREADER_SRC/formats/tcr/PPLBookReader.cpp \
$$FBREADER_SRC/formats/rtf/RtfReaderStream.cpp \
$$FBREADER_SRC/formats/rtf/RtfBookReader.cpp \
$$FBREADER_SRC/formats/rtf/RtfReader.cpp \
$$FBREADER_SRC/formats/rtf/RtfDescriptionReader.cpp \
$$FBREADER_SRC/formats/rtf/RtfPlugin.cpp \
$$FBREADER_SRC/formats/rtf/RtfImage.cpp \
$$FBREADER_SRC/formats/pdb/PmlBookReader.cpp \
$$FBREADER_SRC/formats/pdb/PalmDocLikePlugin.cpp \
$$FBREADER_SRC/formats/pdb/HuffDecompressor.cpp \
$$FBREADER_SRC/formats/pdb/EReaderStream.cpp \
$$FBREADER_SRC/formats/pdb/MobipocketPlugin.cpp \
$$FBREADER_SRC/formats/pdb/PluckerBookReader.cpp \
$$FBREADER_SRC/formats/pdb/PalmDocLikeStream.cpp \
$$FBREADER_SRC/formats/pdb/PluckerImages.cpp \
$$FBREADER_SRC/formats/pdb/PdbStream.cpp \
$$FBREADER_SRC/formats/pdb/PmlReader.cpp \
$$FBREADER_SRC/formats/pdb/BitReader.cpp \
$$FBREADER_SRC/formats/pdb/PluckerTextStream.cpp \
$$FBREADER_SRC/formats/pdb/MobipocketHtmlBookReader.cpp \
$$FBREADER_SRC/formats/pdb/ZTXTStream.cpp \
$$FBREADER_SRC/formats/pdb/ZTXTPlugin.cpp \
$$FBREADER_SRC/formats/pdb/PdbPlugin.cpp \
$$FBREADER_SRC/formats/pdb/HtmlMetainfoReader.cpp \
$$FBREADER_SRC/formats/pdb/PdbReader.cpp \
$$FBREADER_SRC/formats/pdb/PalmDocStream.cpp \
$$FBREADER_SRC/formats/pdb/PalmDocPlugin.cpp \
$$FBREADER_SRC/formats/pdb/DocDecompressor.cpp \
$$FBREADER_SRC/formats/pdb/SimplePdbPlugin.cpp \
$$FBREADER_SRC/formats/pdb/PluckerPlugin.cpp \
$$FBREADER_SRC/formats/pdb/EReaderPlugin.cpp \
$$FBREADER_SRC/formats/xhtml/XHTMLReader.cpp \
$$FBREADER_SRC/formats/oeb/OEBPlugin.cpp \
$$FBREADER_SRC/formats/oeb/OEBMetaInfoReader.cpp \
$$FBREADER_SRC/formats/oeb/OEBCoverReader.cpp \
$$FBREADER_SRC/formats/oeb/OEBTextStream.cpp \
$$FBREADER_SRC/formats/oeb/OEBBookReader.cpp \
$$FBREADER_SRC/formats/oeb/NCXReader.cpp \
$$FBREADER_SRC/formats/PluginCollection.cpp \
$$FBREADER_SRC/formats/html/HtmlPlugin.cpp \
$$FBREADER_SRC/formats/html/HtmlReaderStream.cpp \
$$FBREADER_SRC/formats/html/HtmlEntityCollection.cpp \
$$FBREADER_SRC/formats/html/HtmlBookReader.cpp \
$$FBREADER_SRC/formats/html/HtmlDescriptionReader.cpp \
$$FBREADER_SRC/formats/html/HtmlReader.cpp \
$$FBREADER_SRC/formats/chm/CHMFileImage.cpp \
$$FBREADER_SRC/formats/chm/HHCReferenceCollector.cpp \
$$FBREADER_SRC/formats/chm/CHMReferenceCollection.cpp \
$$FBREADER_SRC/formats/chm/BitStream.cpp \
$$FBREADER_SRC/formats/chm/HtmlSectionReader.cpp \
$$FBREADER_SRC/formats/chm/LZXDecompressor.cpp \
$$FBREADER_SRC/formats/chm/HuffmanDecoder.cpp \
$$FBREADER_SRC/formats/chm/CHMFile.cpp \
$$FBREADER_SRC/formats/chm/HHCReader.cpp \
$$FBREADER_SRC/formats/chm/CHMPlugin.cpp \
$$FBREADER_SRC/formats/chm/E8Decoder.cpp \
$$FBREADER_SRC/formats/txt/TxtReader.cpp \
$$FBREADER_SRC/formats/txt/PlainTextFormat.cpp \
$$FBREADER_SRC/formats/txt/TxtBookReader.cpp \
$$FBREADER_SRC/formats/txt/TxtPlugin.cpp \
$$FBREADER_SRC/bookmodel/BookReader.cpp \
$$FBREADER_SRC/bookmodel/BookModel.cpp \
HEADERS += \
$$FBREADER_SRC/migration/BookInfo.h \
$$FBREADER_SRC/options/FBOptions.h \
$$FBREADER_SRC/libraryActions/LibraryAuthorActions.h \
$$FBREADER_SRC/libraryActions/AuthorInfoDialog.h \
$$FBREADER_SRC/libraryActions/LibraryBookActions.h \
$$FBREADER_SRC/libraryActions/BooksUtil.h \
$$FBREADER_SRC/libraryActions/LibraryTagActions.h \
$$FBREADER_SRC/fbreader/FBView.h \
$$FBREADER_SRC/fbreader/ReadingState.h \
$$FBREADER_SRC/fbreader/FBReaderActions.h \
$$FBREADER_SRC/fbreader/PreferencesPopupData.h \
$$FBREADER_SRC/fbreader/TimeUpdater.h \
$$FBREADER_SRC/fbreader/ContentsView.h \
$$FBREADER_SRC/fbreader/RecentBooksPopupData.h \
$$FBREADER_SRC/fbreader/FootnoteView.h \
$$FBREADER_SRC/fbreader/ScrollingAction.h \
$$FBREADER_SRC/fbreader/BookTextView.h \
$$FBREADER_SRC/library/Author.h \
$$FBREADER_SRC/library/Book.h \
$$FBREADER_SRC/library/Lists.h \
$$FBREADER_SRC/library/Library.h \
$$FBREADER_SRC/library/Tag.h \
$$FBREADER_SRC/formats/dummy/DummyBookReader.h \
$$FBREADER_SRC/formats/dummy/DummyMetaInfoReader.h \
$$FBREADER_SRC/formats/fb2/FB2Plugin.h \
$$FBREADER_SRC/formats/fb2/FB2MetaInfoReader.h \
$$FBREADER_SRC/formats/fb2/FB2TagManager.h \
$$FBREADER_SRC/formats/fb2/FB2BookReader.h \
$$FBREADER_SRC/formats/fb2/FB2Reader.h \
$$FBREADER_SRC/formats/fb2/FB2CoverReader.h \
$$FBREADER_SRC/formats/openreader/OpenReaderPlugin.h \
$$FBREADER_SRC/formats/openreader/ORDescriptionReader.h \
$$FBREADER_SRC/formats/openreader/ORBookReader.h \
$$FBREADER_SRC/formats/EncodedTextReader.h \
$$FBREADER_SRC/formats/util/TextFormatDetector.h \
$$FBREADER_SRC/formats/util/MergedStream.h \
$$FBREADER_SRC/formats/util/MiscUtil.h \
$$FBREADER_SRC/formats/util/EntityFilesCollector.h \
$$FBREADER_SRC/formats/util/XMLTextStream.h \
$$FBREADER_SRC/formats/css/StyleSheetParser.h \
$$FBREADER_SRC/formats/css/StyleSheetTable.h \
$$FBREADER_SRC/formats/tcr/TcrStream.h \
$$FBREADER_SRC/formats/tcr/TcrPlugin.h \
$$FBREADER_SRC/formats/tcr/PPLBookReader.h \
$$FBREADER_SRC/formats/rtf/RtfPlugin.h \
$$FBREADER_SRC/formats/rtf/RtfBookReader.h \
$$FBREADER_SRC/formats/rtf/RtfReader.h \
$$FBREADER_SRC/formats/rtf/RtfReaderStream.h \
$$FBREADER_SRC/formats/rtf/RtfImage.h \
$$FBREADER_SRC/formats/rtf/RtfDescriptionReader.h \
$$FBREADER_SRC/formats/FormatPlugin.h \
$$FBREADER_SRC/formats/pdb/ZTXTStream.h \
$$FBREADER_SRC/formats/pdb/PalmDocStream.h \
$$FBREADER_SRC/formats/pdb/PdbReader.h \
$$FBREADER_SRC/formats/pdb/PmlBookReader.h \
$$FBREADER_SRC/formats/pdb/PdbStream.h \
$$FBREADER_SRC/formats/pdb/PluckerImages.h \
$$FBREADER_SRC/formats/pdb/PalmDocLikeStream.h \
$$FBREADER_SRC/formats/pdb/HtmlMetainfoReader.h \
$$FBREADER_SRC/formats/pdb/MobipocketHtmlBookReader.h \
$$FBREADER_SRC/formats/pdb/BitReader.h \
$$FBREADER_SRC/formats/pdb/HuffDecompressor.h \
$$FBREADER_SRC/formats/pdb/DocDecompressor.h \
$$FBREADER_SRC/formats/pdb/PmlReader.h \
$$FBREADER_SRC/formats/pdb/PluckerBookReader.h \
$$FBREADER_SRC/formats/pdb/PdbPlugin.h \
$$FBREADER_SRC/formats/pdb/EReaderStream.h \
$$FBREADER_SRC/formats/pdb/PluckerTextStream.h \
$$FBREADER_SRC/formats/xhtml/XHTMLReader.h \
$$FBREADER_SRC/formats/oeb/OEBBookReader.h \
$$FBREADER_SRC/formats/oeb/OEBTextStream.h \
$$FBREADER_SRC/formats/oeb/OEBPlugin.h \
$$FBREADER_SRC/formats/oeb/NCXReader.h \
$$FBREADER_SRC/formats/oeb/OEBMetaInfoReader.h \
$$FBREADER_SRC/formats/oeb/OEBCoverReader.h \
$$FBREADER_SRC/formats/html/HtmlPlugin.h \
$$FBREADER_SRC/formats/html/HtmlEntityCollection.h \
$$FBREADER_SRC/formats/html/HtmlReaderStream.h \
$$FBREADER_SRC/formats/html/HtmlReader.h \
$$FBREADER_SRC/formats/html/HtmlTagActions.h \
$$FBREADER_SRC/formats/html/HtmlBookReader.h \
$$FBREADER_SRC/formats/html/HtmlDescriptionReader.h \
$$FBREADER_SRC/formats/chm/CHMPlugin.h \
$$FBREADER_SRC/formats/chm/CHMFile.h \
$$FBREADER_SRC/formats/chm/LZXDecompressor.h \
$$FBREADER_SRC/formats/chm/CHMFileImage.h \
$$FBREADER_SRC/formats/chm/HHCReferenceCollector.h \
$$FBREADER_SRC/formats/chm/HtmlSectionReader.h \
$$FBREADER_SRC/formats/chm/CHMReferenceCollection.h \
$$FBREADER_SRC/formats/chm/HuffmanDecoder.h \
$$FBREADER_SRC/formats/chm/BitStream.h \
$$FBREADER_SRC/formats/chm/HHCReader.h \
$$FBREADER_SRC/formats/txt/TxtBookReader.h \
$$FBREADER_SRC/formats/txt/PlainTextFormat.h \
$$FBREADER_SRC/formats/txt/TxtPlugin.h \
$$FBREADER_SRC/formats/txt/TxtReader.h \
$$FBREADER_SRC/bookmodel/FBTextKind.h \
$$FBREADER_SRC/bookmodel/BookModel.h \
$$FBREADER_SRC/bookmodel/BookReader.h

6
harbour-books.pro Normal file
View file

@ -0,0 +1,6 @@
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS = fribidi linebreak fbreader harbour-lib app
OTHER_FILES += \
rpm/harbour-books.spec

1
harbour-lib Submodule

@ -0,0 +1 @@
Subproject commit 24f9fe58d06dba0982aa7c3eecce461dbc3f7d6d

56
rpm/harbour-books.spec Normal file
View file

@ -0,0 +1,56 @@
Name: harbour-books
Summary: E-book reader
Version: 1.0.0
Release: 1
License: GPL
Group: Applications/File
URL: http://github.com/monich/harbour-books
Source0: %{name}-%{version}.tar.gz
Requires: sailfishsilica-qt5
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(sailfishapp)
BuildRequires: pkgconfig(Qt5Quick)
BuildRequires: pkgconfig(Qt5Core)
BuildRequires: pkgconfig(Qt5Svg)
BuildRequires: pkgconfig(Qt5Qml)
BuildRequires: pkgconfig(mlite5)
BuildRequires: pkgconfig(expat)
BuildRequires: pkgconfig(systemd)
BuildRequires: bzip2-devel
BuildRequires: desktop-file-utils
BuildRequires: qt5-qttools-linguist
%{!?qtc_qmake5:%define qtc_qmake5 %qmake5}
%{!?qtc_make:%define qtc_make make}
%{?qtc_builddir:%define _builddir %qtc_builddir}
%description
FBReader-based e-book reader.
%prep
%setup -q -n %{name}-%{version}
%build
%qtc_qmake5 harbour-books.pro
%qtc_make %{?_smp_mflags}
%install
rm -rf %{buildroot}
cd app
%qmake5_install
desktop-file-install --delete-original \
--dir %{buildroot}%{_datadir}/applications \
%{buildroot}%{_datadir}/applications/*.desktop
%files
%defattr(-,root,root,-)
%{_bindir}/%{name}
%{_datadir}/%{name}
%{_datadir}/applications/%{name}.desktop
%{_datadir}/icons/hicolor/86x86/apps/%{name}.png
%changelog
* Thu May 14 2015 Slava Monich <slava.monich@jolla.com> 1.0.0
- Initial version