diff --git a/app/qml/BooksBookView.qml b/app/qml/BooksBookView.qml index c2d647c..b58c98c 100644 --- a/app/qml/BooksBookView.qml +++ b/app/qml/BooksBookView.qml @@ -41,6 +41,7 @@ SilicaFlickable { id: root property variant book + property bool selecting signal closeBook() signal pageClicked(var page) @@ -56,7 +57,8 @@ SilicaFlickable { { pager: true, page: true, title: true, tools: true } ] - interactive: (!linkMenu || !linkMenu.visible) && + interactive: !selecting && + (!linkMenu || !linkMenu.visible) && (!imageView || !imageView.visible) && (!footnoteView || !footnoteView.visible) @@ -168,6 +170,7 @@ SilicaFlickable { } delegate: BooksPageView { + id: pageView width: bookView.width height: bookView.height model: bookModel @@ -180,15 +183,26 @@ SilicaFlickable { rightSpaceReserved: pageTools.visible ? pageTools.rightSpaceUsed: 0 titleVisible: _currentState.title pageNumberVisible: _currentState.page + currentPage: bookViewWatcher.currentIndex == index title: bookModel.title onJumpToPage: bookView.jumpTo(page) onPushPosition: stackModel.pushPosition(position) // bookView.jumpTo(page) + onCurrentPageChanged: { + if (currentPage) { + root.selecting = pageView.selecting + } + } + onSelectingChanged: { + if (currentPage) { + root.selecting = pageView.selecting + } + } onPageClicked: { root.pageClicked(index) Settings.pageDetails = (Settings.pageDetails + 1) % _visibilityStates.length } onImagePressed: { - if (bookViewWatcher.currentIndex == index) { + if (currentPage) { if (!imageView) { imageView = imageViewComponent.createObject(root) } @@ -196,7 +210,7 @@ SilicaFlickable { } } onBrowserLinkPressed: { - if (bookViewWatcher.currentIndex == index) { + if (currentPage) { if (!linkMenu) { linkMenu = linkMenuComponent.createObject(root) } diff --git a/app/qml/BooksPageView.qml b/app/qml/BooksPageView.qml index eb6f130..2a03507 100644 --- a/app/qml/BooksPageView.qml +++ b/app/qml/BooksPageView.qml @@ -43,6 +43,8 @@ Item { property alias rightMargin: widget.rightMargin property alias topMargin: widget.topMargin property alias bottomMargin: widget.bottomMargin + property alias selecting: widget.selecting + property alias currentPage: widget.currentPage property alias title: titleLabel.text property real leftSpaceReserved property real rightSpaceReserved @@ -60,6 +62,7 @@ Item { id: widget anchors.fill: parent model: bookModel + pressed: mouseArea.pressed onBrowserLinkPressed: view.browserLinkPressed(url) onImagePressed: view.imagePressed(imageId, rect) onActiveTouch: pressImage.animate(touchX, touchY) @@ -156,9 +159,17 @@ Item { } MouseArea { + id: mouseArea anchors.fill: parent - onClicked: view.pageClicked() + onClicked: { + if (widget.selectionEmpty) { + view.pageClicked() + } else { + widget.clearSelection() + } + } onPressed: widget.handlePress(mouseX, mouseY) onPressAndHold: widget.handleLongPress(mouseX, mouseY) + onPositionChanged: widget.handlePositionChanged(mouseX, mouseY) } } diff --git a/app/src/BooksPageWidget.cpp b/app/src/BooksPageWidget.cpp index 4745a7f..d693c57 100644 --- a/app/src/BooksPageWidget.cpp +++ b/app/src/BooksPageWidget.cpp @@ -42,6 +42,8 @@ #include "HarbourDebug.h" +#include +#include #include static const QString IMAGE_URL("image://%1/%2"); @@ -136,7 +138,7 @@ void BooksPageWidget::ResetTask::performTask() class BooksPageWidget::RenderTask : public BooksTask { public: RenderTask(shared_ptr aData, int aWidth, int aHeight) : - iData(aData), iWidth(aWidth), iHeight(aHeight), iImage(NULL) {} + iData(aData), iWidth(aWidth), iHeight(aHeight) {} void performTask(); @@ -159,6 +161,119 @@ void BooksPageWidget::RenderTask::performTask() } } +// ========================================================================== +// BooksPageWidget::ClearSelectionTask +// ========================================================================== + +class BooksPageWidget::ClearSelectionTask : public BooksTask { +public: + ClearSelectionTask(shared_ptr aData, int aWidth, + int aHeight) : iData(aData), iWidth(aWidth), iHeight(aHeight), + iImageUpdated(false) {} + + void performTask(); + +public: + shared_ptr iData; + int iWidth; + int iHeight; + QImage iImage; + bool iImageUpdated; +}; + +void BooksPageWidget::ClearSelectionTask::performTask() +{ + iData->iView->endSelection(); + if (!isCanceled() && iWidth > 0 && iHeight > 0) { + const ZLTextArea& area = iData->iView->textArea(); + if (!area.selectionIsEmpty()) { + area.clearSelection(); + iImage = QImage(iWidth, iHeight, QImage::Format_RGB32); + if (!isCanceled()) { + QPainter painter(&iImage); + iData->paint(&painter); + iImageUpdated = true; + } + } + } +} + +// ========================================================================== +// BooksPageWidget::StartSelectionTask +// ========================================================================== + +class BooksPageWidget::StartSelectionTask : public BooksTask { +public: + StartSelectionTask(shared_ptr aData, int aX, int aY, + int aWidth, int aHeight) : iData(aData), iX(aX), iY(aY), + iWidth(aWidth), iHeight(aHeight), iSelectionEmpty(true) {} + + void performTask(); + +public: + shared_ptr iData; + int iX; + int iY; + int iWidth; + int iHeight; + QImage iImage; + bool iSelectionEmpty; +}; + +void BooksPageWidget::StartSelectionTask::performTask() +{ + if (!isCanceled() && iWidth > 0 && iHeight > 0) { + iData->iView->startSelection(iX, iY); + iSelectionEmpty = iData->iView->textArea().selectionIsEmpty(); + if (!isCanceled()) { + iImage = QImage(iWidth, iHeight, QImage::Format_RGB32); + if (!isCanceled()) { + QPainter painter(&iImage); + iData->paint(&painter); + } + } + } +} + +// ========================================================================== +// BooksPageWidget::ExtendSelectionTask +// ========================================================================== + +class BooksPageWidget::ExtendSelectionTask : public BooksTask { +public: + ExtendSelectionTask(shared_ptr aData, int aX, int aY, + int aWidth, int aHeight) : iData(aData), iX(aX), iY(aY), + iWidth(aWidth), iHeight(aHeight), iSelectionChanged(false), + iSelectionEmpty(true) {} + + void performTask(); + +public: + shared_ptr iData; + int iX; + int iY; + int iWidth; + int iHeight; + QImage iImage; + bool iSelectionChanged; + bool iSelectionEmpty; +}; + +void BooksPageWidget::ExtendSelectionTask::performTask() +{ + if (!isCanceled() && iWidth > 0 && iHeight > 0) { + iSelectionChanged = iData->iView->extendSelection(iX, iY); + iSelectionEmpty = iData->iView->textArea().selectionIsEmpty(); + if (iSelectionChanged && !isCanceled()) { + iImage = QImage(iWidth, iHeight, QImage::Format_RGB32); + if (!isCanceled()) { + QPainter painter(&iImage); + iData->paint(&painter); + } + } + } +} + // ========================================================================== // BooksPageWidget::FootnoteTask // ========================================================================== @@ -380,10 +495,16 @@ BooksPageWidget::BooksPageWidget(QQuickItem* aParent) : iModel(NULL), iResetTask(NULL), iRenderTask(NULL), + iClearSelectionTask(NULL), + iStartSelectionTask(NULL), iPressTask(NULL), iLongPressTask(NULL), iFootnoteTask(NULL), iEmpty(false), + iPressed(false), + iSelecting(false), + iSelectionEmpty(true), + iCurrentPage(false), iPage(-1) { connect(iSettings.data(), @@ -403,13 +524,48 @@ BooksPageWidget::BooksPageWidget(QQuickItem* aParent) : BooksPageWidget::~BooksPageWidget() { HDEBUG("page" << iPage); + releaseExtendSelectionTasks(); if (iResetTask) iResetTask->release(this); if (iRenderTask) iRenderTask->release(this); + if (iClearSelectionTask) iClearSelectionTask->release(this); + if (iStartSelectionTask) iStartSelectionTask->release(this); if (iPressTask) iPressTask->release(this); if (iLongPressTask) iLongPressTask->release(this); if (iFootnoteTask) iFootnoteTask->release(this); } +void BooksPageWidget::releaseExtendSelectionTasks() +{ + while (!iExtendSelectionTasks.isEmpty()) { + const int i = iExtendSelectionTasks.count()-1; + iExtendSelectionTasks.at(i)->release(this); + iExtendSelectionTasks.removeAt(i); + } +} + +void BooksPageWidget::setPressed(bool aPressed) +{ + if (iPressed != aPressed) { + iPressed = aPressed; + if (!iPressed && iSelecting) { + HDEBUG("leaving selection mode"); + iSelecting = false; + Q_EMIT selectingChanged(); + } + Q_EMIT pressedChanged(); + } +} + +void BooksPageWidget::setCurrentPage(bool aCurrentPage) +{ + if (iCurrentPage != aCurrentPage) { + iCurrentPage = aCurrentPage; + HDEBUG(iCurrentPage); + if (!iCurrentPage) clearSelection(); + Q_EMIT currentPageChanged(); + } +} + void BooksPageWidget::setModel(BooksBookModel* aModel) { if (iModel != aModel) { @@ -582,7 +738,7 @@ void BooksPageWidget::paint(QPainter* aPainter) bool BooksPageWidget::loading() const { - return iPage >= 0 && (iResetTask || iRenderTask); + return iPage >= 0 && iImage.isNull() && (iResetTask || iRenderTask); } void BooksPageWidget::resetView() @@ -680,6 +836,75 @@ void BooksPageWidget::onPressTaskDone() task->release(this); } +void BooksPageWidget::onClearSelectionTaskDone() +{ + HASSERT(sender() == iClearSelectionTask); + ClearSelectionTask* task = iClearSelectionTask; + iClearSelectionTask = NULL; + + if (!iSelectionEmpty) { + iSelectionEmpty = true; + HDEBUG("selection cleared"); + Q_EMIT selectionEmptyChanged(); + } + + if (task->iImageUpdated) { + iImage = task->iImage; + update(); + } + + task->release(this); +} + +void BooksPageWidget::onStartSelectionTaskDone() +{ + HASSERT(sender() == iStartSelectionTask); + StartSelectionTask* task = iStartSelectionTask; + iStartSelectionTask = NULL; + + if (iPressed) { + iImage = task->iImage; + + // Emit signals when we are in a consistent state + bool emitSelectionEmpty; + if (iSelectionEmpty != task->iSelectionEmpty) { + iSelectionEmpty = task->iSelectionEmpty; + HDEBUG("selection" << iSelectionEmpty); + emitSelectionEmpty = true; + } + if (!iSelecting) { + iSelecting = true; + HDEBUG("entering selection mode"); + Q_EMIT selectingChanged(); + } + if (emitSelectionEmpty) { + Q_EMIT selectionEmptyChanged(); + } + update(); + } + + task->release(this); +} + +void BooksPageWidget::onExtendSelectionTaskDone() +{ + ExtendSelectionTask* task = (ExtendSelectionTask*)sender(); + HASSERT(iExtendSelectionTasks.contains(task)); + iExtendSelectionTasks.removeOne(task); + + if (iSelecting && task->iSelectionChanged) { + iImage = task->iImage; + if (iSelectionEmpty != task->iSelectionEmpty) { + iSelectionEmpty = task->iSelectionEmpty; + HDEBUG("selection" << iSelectionEmpty); + Q_EMIT selectionEmptyChanged(); + } + update(); + } + + task->release(this); +} + void BooksPageWidget::onFootnoteTaskDone() { HASSERT(sender() == iFootnoteTask); @@ -769,6 +994,12 @@ void BooksPageWidget::onLongPressTaskDone() BooksImageProvider::instance()->addImage(iModel, id, task->iImage); Q_EMIT imagePressed(IMAGE_URL.arg(BooksImageProvider::PROVIDER_ID, id), task->iRect); + } else if (!iData.isNull()) { + if (iStartSelectionTask) iStartSelectionTask->release(this); + iStartSelectionTask = new StartSelectionTask(iData, + task->iX, task->iY, width(), height()); + iTaskQueue->submit(iStartSelectionTask, this, + SLOT(onStartSelectionTaskDone())); } task->release(this); @@ -813,8 +1044,8 @@ void BooksPageWidget::handleLongPress(int aX, int aY) HDEBUG(aX << aY); if (!iResetTask && !iRenderTask && !iData.isNull()) { if (iLongPressTask) iLongPressTask->release(this); - iLongPressTask = new PressTask(iData, aX, aY); - iTaskQueue->submit(iLongPressTask, this, SLOT(onLongPressTaskDone())); + iTaskQueue->submit(iLongPressTask = new PressTask(iData, aX, aY), + this, SLOT(onLongPressTaskDone())); } } @@ -823,7 +1054,50 @@ void BooksPageWidget::handlePress(int aX, int aY) HDEBUG(aX << aY); if (!iResetTask && !iRenderTask && !iData.isNull()) { if (iPressTask) iPressTask->release(this); - iPressTask = new PressTask(iData, aX, aY); - iTaskQueue->submit(iPressTask, this, SLOT(onPressTaskDone())); + iTaskQueue->submit(iPressTask = new PressTask(iData, aX, aY), + this, SLOT(onPressTaskDone())); + } +} + +void BooksPageWidget::handlePositionChanged(int aX, int aY) +{ + if (iSelecting && !iData.isNull()) { + HDEBUG(aX << aY); + // Drop the tasks which haven't been started yet + ExtendSelectionTask* task; + for (int i = iExtendSelectionTasks.count()-1; i>=0; i--) { + task = iExtendSelectionTasks.at(i); + if (task->isStarted()) { + break; + } else { + task->release(this); + iExtendSelectionTasks.removeAt(i); + HDEBUG("dropped queued task," << i << "left"); + } + } + task = new ExtendSelectionTask(iData, aX, aY, width(), height()); + iTaskQueue->submit(task, this, SLOT(onExtendSelectionTaskDone())); + iExtendSelectionTasks.append(task); + } else { + // Finger was moved before we entered selection mode + if (iStartSelectionTask) { + iStartSelectionTask->release(this); + iStartSelectionTask = NULL; + HDEBUG("oops"); + } + } +} + +void BooksPageWidget::clearSelection() +{ + if (!iData.isNull()) { + if (iClearSelectionTask) iClearSelectionTask->release(this); + iTaskQueue->submit(iClearSelectionTask = + new ClearSelectionTask(iData, width(), height()), + this, SLOT(onClearSelectionTaskDone())); + } + if (iSelecting) { + iSelecting = false; + Q_EMIT selectingChanged(); } } diff --git a/app/src/BooksPageWidget.h b/app/src/BooksPageWidget.h index b770159..e52444e 100644 --- a/app/src/BooksPageWidget.h +++ b/app/src/BooksPageWidget.h @@ -47,11 +47,16 @@ #include #include +#include class BooksPageWidget: public QQuickPaintedItem, private BooksLoadingProperty { Q_OBJECT Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged) + Q_PROPERTY(bool selecting READ selecting NOTIFY selectingChanged) + Q_PROPERTY(bool selectionEmpty READ selectionEmpty NOTIFY selectionEmptyChanged) + Q_PROPERTY(bool pressed READ pressed WRITE setPressed NOTIFY pressedChanged) + Q_PROPERTY(bool currentPage READ currentPage WRITE setCurrentPage NOTIFY currentPageChanged) 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) @@ -62,34 +67,48 @@ class BooksPageWidget: public QQuickPaintedItem, private BooksLoadingProperty public: class Data; - BooksPageWidget(QQuickItem* aParent = NULL); + explicit BooksPageWidget(QQuickItem* aParent = NULL); ~BooksPageWidget(); bool loading() const; + bool selecting() const; + bool selectionEmpty() const; - int page() const { return iPage; } + bool pressed() const; + void setPressed(bool aPressed); + + bool currentPage() const; + void setCurrentPage(bool aCurrentPage); + + int page() const; void setPage(int aPage); - BooksBookModel* model() const { return iModel; } + BooksBookModel* model() const; void setModel(BooksBookModel* 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; } + int leftMargin() const; + int rightMargin() const; + int topMargin() const; + int bottomMargin() const; void setLeftMargin(int aMargin); void setRightMargin(int aMargin); void setTopMargin(int aMargin); void setBottomMargin(int aMargin); - BooksMargins margins() const { return iMargins; } + BooksMargins margins() const; - Q_INVOKABLE void handleLongPress(int aX, int aY); Q_INVOKABLE void handlePress(int aX, int aY); + Q_INVOKABLE void handleLongPress(int aX, int aY); + Q_INVOKABLE void handlePositionChanged(int aX, int aY); + Q_INVOKABLE void clearSelection(); Q_SIGNALS: void loadingChanged(); + void pressedChanged(); + void selectingChanged(); + void selectionEmptyChanged(); + void currentPageChanged(); void pageChanged(); void modelChanged(); void leftMarginChanged(); @@ -115,6 +134,9 @@ private Q_SLOTS: void onInvertColorsChanged(); void onResetTaskDone(); void onRenderTaskDone(); + void onClearSelectionTaskDone(); + void onStartSelectionTaskDone(); + void onExtendSelectionTaskDone(); void onPressTaskDone(); void onLongPressTaskDone(); void onFootnoteTaskDone(); @@ -123,6 +145,7 @@ private: void paint(QPainter *painter); void updateSize(); void resetView(); + void releaseExtendSelectionTasks(); void scheduleRepaint(); void cancelRepaint(); bool invertColors() const; @@ -131,6 +154,9 @@ private: class ResetTask; class RenderTask; class PressTask; + class ClearSelectionTask; + class StartSelectionTask; + class ExtendSelectionTask; class FootnoteTask; QSharedPointer iSettings; @@ -144,13 +170,42 @@ private: QImage iImage; ResetTask* iResetTask; RenderTask* iRenderTask; + ClearSelectionTask* iClearSelectionTask; + StartSelectionTask* iStartSelectionTask; + QList iExtendSelectionTasks; PressTask* iPressTask; PressTask* iLongPressTask; FootnoteTask* iFootnoteTask; bool iEmpty; + bool iPressed; + bool iSelecting; + bool iSelectionEmpty; + bool iCurrentPage; int iPage; }; +inline bool BooksPageWidget::pressed() const + { return iPressed; } +inline bool BooksPageWidget::selecting() const + { return iSelecting; } +inline bool BooksPageWidget::selectionEmpty() const + { return iSelectionEmpty; } +inline bool BooksPageWidget::currentPage() const + { return iCurrentPage; } +inline int BooksPageWidget::page() const + { return iPage; } +inline BooksBookModel* BooksPageWidget::model() const + { return iModel; } +inline int BooksPageWidget::leftMargin() const + { return iMargins.iLeft; } +inline int BooksPageWidget::rightMargin() const + { return iMargins.iRight; } +inline int BooksPageWidget::topMargin() const + { return iMargins.iTop; } +inline int BooksPageWidget::bottomMargin() const + { return iMargins.iBottom; } +inline BooksMargins BooksPageWidget::margins() const + { return iMargins; } inline bool BooksPageWidget::invertColors() const { return iSettings && iSettings->invertColors(); } diff --git a/app/src/BooksTextView.cpp b/app/src/BooksTextView.cpp index 90025a7..513a3d0 100644 --- a/app/src/BooksTextView.cpp +++ b/app/src/BooksTextView.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015-2016 Jolla Ltd. + * Copyright (C) 2015-2017 Jolla Ltd. * Contact: Slava Monich * * You may use this file under the terms of the BSD license as follows: @@ -14,7 +14,7 @@ * 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 + * * Neither the name of 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. * @@ -34,6 +34,8 @@ #include "BooksTextView.h" #include "BooksTextStyle.h" +#include "ZLTextSelectionModel.h" + #define SUPER ZLTextView const ZLColor BooksTextView::DEFAULT_BACKGROUND(255, 255, 255); @@ -97,12 +99,12 @@ shared_ptr BooksTextView::baseStyle() const bool BooksTextView::isSelectionEnabled() const { - return false; + return true; } int BooksTextView::doubleClickDelay() const { - return isSelectionEnabled() ? 200 : 0; + return 0; } shared_ptr BooksTextView::indicatorInfo() const @@ -140,3 +142,22 @@ void BooksTextView::gotoPosition(const BooksPos& aPos) SUPER::gotoPosition(aPos.iParagraphIndex, aPos.iElementIndex, aPos.iCharIndex); } + +void BooksTextView::startSelection(int aX, int aY) +{ + if (!selectionModel().selectWord(textArea().realX(aX), aY, true)) { + // There's no text where we have clicked + activateSelection(aX, aY); + } +} + +bool BooksTextView::extendSelection(int aX, int aY) +{ + return selectionModel().extendTo(textArea().realX(aX), aY, true) == + ZLTextSelectionModel::BOUND_CHANGED; +} + +void BooksTextView::endSelection() +{ + selectionModel().deactivate(); +} diff --git a/app/src/BooksTextView.h b/app/src/BooksTextView.h index fdd3076..c012e3a 100644 --- a/app/src/BooksTextView.h +++ b/app/src/BooksTextView.h @@ -1,35 +1,35 @@ /* - Copyright (C) 2015 Jolla Ltd. - Contact: Slava Monich - - 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. -*/ + * Copyright (C) 2015-2017 Jolla Ltd. + * Contact: Slava Monich + * + * 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 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 + * 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 @@ -57,6 +57,9 @@ public: public: BooksPos position() const; const BooksPos rewind(); + void startSelection(int aX, int aY); + bool extendSelection(int aX, int aY); + void endSelection(); void setInvertColors(bool aInvertColors); void gotoPosition(const BooksPos& aPos); bool nextPage();