pageview.cpp 211 KB
Newer Older
1
/***************************************************************************
2
 *   Copyright (C) 2004-2005 by Enrico Ros <eros.kde@email.it>             *
Albert Astals Cid's avatar
Albert Astals Cid committed
3
 *   Copyright (C) 2004-2006 by Albert Astals Cid <aacid@kde.org>          *
4
5
6
 *   Copyright (C) 2017    Klarälvdalens Datakonsult AB, a KDAB Group      *
 *                         company, info@kdab.com. Work sponsored by the   *
 *                         LiMux project of the city of Munich             *
7
 *                                                                         *
8
 *   With portions of code from kpdf/kpdf_pagewidget.cc by:                *
9
10
11
12
13
14
 *     Copyright (C) 2002 by Wilco Greven <greven@kde.org>                 *
 *     Copyright (C) 2003 by Christophe Devriese                           *
 *                           <Christophe.Devriese@student.kuleuven.ac.be>  *
 *     Copyright (C) 2003 by Laurent Montel <montel@kde.org>               *
 *     Copyright (C) 2003 by Dirk Mueller <mueller@kde.org>                *
 *     Copyright (C) 2004 by James Ots <kde@jamesots.com>                  *
Jiri Baum's avatar
Jiri Baum committed
15
 *     Copyright (C) 2011 by Jiri Baum - NICTA <jiri@baum.com.au>          *
16
17
18
19
20
21
22
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/

Albert Astals Cid's avatar
Albert Astals Cid committed
23
24
#include "pageview.h"

25
// qt/kde includes
Albert Astals Cid's avatar
Albert Astals Cid committed
26
27
#include <QApplication>
#include <QClipboard>
28
#include <QCursor>
Albert Astals Cid's avatar
Albert Astals Cid committed
29
30
31
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QElapsedTimer>
32
#include <QEvent>
Albert Astals Cid's avatar
Albert Astals Cid committed
33
#include <QGestureEvent>
34
#include <QImage>
35
#include <QInputDialog>
Albert Astals Cid's avatar
Albert Astals Cid committed
36
37
#include <QLoggingCategory>
#include <QMenu>
Olivier Churlaud's avatar
Olivier Churlaud committed
38
#include <QMimeData>
Albert Astals Cid's avatar
Albert Astals Cid committed
39
40
41
#include <QMimeDatabase>
#include <QPainter>
#include <QScrollBar>
42
43
#include <QScroller>
#include <QScrollerProperties>
Albert Astals Cid's avatar
Albert Astals Cid committed
44
45
46
#include <QSet>
#include <QTimer>
#include <QToolTip>
47

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

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

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

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

Albert Astals Cid's avatar
Albert Astals Cid committed
105
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
106

107
108
109
110
111
112
// 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
113
static inline double normClamp(double value, double def)
114
{
Albert Astals Cid's avatar
Albert Astals Cid committed
115
    return (value < 0.0 || value > 1.0) ? def : value;
116
117
}

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

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

Albert Astals Cid's avatar
Albert Astals Cid committed
126
127
128
129
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
130
131
132
{
}

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

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

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

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

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

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

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

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

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

211
    // infinite resizing loop prevention
212
    bool verticalScrollBarVisible;
Thomas Fischer's avatar
Thomas Fischer committed
213
    bool horizontalScrollBarVisible;
214

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

219
220
221
    // left click depress
    QTimer leftClickTimer;

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

257
    int setting_viewCols;
258
    bool rtl_Mode;
259
260
    // Keep track of whether tablet pen is currently pressed down
    bool penDown;
261
262

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

Albert Astals Cid's avatar
Albert Astals Cid committed
265
    QScroller *scroller;
266
267
};

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

Albert Astals Cid's avatar
Albert Astals Cid committed
276
FormWidgetsController *PageViewPrivate::formWidgetsController()
277
{
Albert Astals Cid's avatar
Albert Astals Cid committed
278
279
280
281
282
283
284
285
    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); });
286
287
288
289
290
    }

    return formsWidgetController;
}

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

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

    return m_tts;
}
307
#endif
308

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

Albert Astals Cid's avatar
Albert Astals Cid committed
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
    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;
    }
408
409
    }

Albert Astals Cid's avatar
Albert Astals Cid committed
410
411
    d->delayResizeEventTimer = new QTimer(this);
    d->delayResizeEventTimer->setSingleShot(true);
412
    d->delayResizeEventTimer->setObjectName(QStringLiteral("delayResizeEventTimer"));
Albert Astals Cid's avatar
Albert Astals Cid committed
413
    connect(d->delayResizeEventTimer, &QTimer::timeout, this, &PageView::delayedResizeEvent);
414

415
416
    setFrameStyle(QFrame::NoFrame);

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

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

421
    // viewport setup: setup focus, and track mouse
Albert Astals Cid's avatar
Albert Astals Cid committed
422
423
424
425
426
427
    viewport()->setFocusProxy(this);
    viewport()->setFocusPolicy(Qt::StrongFocus);
    viewport()->setAttribute(Qt::WA_OpaquePaintEvent);
    viewport()->setAttribute(Qt::WA_NoSystemBackground);
    viewport()->setMouseTracking(true);
    viewport()->setAutoFillBackground(false);
428
429
430
431
432
433

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

    QScrollerProperties prop;
    prop.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.3);
    prop.setScrollMetric(QScrollerProperties::MaximumVelocity, 1);
434
    prop.setScrollMetric(QScrollerProperties::AcceleratingFlickMaximumTime, 0.2); // Workaround for QTBUG-88249 (non-flick gestures recognized as accelerating flick)
Kezi Olio's avatar
Kezi Olio committed
435
436
    prop.setScrollMetric(QScrollerProperties::HorizontalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
    prop.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, QScrollerProperties::OvershootAlwaysOff);
437
    prop.setScrollMetric(QScrollerProperties::DragStartDistance, 0.0);
438
439
    d->scroller->setScrollerProperties(prop);

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

442
    // the apparently "magic" value of 20 is the same used internally in QScrollArea
Albert Astals Cid's avatar
Albert Astals Cid committed
443
444
445
446
    verticalScrollBar()->setCursor(Qt::ArrowCursor);
    verticalScrollBar()->setSingleStep(20);
    horizontalScrollBar()->setCursor(Qt::ArrowCursor);
    horizontalScrollBar()->setSingleStep(20);
447

448
449
    // make the smooth scroll animation durations respect the global animation
    // scale
450
451
452
453
454
455
    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();
        }
    });
456

Yuri Chornoivan's avatar
Yuri Chornoivan committed
457
    // connect the padding of the viewport to pixmaps requests
Laurent Montel's avatar
Laurent Montel committed
458
459
    connect(horizontalScrollBar(), &QAbstractSlider::valueChanged, this, &PageView::slotRequestVisiblePixmaps);
    connect(verticalScrollBar(), &QAbstractSlider::valueChanged, this, &PageView::slotRequestVisiblePixmaps);
460

461
462
463
464
465
    // 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
466
467
    auto update_scroller = [=]() {
        d->scroller->scrollTo(QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()), 0); // sync scroller with scrollbar
468
    };
469
470
    connect(verticalScrollBar(), &QAbstractSlider::actionTriggered, this, update_scroller, Qt::QueuedConnection);
    connect(horizontalScrollBar(), &QAbstractSlider::actionTriggered, this, update_scroller, Qt::QueuedConnection);
471

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

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

477
    // set a corner button to resize the view to the page size
Albert Astals Cid's avatar
Albert Astals Cid committed
478
479
480
481
    //    QPushButton * resizeButton = new QPushButton( viewport() );
    //    resizeButton->setPixmap( SmallIcon("crop") );
    //    setCornerWidget( resizeButton );
    //    resizeButton->setEnabled( false );
482
    // connect(...);
Albert Astals Cid's avatar
Albert Astals Cid committed
483
    setAttribute(Qt::WA_InputMethodEnabled, true);
484

485
    // Grab pinch gestures to zoom and rotate the view
486
487
    grabGesture(Qt::PinchGesture);

Michal Humpula's avatar
Michal Humpula committed
488
489
    d->magnifierView = new MagnifierView(document, this);
    d->magnifierView->hide();
490
    d->magnifierView->setGeometry(0, 0, 351, 201); // TODO: more dynamic?
Michal Humpula's avatar
Michal Humpula committed
491

Laurent Montel's avatar
Laurent Montel committed
492
493
    connect(document, &Okular::Document::processMovieAction, this, &PageView::slotProcessMovieAction);
    connect(document, &Okular::Document::processRenditionAction, this, &PageView::slotProcessRenditionAction);
494

495
    // schedule the welcome message
496
    QMetaObject::invokeMethod(this, "slotShowWelcome", Qt::QueuedConnection);
497
498
499
500
}

PageView::~PageView()
{
501
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
502
    if (d->m_tts)
Pino Toscano's avatar
Pino Toscano committed
503
        d->m_tts->stopAllSpeechs();
504
#endif
Pino Toscano's avatar
Pino Toscano committed
505

506
    delete d->mouseAnnotation;
Pino Toscano's avatar
Pino Toscano committed
507

508
    // delete the local storage structure
509

510
511
    // 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
512
    QSet<AnnotWindow *> annowindows = d->m_annowindows;
513
    d->m_annowindows.clear();
Albert Astals Cid's avatar
Albert Astals Cid committed
514
    qDeleteAll(annowindows);
515

516
    // delete all widgets
Albert Astals Cid's avatar
Albert Astals Cid committed
517
    qDeleteAll(d->items);
518
    delete d->formsWidgetController;
Albert Astals Cid's avatar
Albert Astals Cid committed
519
    d->document->removeObserver(this);
520
521
522
    delete d;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
523
void PageView::setupBaseActions(KActionCollection *ac)
524
525
526
527
{
    d->actionCollection = ac;

    // Zoom actions ( higher scales takes lots of memory! )
Albert Astals Cid's avatar
Albert Astals Cid committed
528
529
530
531
532
    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);
533
534
    updateZoomText();

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

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

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

Albert Astals Cid's avatar
Albert Astals Cid committed
543
void PageView::setupViewerActions(KActionCollection *ac)
544
{
545
546
    d->actionCollection = ac;

547
548
    ac->setDefaultShortcut(d->aZoomIn, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Plus));
    ac->setDefaultShortcut(d->aZoomOut, QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Minus));
549

550
    // orientation menu actions
Albert Astals Cid's avatar
Albert Astals Cid committed
551
552
553
554
555
556
557
558
559
560
561
562
563
564
    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);
565

Jake Linder's avatar
Jake Linder committed
566
    // Trim View actions
Albert Astals Cid's avatar
Albert Astals Cid committed
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
    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);
598
    ac->setDefaultShortcut(d->aFitWindowToPage, QKeySequence(Qt::CTRL | Qt::Key_J));
Albert Astals Cid's avatar
Albert Astals Cid committed
599
600
    ac->addAction(QStringLiteral("fit_window_to_page"), d->aFitWindowToPage);
    connect(d->aFitWindowToPage, &QAction::triggered, this, &PageView::slotFitWindowToPage);
Thomas Fischer's avatar
Thomas Fischer committed
601

602
    // View Mode action menu (Single Page, Facing Pages,...(choose), and Continuous (on/off))
Albert Astals Cid's avatar
Albert Astals Cid committed
603
604
605
606
607
608
609
610
611
612
613
    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);
614
    };
Albert Astals Cid's avatar
Albert Astals Cid committed
615
616
617
618
619
620
621
622
623
624
    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);
625
            break;
626
627
        }
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
628
    connect(d->viewModeActionGroup, &QActionGroup::triggered, this, &PageView::slotViewMode);
629

630
631
    // Continuous view action, add to view mode action menu.
    d->aViewModeMenu->addSeparator();
Albert Astals Cid's avatar
Albert Astals Cid committed
632
    d->aViewContinuous = new KToggleAction(QIcon::fromTheme(QStringLiteral("view-pages-continuous")), i18n("&Continuous"), this);
633
    d->aViewModeMenu->addAction(d->aViewContinuous);
Albert Astals Cid's avatar
Albert Astals Cid committed
634
635
636
    ac->addAction(QStringLiteral("view_continuous"), d->aViewContinuous);
    connect(d->aViewContinuous, &QAction::toggled, this, &PageView::slotContinuousToggled);
    d->aViewContinuous->setChecked(Okular::Settings::viewContinuous());
637

David Hurka's avatar
David Hurka committed
638
639
640
641
642
643
644
    // 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);

645
    // Mouse mode actions for viewer mode
Albert Astals Cid's avatar
Albert Astals Cid committed
646
647
648
649
650
651
    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);
652
    ac->setDefaultShortcut(d->aMouseNormal, QKeySequence(Qt::CTRL | Qt::Key_1));
Albert Astals Cid's avatar
Albert Astals Cid committed
653
654
    d->aMouseNormal->setActionGroup(d->mouseModeActionGroup);
    d->aMouseNormal->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Browse);
655

Albert Astals Cid's avatar
Albert Astals Cid committed
656
657
658
659
    QAction *mz = new QAction(QIcon::fromTheme(QStringLiteral("page-zoom")), i18n("&Zoom"), this);
    ac->addAction(QStringLiteral("mouse_zoom"), mz);
    connect(mz, &QAction::triggered, this, &PageView::slotSetMouseZoom);
    mz->setCheckable(true);
660
    ac->setDefaultShortcut(mz, QKeySequence(Qt::CTRL | Qt::Key_2));
Albert Astals Cid's avatar
Albert Astals Cid committed
661
662
    mz->setActionGroup(d->mouseModeActionGroup);
    mz->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Zoom);
663

Albert Astals Cid's avatar
Albert Astals Cid committed
664
665
666
    QAction *aToggleChangeColors = new QAction(i18n("&Toggle Change Colors"), this);
    ac->addAction(QStringLiteral("toggle_change_colors"), aToggleChangeColors);
    connect(aToggleChangeColors, &QAction::triggered, this, &PageView::slotToggleChangeColors);
667
668
669
}

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

674
675
    ac->setDefaultShortcuts(d->aZoomIn, KStandardShortcut::zoomIn());
    ac->setDefaultShortcuts(d->aZoomOut, KStandardShortcut::zoomOut());
676
677

    // Mouse-Mode actions
Albert Astals Cid's avatar
Albert Astals Cid committed
678
679
680
681
    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);
682
    ac->setDefaultShortcut(d->aMouseSelect, Qt::CTRL | Qt::Key_3);
683

Albert Astals Cid's avatar
Albert Astals Cid committed
684
685
    d->aMouseSelect->setActionGroup(d->mouseModeActionGroup);
    d->aMouseSelect->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::RectSelect);
686

Albert Astals Cid's avatar
Albert Astals Cid committed
687
688
689
690
    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);
691
    ac->setDefaultShortcut(d->aMouseTextSelect, Qt::CTRL | Qt::Key_4);
Albert Astals Cid's avatar
Albert Astals Cid committed
692
693
    d->aMouseTextSelect->setActionGroup(d->mouseModeActionGroup);
    d->aMouseTextSelect->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::TextSelect);
694

Albert Astals Cid's avatar
Albert Astals Cid committed
695
696
697
698
    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);
699
    ac->setDefaultShortcut(d->aMouseTableSelect, Qt::CTRL | Qt::Key_5);
Albert Astals Cid's avatar
Albert Astals Cid committed
700
701
    d->aMouseTableSelect->setActionGroup(d->mouseModeActionGroup);
    d->aMouseTableSelect->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::TableSelect);
Jiri Baum's avatar
Jiri Baum committed
702

Albert Astals Cid's avatar
Albert Astals Cid committed
703
704
705
706
    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);
707
    ac->setDefaultShortcut(d->aMouseMagnifier, Qt::CTRL | Qt::Key_6);
Albert Astals Cid's avatar
Albert Astals Cid committed
708
709
    d->aMouseMagnifier->setActionGroup(d->mouseModeActionGroup);
    d->aMouseMagnifier->setChecked(Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier);
Michal Humpula's avatar
Michal Humpula committed
710

711
    // Mouse-Mode action menu
Albert Astals Cid's avatar
Albert Astals Cid committed
712
713
714
715
716
717
718
    d->aMouseModeMenu = new ToggleActionMenu(QIcon(), QString(), this, ToggleActionMenu::MenuButtonPopup, ToggleActionMenu::ImplicitDefaultAction);
    d->aMouseModeMenu->addAction(d->aMouseSelect);
    d->aMouseModeMenu->addAction(d->aMouseTextSelect);
    d->aMouseModeMenu->addAction(d->aMouseTableSelect);
    d->aMouseModeMenu->suggestDefaultAction(d->aMouseTextSelect);
    d->aMouseModeMenu->setText(i18nc("@action", "Selection Tools"));
    ac->addAction(QStringLiteral("mouse_selecttools"), d->aMouseModeMenu);
719

Albert Astals Cid's avatar
Albert Astals Cid committed
720
721
    d->aSignature = new QAction(QIcon::fromTheme(QStringLiteral("application-pkcs7-signature")), i18n("Digitally &Sign..."), this);
    ac->addAction(QStringLiteral("add_digital_signature"), d->aSignature);
Albert Astals Cid's avatar
Albert Astals Cid committed
722
    connect(d->aSignature, &QAction::triggered, this, &PageView::slotSignature);
Katarina Behrens's avatar
Katarina Behrens committed
723

724
    // speak actions
725
#ifdef HAVE_SPEECH
Albert Astals Cid's avatar
Albert Astals Cid committed
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
    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);
745
#else
746
747
748
749
    d->aSpeakDoc = nullptr;
    d->aSpeakPage = nullptr;
    d->aSpeakStop = nullptr;
    d->aSpeakPauseResume = nullptr;
750
#endif
751

752
    // Other actions
Albert Astals Cid's avatar
Albert Astals Cid committed
753
754
755
    QAction *su = new QAction(i18n("Scroll Up"), this);
    ac->addAction(QStringLiteral("view_scroll_up"), su);
    connect(su, &QAction::triggered, this, &PageView::slotAutoScrollUp);
756
    ac->setDefaultShortcut(su, QKeySequence(Qt::SHIFT | Qt::Key_Up));
Albert Astals Cid's avatar
Albert Astals Cid committed
757
    addAction(su);
758

Albert Astals Cid's avatar
Albert Astals Cid committed
759
760
761
    QAction *sd = new QAction(i18n("Scroll Down"), this);
    ac->addAction(QStringLiteral("view_scroll_down"), sd);
    connect(sd, &QAction::triggered, this, &PageView::slotAutoScrollDown);
762
    ac->setDefaultShortcut(sd, QKeySequence(Qt::SHIFT | Qt::Key_Down));
Albert Astals Cid's avatar
Albert Astals Cid committed
763
    addAction(sd);
764

Albert Astals Cid's avatar
Albert Astals Cid committed
765
766
767
    QAction *spu = new QAction(i18n("Scroll Page Up"), this);
    ac->addAction(QStringLiteral("view_scroll_page_up"), spu);
    connect(spu, &QAction::triggered, this, &PageView::slotScrollUp);
768
    ac->setDefaultShortcut(spu, QKeySequence(Qt::SHIFT | Qt::Key_Space));
Albert Astals Cid's avatar
Albert Astals Cid committed
769
    addAction(spu);
770

Albert Astals Cid's avatar
Albert Astals Cid committed
771
772
773
    QAction *spd = new QAction(i18n("Scroll Page Down"), this);
    ac->addAction(QStringLiteral("view_scroll_page_down"), spd);
    connect(spd, &QAction::triggered, this, &PageView::slotScrollDown);
774
    ac->setDefaultShortcut(spd, QKeySequence(Qt::Key_Space));
Albert Astals Cid's avatar
Albert Astals Cid committed
775
    addAction(spd);
776

Albert Astals Cid's avatar
Albert Astals Cid committed
777
778
779
780
781
    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);
782
783

    // Setup undo and redo actions
Albert Astals Cid's avatar
Albert Astals Cid committed
784
785
    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
786
787
    connect(d->document, &Okular::Document::canUndoChanged, kundo, &QAction::setEnabled);
    connect(d->document, &Okular::Document::canRedoChanged, kredo, &QAction::setEnabled);
788
789
    kundo->setEnabled(false);
    kredo->setEnabled(false);
Simone Gaiarin's avatar
Simone Gaiarin committed
790

Albert Astals Cid's avatar
Albert Astals Cid committed
791
792
793
    if (!d->annotator) {
        d->annotator = new PageViewAnnotator(this, d->document);
        connect(d->annotator, &PageViewAnnotator::toolSelected, d->aMouseNormal, &QAction::trigger);
794
        connect(d->annotator, &PageViewAnnotator::toolSelected, d->mouseAnnotation, &MouseAnnotation::reset);
Simone Gaiarin's avatar
Simone Gaiarin committed
795
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
796
    d->annotator->setupActions(ac);
797
798
}

Pino Toscano's avatar
Pino Toscano committed
799
bool PageView::canFitPageWidth() const
800
{
801
    return Okular::Settings::viewMode() != Okular::Settings::EnumViewMode::Single || d->zoomMode != ZoomFitWidth;
802
803
}

Albert Astals Cid's avatar
Albert Astals Cid committed
804
void PageView::fitPageWidth(int page)
805
{
806
    // zoom: Fit Width, columns: 1. setActions + relayout + setPage + update
807
    d->zoomMode = ZoomFitWidth;
Albert Astals Cid's avatar
Albert Astals Cid committed
808
809
810
811
812
813
    Okular::Settings::setViewMode(0);
    d->aZoomFitWidth->setChecked(true);
    d->aZoomFitPage->setChecked(false);
    d->aZoomAutoFit->setChecked(false);
    updateViewMode(0);
    viewport()->setUpdatesEnabled(false);
814
    slotRelayoutPages();
Albert Astals Cid's avatar
Albert Astals Cid committed
815
816
    viewport()->setUpdatesEnabled(true);
    d->document->setViewportPage(page);
817
    updateZoomText();
Enrico Ros's avatar
Enrico Ros committed
818
    setFocus();
819
820
}

Albert Astals Cid's avatar
Albert Astals Cid committed
821
void PageView::openAnnotationWindow(Okular::Annotation *annotation, int pageNumber)
822
{
Albert Astals Cid's avatar
Albert Astals Cid committed
823
    if (!annotation)
824
        return;
825
826

    // find the annot window
Albert Astals Cid's avatar
Albert Astals Cid committed
827
828
829
    AnnotWindow *existWindow = nullptr;
    for (AnnotWindow *aw : qAsConst(d->m_annowindows)) {
        if (aw->annotation() == annotation) {
830
831
832
            existWindow = aw;
            break;
        }
833
    }
834

Albert Astals Cid's avatar
Albert Astals Cid committed
835
836
    if (existWindow == nullptr) {
        existWindow = new AnnotWindow(this, annotation, d->document, pageNumber);
Laurent Montel's avatar
Laurent Montel committed
837
        connect(existWindow, &QObject::destroyed, this, &PageView::slotAnnotationWindowDestroyed);
838

839
        d->m_annowindows << existWindow;
840
841
842
    } else {
        existWindow->raise();
        existWindow->findChild<KTextEdit *>()->setFocus();
843
    }
844
845
846
847

    existWindow->show();
}

Albert Astals Cid's avatar
Albert Astals Cid committed
848
void PageView::slotAnnotationWindowDestroyed(QObject *window)
849
{
Albert Astals Cid's avatar
Albert Astals Cid committed
850
    d->m_annowindows.remove(static_cast<AnnotWindow *>(window));
851 </