pageview.cpp 212 KB
Newer Older
1
2
3
/*
    SPDX-FileCopyrightText: 2004-2005 Enrico Ros <eros.kde@email.it>
    SPDX-FileCopyrightText: 2004-2006 Albert Astals Cid <aacid@kde.org>
4
5

    Work sponsored by the LiMux project of the city of Munich:
6
    SPDX-FileCopyrightText: 2017 Klarälvdalens Datakonsult AB a KDAB Group company <info@kdab.com>
7
8
9

    With portions of code from kpdf/kpdf_pagewidget.cc by:
    SPDX-FileCopyrightText: 2002 Wilco Greven <greven@kde.org>
10
    SPDX-FileCopyrightText: 2003 Christophe Devriese <Christophe.Devriese@student.kuleuven.ac.be>
11
12
13
14
15
16
17
    SPDX-FileCopyrightText: 2003 Laurent Montel <montel@kde.org>
    SPDX-FileCopyrightText: 2003 Dirk Mueller <mueller@kde.org>
    SPDX-FileCopyrightText: 2004 James Ots <kde@jamesots.com>
    SPDX-FileCopyrightText: 2011 Jiri Baum - NICTA <jiri@baum.com.au>

    SPDX-License-Identifier: GPL-2.0-or-later
*/
18

Albert Astals Cid's avatar
Albert Astals Cid committed
19
20
#include "pageview.h"

21
// qt/kde includes
Albert Astals Cid's avatar
Albert Astals Cid committed
22
23
#include <QApplication>
#include <QClipboard>
24
#include <QCursor>
Albert Astals Cid's avatar
Albert Astals Cid committed
25
#include <QDesktopServices>
26
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
Albert Astals Cid's avatar
Albert Astals Cid committed
27
#include <QDesktopWidget>
28
#endif
Albert Astals Cid's avatar
Albert Astals Cid committed
29
#include <QElapsedTimer>
30
#include <QEvent>
Albert Astals Cid's avatar
Albert Astals Cid committed
31
#include <QGestureEvent>
32
#include <QImage>
33
#include <QInputDialog>
Albert Astals Cid's avatar
Albert Astals Cid committed
34
35
#include <QLoggingCategory>
#include <QMenu>
Olivier Churlaud's avatar
Olivier Churlaud committed
36
#include <QMimeData>
Albert Astals Cid's avatar
Albert Astals Cid committed
37
38
39
#include <QMimeDatabase>
#include <QPainter>
#include <QScrollBar>
40
41
#include <QScroller>
#include <QScrollerProperties>
Albert Astals Cid's avatar
Albert Astals Cid committed
42
43
44
#include <QSet>
#include <QTimer>
#include <QToolTip>
45

46
#include <KActionCollection>
Albert Astals Cid's avatar
Albert Astals Cid committed
47
#include <KActionMenu>
48
#include <KConfigWatcher>
Lukáš Tinkl's avatar
Lukáš Tinkl committed
49
#include <KLocalizedString>
Albert Astals Cid's avatar
Albert Astals Cid committed
50
51
#include <KMessageBox>
#include <KRun>
52
#include <KSelectAction>
Albert Astals Cid's avatar
Albert Astals Cid committed
53
54
#include <KStandardAction>
#include <KStringHandler>
55
#include <KToggleAction>
Albert Astals Cid's avatar
Albert Astals Cid committed
56
57
58
#include <KToolInvocation>
#include <KUriFilter>
#include <QAction>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
59
#include <QDebug>
Laurent Montel's avatar
Laurent Montel committed
60
#include <QIcon>
61
#include <kwidgetsaddons_version.h>
Albert Astals Cid's avatar
compile    
Albert Astals Cid committed
62

63
// system includes
Albert Astals Cid's avatar
Albert Astals Cid committed
64
#include <array>
65
#include <math.h>
66
#include <stdlib.h>
67

68
// local includes
Albert Astals Cid's avatar
Albert Astals Cid committed
69
70
#include "annotationpopup.h"
#include "annotwindow.h"
David Hurka's avatar
David Hurka committed
71
#include "colormodemenu.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
72
#include "core/annotations.h"
73
#include "cursorwraphelper.h"
74
#include "debug_ui.h"
75
#include "formwidgets.h"
Pino Toscano's avatar
Pino Toscano committed
76
#include "guiutils.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
77
78
#include "okmenutitle.h"
#include "pagepainter.h"
79
#include "pageviewannotator.h"
80
#include "pageviewmouseannotation.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
81
#include "pageviewutils.h"
82
#include "priorities.h"
83
#include "toggleactionmenu.h"
84
#ifdef HAVE_SPEECH
85
#include "tts.h"
86
#endif
Pino Toscano's avatar
Pino Toscano committed
87
#include "core/action.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
88
#include "core/audioplayer.h"
89
#include "core/document_p.h"
90
#include "core/form.h"
91
#include "core/generator.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
92
#include "core/misc.h"
93
#include "core/movie.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
94
95
#include "core/page.h"
#include "core/page_p.h"
96
#include "core/sourcereference.h"
97
#include "core/tile.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
98
#include "magnifierview.h"
99
#include "settings.h"
100
#include "settings_core.h"
101
#include "url_utils.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
102
#include "videowidget.h"
103

Albert Astals Cid's avatar
Albert Astals Cid committed
104
static const int pageflags = PagePainter::Accessibility | PagePainter::EnhanceLinks | PagePainter::EnhanceImages | PagePainter::Highlights | PagePainter::TextSelection | PagePainter::Annotations;
105

Albert Astals Cid's avatar
Albert Astals Cid committed
106
static const std::array<float, 16> kZoomValues {0.12, 0.25, 0.33, 0.50, 0.66, 0.75, 1.00, 1.25, 1.50, 2.00, 4.00, 8.00, 16.00, 25.00, 50.00, 100.00};
Tingnan Zhang's avatar
Tingnan Zhang committed
107

108
109
110
111
112
113
// This is the length of the text that will be shown when the user is searching for a specific piece of text.
static const int searchTextPreviewLength = 21;

// When following a link, only a preview of this length will be used to set the text of the action.
static const int linkTextPreviewLength = 30;

Albert Astals Cid's avatar
Albert Astals Cid committed
114
static inline double normClamp(double value, double def)
115
{
Albert Astals Cid's avatar
Albert Astals Cid committed
116
    return (value < 0.0 || value > 1.0) ? def : value;
117
118
}

Jiri Baum's avatar
Jiri Baum committed
119
struct TableSelectionPart {
Albert Astals Cid's avatar
Albert Astals Cid committed
120
    PageViewItem *item;
Jiri Baum's avatar
Jiri Baum committed
121
122
123
    Okular::NormalizedRect rectInItem;
    Okular::NormalizedRect rectInSelection;

Albert Astals Cid's avatar
Albert Astals Cid committed
124
    TableSelectionPart(PageViewItem *item_p, const Okular::NormalizedRect &rectInItem_p, const Okular::NormalizedRect &rectInSelection_p);
Jiri Baum's avatar
Jiri Baum committed
125
126
};

Albert Astals Cid's avatar
Albert Astals Cid committed
127
128
129
130
TableSelectionPart::TableSelectionPart(PageViewItem *item_p, const Okular::NormalizedRect &rectInItem_p, const Okular::NormalizedRect &rectInSelection_p)
    : item(item_p)
    , rectInItem(rectInItem_p)
    , rectInSelection(rectInSelection_p)
Jiri Baum's avatar
Jiri Baum committed
131
132
133
{
}

134
135
136
137
// structure used internally by PageView for data storage
class PageViewPrivate
{
public:
Albert Astals Cid's avatar
Albert Astals Cid committed
138
    PageViewPrivate(PageView *qq);
139

Albert Astals Cid's avatar
Albert Astals Cid committed
140
    FormWidgetsController *formWidgetsController();
141
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
142
    OkularTTS *tts();
143
#endif
144
    QString selectedText() const;
145

146
    // the document, pageviewItems and the 'visible cache'
147
    PageView *q;
Albert Astals Cid's avatar
Albert Astals Cid committed
148
149
150
    Okular::Document *document;
    QVector<PageViewItem *> items;
    QLinkedList<PageViewItem *> visibleItems;
Michal Humpula's avatar
Michal Humpula committed
151
    MagnifierView *magnifierView;
152

153
    // view layout (columns in Settings), zoom and mouse
154
155
    PageView::ZoomMode zoomMode;
    float zoomFactor;
156
    QPoint mouseGrabOffset;
157
    QPoint mousePressPos;
158
    QPoint mouseSelectPos;
159
    QPoint previousMouseMovePos;
160
    int mouseMidLastY;
161
    bool mouseSelecting;
162
    QRect mouseSelectionRect;
163
    QColor mouseSelectionColor;
164
    bool mouseTextSelecting;
Albert Astals Cid's avatar
Albert Astals Cid committed
165
    QSet<int> pagesWithTextSelection;
166
    bool mouseOnRect;
167
    int mouseMode;
Albert Astals Cid's avatar
Albert Astals Cid committed
168
    MouseAnnotation *mouseAnnotation;
169

Jiri Baum's avatar
Jiri Baum committed
170
171
172
173
    // table selection
    QList<double> tableSelectionCols;
    QList<double> tableSelectionRows;
    QList<TableSelectionPart> tableSelectionParts;
174
    bool tableDividersGuessed;
Jiri Baum's avatar
Jiri Baum committed
175

176
177
178
    int lastSourceLocationViewportPageNumber;
    double lastSourceLocationViewportNormalizedX;
    double lastSourceLocationViewportNormalizedY;
179
    int controlWheelAccumulatedDelta;
180
181

    // for everything except PgUp/PgDn and scroll to arbitrary locations
182
183
    const int baseShortScrollDuration = 100;
    int currentShortScrollDuration;
184
    // for PgUp/PgDn and scroll to arbitrary locations
185
186
    const int baseLongScrollDuration = baseShortScrollDuration * 2;
    int currentLongScrollDuration;
187

Enrico Ros's avatar
Enrico Ros committed
188
    // auto scroll
189
    int scrollIncrement;
Albert Astals Cid's avatar
Albert Astals Cid committed
190
    QTimer *autoScrollTimer;
191
    // annotations
Albert Astals Cid's avatar
Albert Astals Cid committed
192
193
194
    PageViewAnnotator *annotator;
    // text annotation dialogs list
    QSet<AnnotWindow *> m_annowindows;
Enrico Ros's avatar
Enrico Ros committed
195
    // other stuff
Albert Astals Cid's avatar
Albert Astals Cid committed
196
    QTimer *delayResizeEventTimer;
197
    bool dirtyLayout;
Albert Astals Cid's avatar
Albert Astals Cid committed
198
199
200
    bool blockViewport;             // prevents changes to viewport
    bool blockPixmapsRequest;       // prevent pixmap requests
    PageViewMessage *messageWindow; // in pageviewutils.h
201
    bool m_formsVisible;
202
    FormWidgetsController *formsWidgetController;
203
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
204
    OkularTTS *m_tts;
205
#endif
Albert Astals Cid's avatar
Albert Astals Cid committed
206
    QTimer *refreshTimer;
207
    QSet<int> refreshPages;
208

Jake Linder's avatar
Jake Linder committed
209
210
    // bbox state for Trim to Selection mode
    Okular::NormalizedRect trimBoundingBox;
211

212
    // infinite resizing loop prevention
213
214
    bool verticalScrollBarVisible = false;
    bool horizontalScrollBarVisible = false;
215

216
217
218
219
    // drag scroll
    QPoint dragScrollVector;
    QTimer dragScrollTimer;

220
221
222
    // left click depress
    QTimer leftClickTimer;

223
    // actions
Albert Astals Cid's avatar
Albert Astals Cid committed
224
225
226
227
228
    QAction *aRotateClockwise;
    QAction *aRotateCounterClockwise;
    QAction *aRotateOriginal;
    KActionMenu *aTrimMode;
    KToggleAction *aTrimMargins;
David Hurka's avatar
David Hurka committed
229
    KToggleAction *aReadingDirection;
Albert Astals Cid's avatar
Albert Astals Cid committed
230
    QAction *aMouseNormal;
231
    QAction *aMouseZoom;
Albert Astals Cid's avatar
Albert Astals Cid committed
232
233
234
235
236
    QAction *aMouseSelect;
    QAction *aMouseTextSelect;
    QAction *aMouseTableSelect;
    QAction *aMouseMagnifier;
    KToggleAction *aTrimToSelection;
Albert Astals Cid's avatar
Albert Astals Cid committed
237
    QAction *aSignature;
Albert Astals Cid's avatar
Albert Astals Cid committed
238
239
240
241
242
243
244
245
246
    KSelectAction *aZoom;
    QAction *aZoomIn;
    QAction *aZoomOut;
    QAction *aZoomActual;
    KToggleAction *aZoomFitWidth;
    KToggleAction *aZoomFitPage;
    KToggleAction *aZoomAutoFit;
    KActionMenu *aViewModeMenu;
    QActionGroup *viewModeActionGroup;
David Hurka's avatar
David Hurka committed
247
    ColorModeMenu *aColorModeMenu;
Albert Astals Cid's avatar
Albert Astals Cid committed
248
249
250
251
252
253
254
255
256
257
258
    KToggleAction *aViewContinuous;
    QAction *aPrevAction;
    KToggleAction *aToggleForms;
    QAction *aSpeakDoc;
    QAction *aSpeakPage;
    QAction *aSpeakStop;
    QAction *aSpeakPauseResume;
    KActionCollection *actionCollection;
    QActionGroup *mouseModeActionGroup;
    ToggleActionMenu *aMouseModeMenu;
    QAction *aFitWindowToPage;
259

260
    int setting_viewCols;
261
    bool rtl_Mode;
262
263
    // Keep track of whether tablet pen is currently pressed down
    bool penDown;
264
265

    // Keep track of mouse over link object
Albert Astals Cid's avatar
Albert Astals Cid committed
266
    const Okular::ObjectRect *mouseOverLinkObject;
267

Albert Astals Cid's avatar
Albert Astals Cid committed
268
    QScroller *scroller;
269
270
};

Albert Astals Cid's avatar
Albert Astals Cid committed
271
272
PageViewPrivate::PageViewPrivate(PageView *qq)
    : q(qq)
273
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
274
    , m_tts(nullptr)
275
#endif
276
277
278
{
}

Albert Astals Cid's avatar
Albert Astals Cid committed
279
FormWidgetsController *PageViewPrivate::formWidgetsController()
280
{
Albert Astals Cid's avatar
Albert Astals Cid committed
281
282
283
284
285
286
287
288
    if (!formsWidgetController) {
        formsWidgetController = new FormWidgetsController(document);
        QObject::connect(formsWidgetController, &FormWidgetsController::changed, q, &PageView::slotFormChanged);
        QObject::connect(formsWidgetController, &FormWidgetsController::action, q, &PageView::slotAction);
        QObject::connect(formsWidgetController, &FormWidgetsController::formatAction, q, [this](const Okular::Action *action, Okular::FormFieldText *fft) { document->processFormatAction(action, fft); });
        QObject::connect(formsWidgetController, &FormWidgetsController::keystrokeAction, q, [this](const Okular::Action *action, Okular::FormFieldText *fft, bool &ok) { document->processKeystrokeAction(action, fft, ok); });
        QObject::connect(formsWidgetController, &FormWidgetsController::focusAction, q, [this](const Okular::Action *action, Okular::FormFieldText *fft) { document->processFocusAction(action, fft); });
        QObject::connect(formsWidgetController, &FormWidgetsController::validateAction, q, [this](const Okular::Action *action, Okular::FormFieldText *fft, bool &ok) { document->processValidateAction(action, fft, ok); });
289
290
291
292
293
    }

    return formsWidgetController;
}

294
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
295
OkularTTS *PageViewPrivate::tts()
296
{
Albert Astals Cid's avatar
Albert Astals Cid committed
297
298
299
300
    if (!m_tts) {
        m_tts = new OkularTTS(q);
        if (aSpeakStop) {
            QObject::connect(m_tts, &OkularTTS::canPauseOrResume, aSpeakStop, &QAction::setEnabled);
301
        }
302

Albert Astals Cid's avatar
Albert Astals Cid committed
303
304
        if (aSpeakPauseResume) {
            QObject::connect(m_tts, &OkularTTS::canPauseOrResume, aSpeakPauseResume, &QAction::setEnabled);
305
        }
306
307
308
309
    }

    return m_tts;
}
310
#endif
311

312
313
314
315
/* PageView. What's in this file? -> quick overview.
 * Code weight (in rows) and meaning:
 *  160 - constructor and creating actions plus their connected slots (empty stuff)
 *  70  - DocumentObserver inherited methodes (important)
316
 *  550 - events: mouse, keyboard, drag
317
 *  170 - slotRelayoutPages: set contents of the scrollview on continuous/single modes
318
 *  100 - zoom: zooming pages in different ways, keeping update the toolbar actions, etc..
319
 *  other misc functions: only slotRequestVisiblePixmaps and pickItemOnPoint noticeable,
320
 * and many insignificant stuff like this comment :-)
321
 */
Albert Astals Cid's avatar
Albert Astals Cid committed
322
323
324
PageView::PageView(QWidget *parent, Okular::Document *document)
    : QAbstractScrollArea(parent)
    , Okular::View(QStringLiteral("PageView"))
325
326
{
    // create and initialize private storage structure
Albert Astals Cid's avatar
Albert Astals Cid committed
327
    d = new PageViewPrivate(this);
328
    d->document = document;
329
330
331
    d->aRotateClockwise = nullptr;
    d->aRotateCounterClockwise = nullptr;
    d->aRotateOriginal = nullptr;
332
    d->aViewModeMenu = nullptr;
333
    d->zoomMode = PageView::ZoomFitWidth;
334
    d->zoomFactor = 1.0;
335
    d->mouseSelecting = false;
336
    d->mouseTextSelecting = false;
337
    d->mouseOnRect = false;
338
    d->mouseMode = Okular::Settings::mouseMode();
Albert Astals Cid's avatar
Albert Astals Cid committed
339
    d->mouseAnnotation = new MouseAnnotation(this, document);
340
    d->tableDividersGuessed = false;
341
342
343
    d->lastSourceLocationViewportPageNumber = -1;
    d->lastSourceLocationViewportNormalizedX = 0.0;
    d->lastSourceLocationViewportNormalizedY = 0.0;
344
    d->controlWheelAccumulatedDelta = 0;
345
346
    d->currentShortScrollDuration = d->baseShortScrollDuration;
    d->currentLongScrollDuration = d->baseLongScrollDuration;
347
    d->scrollIncrement = 0;
348
349
    d->autoScrollTimer = nullptr;
    d->annotator = nullptr;
350
    d->dirtyLayout = false;
351
    d->blockViewport = false;
352
    d->blockPixmapsRequest = false;
353
    d->messageWindow = new PageViewMessage(this);
354
    d->m_formsVisible = false;
355
    d->formsWidgetController = nullptr;
356
#ifdef HAVE_SPEECH
357
    d->m_tts = nullptr;
358
#endif
359
360
361
362
363
364
365
    d->refreshTimer = nullptr;
    d->aRotateClockwise = nullptr;
    d->aRotateCounterClockwise = nullptr;
    d->aRotateOriginal = nullptr;
    d->aTrimMode = nullptr;
    d->aTrimMargins = nullptr;
    d->aTrimToSelection = nullptr;
David Hurka's avatar
David Hurka committed
366
    d->aReadingDirection = nullptr;
367
    d->aMouseNormal = nullptr;
368
    d->aMouseZoom = nullptr;
369
370
    d->aMouseSelect = nullptr;
    d->aMouseTextSelect = nullptr;
371
    d->aSignature = nullptr;
372
373
374
    d->aZoomFitWidth = nullptr;
    d->aZoomFitPage = nullptr;
    d->aZoomAutoFit = nullptr;
375
    d->aViewModeMenu = nullptr;
376
    d->aViewContinuous = nullptr;
377
    d->viewModeActionGroup = nullptr;
David Hurka's avatar
David Hurka committed
378
    d->aColorModeMenu = nullptr;
379
380
381
382
383
    d->aPrevAction = nullptr;
    d->aToggleForms = nullptr;
    d->aSpeakDoc = nullptr;
    d->aSpeakPage = nullptr;
    d->aSpeakStop = nullptr;
384
    d->aSpeakPauseResume = nullptr;
385
    d->actionCollection = nullptr;
386
    d->setting_viewCols = Okular::Settings::viewColumns();
387
    d->rtl_Mode = Okular::Settings::rtlReadingDirection();
388
    d->mouseModeActionGroup = nullptr;
389
    d->aMouseModeMenu = nullptr;
390
    d->penDown = false;
391
392
    d->aMouseMagnifier = nullptr;
    d->aFitWindowToPage = nullptr;
Jake Linder's avatar
Jake Linder committed
393
    d->trimBoundingBox = Okular::NormalizedRect(); // Null box
394

Albert Astals Cid's avatar
Albert Astals Cid committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
    switch (Okular::Settings::zoomMode()) {
    case 0: {
        d->zoomFactor = 1;
        d->zoomMode = PageView::ZoomFixed;
        break;
    }
    case 1: {
        d->zoomMode = PageView::ZoomFitWidth;
        break;
    }
    case 2: {
        d->zoomMode = PageView::ZoomFitPage;
        break;
    }
    case 3: {
        d->zoomMode = PageView::ZoomFitAuto;
        break;
    }
413
414
    }

415
416
417
418
419
    connect(Okular::Settings::self(), &Okular::Settings::viewContinuousChanged, this, [=]() {
        if (d->aViewContinuous && !d->document->isOpened())
            d->aViewContinuous->setChecked(Okular::Settings::viewContinuous());
    });

Albert Astals Cid's avatar
Albert Astals Cid committed
420
421
    d->delayResizeEventTimer = new QTimer(this);
    d->delayResizeEventTimer->setSingleShot(true);
422
    d->delayResizeEventTimer->setObjectName(QStringLiteral("delayResizeEventTimer"));
Albert Astals Cid's avatar
Albert Astals Cid committed
423
    connect(d->delayResizeEventTimer, &QTimer::timeout, this, &PageView::delayedResizeEvent);
424

425
426
    setFrameStyle(QFrame::NoFrame);

Albert Astals Cid's avatar
Albert Astals Cid committed
427
    setAttribute(Qt::WA_StaticContents);
Pino Toscano's avatar
Pino Toscano committed
428

Albert Astals Cid's avatar
Albert Astals Cid committed
429
    setObjectName(QStringLiteral("okular::pageView"));
Pino Toscano's avatar
Pino Toscano committed
430

431
    // viewport setup: setup focus, and track mouse
Albert Astals Cid's avatar
Albert Astals Cid committed
432
433
434
435
436
437
    viewport()->setFocusProxy(this);
    viewport()->setFocusPolicy(Qt::StrongFocus);
    viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
    viewport()->setAttribute(Qt::WA_NoSystemBackground);
    viewport()->setMouseTracking(true);
    viewport()->setAutoFillBackground(false);
438
439
440
441
442
443

    d->scroller = QScroller::scroller(viewport());

    QScrollerProperties prop;
    prop.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.3);
    prop.setScrollMetric(QScrollerProperties::MaximumVelocity, 1);
444
    prop.setScrollMetric(QScrollerProperties::AcceleratingFlickMaximumTime, 0.2); // Workaround for QTBUG-88249 (non-flick gestures recognized as accelerating flick)
Kezi Olio's avatar
Kezi Olio committed
445
446
    prop.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
    prop.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
447
    prop.setScrollMetric(QScrollerProperties::DragStartDistance, 0.0);
448
449
    d->scroller->setScrollerProperties(prop);

kezi olio's avatar
kezi olio committed
450
451
    connect(d->scroller, &QScroller::stateChanged, this, &PageView::slotRequestVisiblePixmaps);

452
    // the apparently "magic" value of 20 is the same used internally in QScrollArea
Albert Astals Cid's avatar
Albert Astals Cid committed
453
454
455
456
    verticalScrollBar()->setCursor(Qt::ArrowCursor);
    verticalScrollBar()->setSingleStep(20);
    horizontalScrollBar()->setCursor(Qt::ArrowCursor);
    horizontalScrollBar()->setSingleStep(20);
457

458
459
    // make the smooth scroll animation durations respect the global animation
    // scale
460
461
462
463
464
465
    KConfigWatcher::Ptr animationSpeedWatcher = KConfigWatcher::create(KSharedConfig::openConfig());
    connect(animationSpeedWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) {
        if (group.name() == QLatin1String("KDE") && names.contains(QByteArrayLiteral("AnimationDurationFactor"))) {
            PageView::updateSmoothScrollAnimationSpeed();
        }
    });
466

Yuri Chornoivan's avatar
Yuri Chornoivan committed
467
    // connect the padding of the viewport to pixmaps requests
Laurent Montel's avatar
Laurent Montel committed
468
469
    connect(horizontalScrollBar(), &QAbstractSlider::valueChanged, this, &PageView::slotRequestVisiblePixmaps);
    connect(verticalScrollBar(), &QAbstractSlider::valueChanged, this, &PageView::slotRequestVisiblePixmaps);
470

471
472
473
474
475
    // Keep the scroller in sync with user input on the scrollbars.
    // QAbstractSlider::sliderMoved() and sliderReleased are the intuitive signals,
    // but are only emitted when the “slider is down”, i. e. not when the user scrolls on the scrollbar.
    // QAbstractSlider::actionTriggered() is emitted in all user input cases,
    // but before the value() changes, so we need queued connection here.
Albert Astals Cid's avatar
Albert Astals Cid committed
476
477
    auto update_scroller = [=]() {
        d->scroller->scrollTo(QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()), 0); // sync scroller with scrollbar
478
    };
479
480
    connect(verticalScrollBar(), &QAbstractSlider::actionTriggered, this, update_scroller, Qt::QueuedConnection);
    connect(horizontalScrollBar(), &QAbstractSlider::actionTriggered, this, update_scroller, Qt::QueuedConnection);
481

Albert Astals Cid's avatar
Albert Astals Cid committed
482
    connect(&d->dragScrollTimer, &QTimer::timeout, this, &PageView::slotDragScroll);
483

Albert Astals Cid's avatar
Albert Astals Cid committed
484
485
    d->leftClickTimer.setSingleShot(true);
    connect(&d->leftClickTimer, &QTimer::timeout, this, &PageView::slotShowSizeAllCursor);
486

487
    // set a corner button to resize the view to the page size
Albert Astals Cid's avatar
Albert Astals Cid committed
488
489
490
491
    //    QPushButton * resizeButton = new QPushButton( viewport() );
    //    resizeButton->setPixmap( SmallIcon("crop") );
    //    setCornerWidget( resizeButton );
    //    resizeButton->setEnabled( false );
492
    // connect(...);
Albert Astals Cid's avatar
Albert Astals Cid committed
493
    setAttribute(Qt::WA_InputMethodEnabled, true);
494

495
    // Grab pinch gestures to zoom and rotate the view
496
497
    grabGesture(Qt::PinchGesture);

Michal Humpula's avatar
Michal Humpula committed
498
499
    d->magnifierView = new MagnifierView(document, this);
    d->magnifierView->hide();
500
    d->magnifierView->setGeometry(0, 0, 351, 201); // TODO: more dynamic?
Michal Humpula's avatar
Michal Humpula committed
501

Laurent Montel's avatar
Laurent Montel committed
502
503
    connect(document, &Okular::Document::processMovieAction, this, &PageView::slotProcessMovieAction);
    connect(document, &Okular::Document::processRenditionAction, this, &PageView::slotProcessRenditionAction);
504

505
    // schedule the welcome message
506
    QMetaObject::invokeMethod(this, "slotShowWelcome", Qt::QueuedConnection);
507
508
509
510
}

PageView::~PageView()
{
511
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
512
    if (d->m_tts)
Pino Toscano's avatar
Pino Toscano committed
513
        d->m_tts->stopAllSpeechs();
514
#endif
Pino Toscano's avatar
Pino Toscano committed
515

516
    delete d->mouseAnnotation;
Pino Toscano's avatar
Pino Toscano committed
517

518
    // delete the local storage structure
519

520
521
    // We need to assign it to a different list otherwise slotAnnotationWindowDestroyed
    // will bite us and clear d->m_annowindows
Albert Astals Cid's avatar
Albert Astals Cid committed
522
    QSet<AnnotWindow *> annowindows = d->m_annowindows;
523
    d->m_annowindows.clear();
Albert Astals Cid's avatar
Albert Astals Cid committed
524
    qDeleteAll(annowindows);
525

526
    // delete all widgets
Albert Astals Cid's avatar
Albert Astals Cid committed
527
    qDeleteAll(d->items);
528
    delete d->formsWidgetController;
Albert Astals Cid's avatar
Albert Astals Cid committed
529
    d->document->removeObserver(this);
530
531
532
    delete d;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
533
void PageView::setupBaseActions(KActionCollection *ac)
534
535
536
537
{
    d->actionCollection = ac;

    // Zoom actions ( higher scales takes lots of memory! )
Albert Astals Cid's avatar
Albert Astals Cid committed
538
539
540
541
542
    d->aZoom = new KSelectAction(QIcon::fromTheme(QStringLiteral("page-zoom")), i18n("Zoom"), this);
    ac->addAction(QStringLiteral("zoom_to"), d->aZoom);
    d->aZoom->setEditable(true);
    d->aZoom->setMaxComboViewCount(kZoomValues.size() + 3);
    connect(d->aZoom, QOverload<QAction *>::of(&KSelectAction::triggered), this, &PageView::slotZoom);
543
544
    updateZoomText();

Albert Astals Cid's avatar
Albert Astals Cid committed
545
    d->aZoomIn = KStandardAction::zoomIn(this, SLOT(slotZoomIn()), ac);
546

Albert Astals Cid's avatar
Albert Astals Cid committed
547
    d->aZoomOut = KStandardAction::zoomOut(this, SLOT(slotZoomOut()), ac);
Nate Graham's avatar
Nate Graham committed
548

Albert Astals Cid's avatar
Albert Astals Cid committed
549
    d->aZoomActual = KStandardAction::actualSize(this, &PageView::slotZoomActual, ac);
Nate Graham's avatar
Nate Graham committed
550
    d->aZoomActual->setText(i18n("Zoom to 100%"));
551
552
}

Albert Astals Cid's avatar
Albert Astals Cid committed
553
void PageView::setupViewerActions(KActionCollection *ac)
554
{
555
556
    d->actionCollection = ac;

557
558
    ac->setDefaultShortcut(d->aZoomIn, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Plus));
    ac->setDefaultShortcut(d->aZoomOut, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Minus));
559

560
    // orientation menu actions
Albert Astals Cid's avatar
Albert Astals Cid committed
561
562
563
564
565
566
567
568
569
570
571
572
573
574
    d->aRotateClockwise = new QAction(QIcon::fromTheme(QStringLiteral("object-rotate-right")), i18n("Rotate &Right"), this);
    d->aRotateClockwise->setIconText(i18nc("Rotate right", "Right"));
    ac->addAction(QStringLiteral("view_orientation_rotate_cw"), d->aRotateClockwise);
    d->aRotateClockwise->setEnabled(false);
    connect(d->aRotateClockwise, &QAction::triggered, this, &PageView::slotRotateClockwise);
    d->aRotateCounterClockwise = new QAction(QIcon::fromTheme(QStringLiteral("object-rotate-left")), i18n("Rotate &Left"), this);
    d->aRotateCounterClockwise->setIconText(i18nc("Rotate left", "Left"));
    ac->addAction(QStringLiteral("view_orientation_rotate_ccw"), d->aRotateCounterClockwise);
    d->aRotateCounterClockwise->setEnabled(false);
    connect(d->aRotateCounterClockwise, &QAction::triggered, this, &PageView::slotRotateCounterClockwise);
    d->aRotateOriginal = new QAction(i18n("Original Orientation"), this);
    ac->addAction(QStringLiteral("view_orientation_original"), d->aRotateOriginal);
    d->aRotateOriginal->setEnabled(false);
    connect(d->aRotateOriginal, &QAction::triggered, this, &PageView::slotRotateOriginal);
575

Jake Linder's avatar
Jake Linder committed
576
    // Trim View actions
Albert Astals Cid's avatar
Albert Astals Cid committed
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
    d->aTrimMode = new KActionMenu(i18n("&Trim View"), this);
    d->aTrimMode->setDelayed(false);
    ac->addAction(QStringLiteral("view_trim_mode"), d->aTrimMode);

    d->aTrimMargins = new KToggleAction(QIcon::fromTheme(QStringLiteral("trim-margins")), i18n("&Trim Margins"), d->aTrimMode->menu());
    d->aTrimMode->addAction(d->aTrimMargins);
    ac->addAction(QStringLiteral("view_trim_margins"), d->aTrimMargins);
    d->aTrimMargins->setData(QVariant::fromValue((int)Okular::Settings::EnumTrimMode::Margins));
    connect(d->aTrimMargins, &QAction::toggled, this, &PageView::slotTrimMarginsToggled);
    d->aTrimMargins->setChecked(Okular::Settings::trimMargins());

    d->aTrimToSelection = new KToggleAction(QIcon::fromTheme(QStringLiteral("trim-to-selection")), i18n("Trim To &Selection"), d->aTrimMode->menu());
    d->aTrimMode->addAction(d->aTrimToSelection);
    ac->addAction(QStringLiteral("view_trim_selection"), d->aTrimToSelection);
    d->aTrimToSelection->setData(QVariant::fromValue((int)Okular::Settings::EnumTrimMode::Selection));
    connect(d->aTrimToSelection, &QAction::toggled, this, &PageView::slotTrimToSelectionToggled);

    d->aZoomFitWidth = new KToggleAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18n("Fit &Width"), this);
    ac->addAction(QStringLiteral("view_fit_to_width"), d->aZoomFitWidth);
    connect(d->aZoomFitWidth, &QAction::toggled, this, &PageView::slotFitToWidthToggled);

    d->aZoomFitPage = new KToggleAction(QIcon::fromTheme(QStringLiteral("zoom-fit-best")), i18n("Fit &Page"), this);
    ac->addAction(QStringLiteral("view_fit_to_page"), d->aZoomFitPage);
    connect(d->aZoomFitPage, &QAction::toggled, this, &PageView::slotFitToPageToggled);

    d->aZoomAutoFit = new KToggleAction(QIcon::fromTheme(QStringLiteral("zoom-fit-best")), i18n("&Auto Fit"), this);
    ac->addAction(QStringLiteral("view_auto_fit"), d->aZoomAutoFit);
    connect(d->aZoomAutoFit, &QAction::toggled, this, &PageView::slotAutoFitToggled);

    d->aFitWindowToPage = new QAction(QIcon::fromTheme(QStringLiteral("zoom-fit-width")), i18n("Fit Wi&ndow to Page"), this);
    d->aFitWindowToPage->setEnabled(Okular::Settings::viewMode() == (int)Okular::Settings::EnumViewMode::Single);
608
    ac->setDefaultShortcut(d->aFitWindowToPage, QKeySequence(Qt::CTRL | Qt::Key_J));
Albert Astals Cid's avatar
Albert Astals Cid committed
609
610
    ac->addAction(QStringLiteral("fit_window_to_page"), d->aFitWindowToPage);
    connect(d->aFitWindowToPage, &QAction::triggered, this, &PageView::slotFitWindowToPage);
Thomas Fischer's avatar
Thomas Fischer committed
611

612
    // View Mode action menu (Single Page, Facing Pages,...(choose), and Continuous (on/off))
Albert Astals Cid's avatar
Albert Astals Cid committed
613
614
615
616
617
618
619
620
621
622
623
    d->aViewModeMenu = new KActionMenu(QIcon::fromTheme(QStringLiteral("view-split-left-right")), i18n("&View Mode"), this);
    d->aViewModeMenu->setDelayed(false);
    ac->addAction(QStringLiteral("view_render_mode"), d->aViewModeMenu);

    d->viewModeActionGroup = new QActionGroup(this);
    auto addViewMode = [=](QAction *a, const QString &name, Okular::Settings::EnumViewMode::type id) {
        a->setCheckable(true);
        a->setData(int(id));
        d->aViewModeMenu->addAction(a);
        ac->addAction(name, a);
        d->viewModeActionGroup->addAction(a);
624
    };
Albert Astals Cid's avatar
Albert Astals Cid committed
625
626
627
628
629
630
631
632
633
634
    addViewMode(new QAction(QIcon::fromTheme(QStringLiteral("view-pages-single")), i18nc("@item:inmenu", "&Single Page"), this), QStringLiteral("view_render_mode_single"), Okular::Settings::EnumViewMode::Single);
    addViewMode(new QAction(QIcon::fromTheme(QStringLiteral("view-pages-facing")), i18nc("@item:inmenu", "&Facing Pages"), this), QStringLiteral("view_render_mode_facing"), Okular::Settings::EnumViewMode::Facing);
    addViewMode(new QAction(QIcon::fromTheme(QStringLiteral("view-pages-facing-first-centered")), i18nc("@item:inmenu", "Facing Pages (&Center First Page)"), this),
                QStringLiteral("view_render_mode_facing_center_first"),
                Okular::Settings::EnumViewMode::FacingFirstCentered);
    addViewMode(new QAction(QIcon::fromTheme(QStringLiteral("view-pages-overview")), i18nc("@item:inmenu", "&Overview"), this), QStringLiteral("view_render_mode_overview"), Okular::Settings::EnumViewMode::Summary);
    const QList<QAction *> viewModeActions = d->viewModeActionGroup->actions();
    for (QAction *viewModeAction : viewModeActions) {
        if (viewModeAction->data().toInt() == Okular::Settings::viewMode()) {
            viewModeAction->setChecked(true);
635
            break;
636
637
        }
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
638
    connect(d->viewModeActionGroup, &QActionGroup::triggered, this, &PageView::slotViewMode);
639

640
641
    // Continuous view action, add to view mode action menu.
    d->aViewModeMenu->addSeparator();
Albert Astals Cid's avatar
Albert Astals Cid committed
642
    d->aViewContinuous = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-pages-continuous")), i18n("&Continuous"), this);
643
    d->aViewModeMenu->addAction(d->aViewContinuous);
Albert Astals Cid's avatar
Albert Astals Cid committed
644
645
646
    ac->addAction(QStringLiteral("view_continuous"), d->aViewContinuous);
    connect(d->aViewContinuous, &QAction::toggled, this, &PageView::slotContinuousToggled);
    d->aViewContinuous->setChecked(Okular::Settings::viewContinuous());
647

David Hurka's avatar
David Hurka committed
648
649
650
651
652
653
654
    // Reading direction toggle action. (Checked means RTL, unchecked means LTR.)
    d->aReadingDirection = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-direction-rtl")), i18nc("@action page layout", "Use Right to Left Reading Direction"), this);
    d->aReadingDirection->setChecked(Okular::Settings::rtlReadingDirection());
    ac->addAction(QStringLiteral("rtl_page_layout"), d->aReadingDirection);
    connect(d->aReadingDirection, &QAction::toggled, this, &PageView::slotReadingDirectionToggled);
    connect(Okular::SettingsCore::self(), &Okular::SettingsCore::configChanged, this, &PageView::slotUpdateReadingDirectionAction);

655
    // Mouse mode actions for viewer mode
Albert Astals Cid's avatar
Albert Astals Cid committed
656
657
658
659
660
661
    d->mouseModeActionGroup = new QActionGroup(this);
    d->mouseModeActionGroup->setExclusive(true);
    d->aMouseNormal = new QAction(QIcon::fromTheme(QStringLiteral("transform-browse")), i18n("&Browse"), this);
    ac->addAction(QStringLiteral("mouse_drag"), d->aMouseNormal);
    connect(d->aMouseNormal, &QAction::toggled, this, &PageView::slotMouseNormalToggled);
    d->aMouseNormal->setCheckable(true);
662
    ac->setDefaultShortcut(d->aMouseNormal, QKeySequence(Qt::CTRL | Qt::Key_1));
Albert Astals Cid's avatar
Albert Astals Cid committed
663
664
    d->aMouseNormal->setActionGroup(d->mouseModeActionGroup);
    d->aMouseNormal->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Browse);
665

666
667
668
669
670
671
672
    d->aMouseZoom = new QAction(QIcon::fromTheme(QStringLiteral("page-zoom")), i18n("&Zoom"), this);
    ac->addAction(QStringLiteral("mouse_zoom"), d->aMouseZoom);
    connect(d->aMouseZoom, &QAction::triggered, this, &PageView::slotSetMouseZoom);
    d->aMouseZoom->setCheckable(true);
    ac->setDefaultShortcut(d->aMouseZoom, QKeySequence(Qt::CTRL | Qt::Key_2));
    d->aMouseZoom->setActionGroup(d->mouseModeActionGroup);
    d->aMouseZoom->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Zoom);
673

David Hurka's avatar
David Hurka committed
674
    d->aColorModeMenu = new ColorModeMenu(ac, this);
675
676
677
}

// WARNING: 'setupViewerActions' must have been called before this method
Albert Astals Cid's avatar
Albert Astals Cid committed
678
void PageView::setupActions(KActionCollection *ac)
679
680
681
{
    d->actionCollection = ac;

682
683
    ac->setDefaultShortcuts(d->aZoomIn, KStandardShortcut::zoomIn());
    ac->setDefaultShortcuts(d->aZoomOut, KStandardShortcut::zoomOut());
684
685

    // Mouse-Mode actions
Albert Astals Cid's avatar
Albert Astals Cid committed
686
687
688
689
    d->aMouseSelect = new QAction(QIcon::fromTheme(QStringLiteral("select-rectangular")), i18n("Area &Selection"), this);
    ac->addAction(QStringLiteral("mouse_select"), d->aMouseSelect);
    connect(d->aMouseSelect, &QAction::triggered, this, &PageView::slotSetMouseSelect);
    d->aMouseSelect->setCheckable(true);
690
    ac->setDefaultShortcut(d->aMouseSelect, Qt::CTRL | Qt::Key_3);
Albert Astals Cid's avatar
Albert Astals Cid committed
691
    d->aMouseSelect->setActionGroup(d->mouseModeActionGroup);
692

Albert Astals Cid's avatar
Albert Astals Cid committed
693
694
695
696
    d->aMouseTextSelect = new QAction(QIcon::fromTheme(QStringLiteral("edit-select-text")), i18n("&Text Selection"), this);
    ac->addAction(QStringLiteral("mouse_textselect"), d->aMouseTextSelect);
    connect(d->aMouseTextSelect, &QAction::triggered, this, &PageView::slotSetMouseTextSelect);
    d->aMouseTextSelect->setCheckable(true);
697
    ac->setDefaultShortcut(d->aMouseTextSelect, Qt::CTRL | Qt::Key_4);
Albert Astals Cid's avatar
Albert Astals Cid committed
698
    d->aMouseTextSelect->setActionGroup(d->mouseModeActionGroup);
699

Albert Astals Cid's avatar
Albert Astals Cid committed
700
701
702
703
    d->aMouseTableSelect = new QAction(QIcon::fromTheme(QStringLiteral("table")), i18n("T&able Selection"), this);
    ac->addAction(QStringLiteral("mouse_tableselect"), d->aMouseTableSelect);
    connect(d->aMouseTableSelect, &QAction::triggered, this, &PageView::slotSetMouseTableSelect);
    d->aMouseTableSelect->setCheckable(true);
704
    ac->setDefaultShortcut(d->aMouseTableSelect, Qt::CTRL | Qt::Key_5);
Albert Astals Cid's avatar
Albert Astals Cid committed
705
    d->aMouseTableSelect->setActionGroup(d->mouseModeActionGroup);
Jiri Baum's avatar
Jiri Baum committed
706

Albert Astals Cid's avatar
Albert Astals Cid committed
707
708
709
710
    d->aMouseMagnifier = new QAction(QIcon::fromTheme(QStringLiteral("document-preview")), i18n("&Magnifier"), this);
    ac->addAction(QStringLiteral("mouse_magnifier"), d->aMouseMagnifier);
    connect(d->aMouseMagnifier, &QAction::triggered, this, &PageView::slotSetMouseMagnifier);
    d->aMouseMagnifier->setCheckable(true);
711
    ac->setDefaultShortcut(d->aMouseMagnifier, Qt::CTRL | Qt::Key_6);
Albert Astals Cid's avatar
Albert Astals Cid committed
712
713
    d->aMouseMagnifier->setActionGroup(d->mouseModeActionGroup);
    d->aMouseMagnifier->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier);
Michal Humpula's avatar
Michal Humpula committed
714

715
716
717
718
719
720
721
722
    // Mouse mode selection tools menu
    d->aMouseModeMenu = new ToggleActionMenu(i18nc("@action", "Selection Tools"), this);
#if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 77, 0)
    d->aMouseModeMenu->setDelayed(false);
    d->aMouseModeMenu->setStickyMenu(false);
#else
    d->aMouseModeMenu->setPopupMode(QToolButton::MenuButtonPopup);
#endif
Albert Astals Cid's avatar
Albert Astals Cid committed
723
724
725
    d->aMouseModeMenu->addAction(d->aMouseSelect);
    d->aMouseModeMenu->addAction(d->aMouseTextSelect);
    d->aMouseModeMenu->addAction(d->aMouseTableSelect);
726
    connect(d->aMouseModeMenu->menu(), &QMenu::triggered, d->aMouseModeMenu, &ToggleActionMenu::setDefaultAction);
Albert Astals Cid's avatar
Albert Astals Cid committed
727
    ac->addAction(QStringLiteral("mouse_selecttools"), d->aMouseModeMenu);
728

729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
    switch (Okular::Settings::mouseMode()) {
    case Okular::Settings::EnumMouseMode::TextSelect:
        d->aMouseTextSelect->setChecked(true);
        d->aMouseModeMenu->setDefaultAction(d->aMouseTextSelect);
        break;
    case Okular::Settings::EnumMouseMode::RectSelect:
        d->aMouseSelect->setChecked(true);
        d->aMouseModeMenu->setDefaultAction(d->aMouseSelect);
        break;
    case Okular::Settings::EnumMouseMode::TableSelect:
        d->aMouseTableSelect->setChecked(true);
        d->aMouseModeMenu->setDefaultAction(d->aMouseTableSelect);
        break;
    default:
        d->aMouseModeMenu->setDefaultAction(d->aMouseTextSelect);
    }

    // Create signature action
747
    d->aSignature = new QAction(QIcon::fromTheme(QStringLiteral("document-edit-sign")), i18n("Digitally &Sign..."), this);
Albert Astals Cid's avatar
Albert Astals Cid committed
748
    ac->addAction(QStringLiteral("add_digital_signature"), d->aSignature);
Albert Astals Cid's avatar
Albert Astals Cid committed
749
    connect(d->aSignature, &QAction::triggered, this, &PageView::slotSignature);
Katarina Behrens's avatar
Katarina Behrens committed
750

751
    // speak actions
752
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
    d->aSpeakDoc = new QAction(QIcon::fromTheme(QStringLiteral("text-speak")), i18n("Speak Whole Document"), this);
    ac->addAction(QStringLiteral("speak_document"), d->aSpeakDoc);
    d->aSpeakDoc->setEnabled(false);
    connect(d->aSpeakDoc, &QAction::triggered, this, &PageView::slotSpeakDocument);

    d->aSpeakPage = new QAction(QIcon::fromTheme(QStringLiteral("text-speak")), i18n("Speak Current Page"), this);
    ac->addAction(QStringLiteral("speak_current_page"), d->aSpeakPage);
    d->aSpeakPage->setEnabled(false);
    connect(d->aSpeakPage, &QAction::triggered, this, &PageView::slotSpeakCurrentPage);

    d->aSpeakStop = new QAction(QIcon::fromTheme(QStringLiteral("media-playback-stop")), i18n("Stop Speaking"), this);
    ac->addAction(QStringLiteral("speak_stop_all"), d->aSpeakStop);
    d->aSpeakStop->setEnabled(false);
    connect(d->aSpeakStop, &QAction::triggered, this, &PageView::slotStopSpeaks);

    d->aSpeakPauseResume = new QAction(QIcon::fromTheme(QStringLiteral("media-playback-pause")), i18n("Pause/Resume Speaking"), this);
    ac->addAction(QStringLiteral("speak_pause_resume"), d->aSpeakPauseResume);
    d->aSpeakPauseResume->setEnabled(false);
    connect(d->aSpeakPauseResume, &QAction::triggered, this, &PageView::slotPauseResumeSpeech);
772
#else
773
774
775
776
    d->aSpeakDoc = nullptr;
    d->aSpeakPage = nullptr;
    d->aSpeakStop = nullptr;
    d->aSpeakPauseResume = nullptr;
777
#endif
778

779
    // Other actions
Albert Astals Cid's avatar
Albert Astals Cid committed
780
781
782
    QAction *su = new QAction(i18n("Scroll Up"), this);
    ac->addAction(QStringLiteral("view_scroll_up"), su);
    connect(su, &QAction::triggered, this, &PageView::slotAutoScrollUp);
783
    ac->setDefaultShortcut(su, QKeySequence(Qt::SHIFT | Qt::Key_Up));
Albert Astals Cid's avatar
Albert Astals Cid committed
784
    addAction(su);
785

Albert Astals Cid's avatar
Albert Astals Cid committed
786
787
788
    QAction *sd = new QAction(i18n("Scroll Down"), this);
    ac->addAction(QStringLiteral("view_scroll_down"), sd);
    connect(sd, &QAction::triggered, this, &PageView::slotAutoScrollDown);
789
    ac->setDefaultShortcut(sd, QKeySequence(Qt::SHIFT | Qt::Key_Down));
Albert Astals Cid's avatar
Albert Astals Cid committed
790
    addAction(sd);
791

Albert Astals Cid's avatar
Albert Astals Cid committed
792
793
794
    QAction *spu = new QAction(i18n("Scroll Page Up"), this);
    ac->addAction(QStringLiteral("view_scroll_page_up"), spu);
    connect(spu, &QAction::triggered, this, &PageView::slotScrollUp);
795
    ac->setDefaultShortcut(spu, QKeySequence(Qt::SHIFT | Qt::Key_Space));
Albert Astals Cid's avatar
Albert Astals Cid committed
796
    addAction(spu);
797

Albert Astals Cid's avatar
Albert Astals Cid committed
798
799
800
    QAction *spd = new QAction(i18n("Scroll Page Down"), this);
    ac->addAction(QStringLiteral("view_scroll_page_down"), spd);
    connect(spd, &QAction::triggered, this, &PageView::slotScrollDown);
801
    ac->setDefaultShortcut(spd, QKeySequence(Qt::Key_Space));
Albert Astals Cid's avatar
Albert Astals Cid committed
802
    addAction(spd);
803

Albert Astals Cid's avatar
Albert Astals Cid committed
804
805
806
807
808
    d->aToggleForms = new KToggleAction(i18n("Show Forms"), this);
    ac->addAction(QStringLiteral("view_toggle_forms"), d->aToggleForms);
    connect(d->aToggleForms, &QAction::toggled, this, &PageView::slotToggleForms);
    d->aToggleForms->setEnabled(false);
    toggleFormWidgets(false);
809
810

    // Setup undo and redo actions
Albert Astals Cid's avatar
Albert Astals Cid committed
811
812
    QAction *kundo = KStandardAction::create(KStandardAction::Undo, d->document, SLOT(undo()), ac);
    QAction *kredo = KStandardAction::create(KStandardAction::Redo, d->document, SLOT(redo()), ac);
Laurent Montel's avatar
Laurent Montel committed
813
814
    connect(d->document, &Okular::Document::canUndoChanged, kundo, &QAction::setEnabled);
    connect(d->document, &Okular::Document::canRedoChanged, kredo, &QAction::setEnabled);
815
816
    kundo->setEnabled(false);
    kredo->setEnabled(false);
Simone Gaiarin's avatar
Simone Gaiarin committed
817

818
819
820
821
    d->annotator = new PageViewAnnotator(this, d->document);
    connect(d->annotator, &PageViewAnnotator::toolSelected, d->aMouseNormal, &QAction::trigger);
    connect(d->annotator, &PageViewAnnotator::toolSelected, d->mouseAnnotation, &MouseAnnotation::reset);
    connect(d->annotator, &PageViewAnnotator::requestOpenFile, this, &PageView::requestOpenFile);
Albert Astals Cid's avatar
Albert Astals Cid committed
822
    d->annotator->setupActions(ac);
823
824
}

Pino Toscano's avatar
Pino Toscano committed
825
bool PageView::canFitPageWidth() const
826
{
827
    return Okular::Settings::viewMode() != Okular::Settings::EnumViewMode::Single || d->zoomMode != ZoomFitWidth;
828
829
}

Albert Astals Cid's avatar
Albert Astals Cid committed
830
void PageView::fitPageWidth(int page)
831
{