pageview.cpp 225 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
Yuri Chornoivan's avatar
Yuri Chornoivan committed
26
#include <qloggingcategory.h>
27
#include <qcursor.h>
28
#include <qevent.h>
29
#include <qimage.h>
30
31
#include <qpainter.h>
#include <qtimer.h>
32
#include <qset.h>
33
#include <qscrollbar.h>
34
#include <qtooltip.h>
Enrico Ros's avatar
Enrico Ros committed
35
36
#include <qapplication.h>
#include <qclipboard.h>
Vishesh Handa's avatar
Vishesh Handa committed
37
#include <qmenu.h>
38
#include <QInputDialog>
39
#include <qdesktopwidget.h>
40
#include <QDesktopServices>
Laurent Montel's avatar
Laurent Montel committed
41
#include <QMimeDatabase>
Olivier Churlaud's avatar
Olivier Churlaud committed
42
#include <QMimeData>
43
#include <QGestureEvent>
Laurent Montel's avatar
Laurent Montel committed
44
#include <QElapsedTimer>
45
46
#include <QScroller>
#include <QScrollerProperties>
47

Laurent Montel's avatar
Laurent Montel committed
48
#include <qaction.h>
49
#include <kactionmenu.h>
Laurent Montel's avatar
Laurent Montel committed
50
#include <kstandardaction.h>
51
#include <kactioncollection.h>
Lukáš Tinkl's avatar
Lukáš Tinkl committed
52
#include <KLocalizedString>
Albert Astals Cid's avatar
compile    
Albert Astals Cid committed
53
54
#include <kselectaction.h>
#include <ktoggleaction.h>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
55
#include <QDebug>
56
#include <kmessagebox.h>
Laurent Montel's avatar
Laurent Montel committed
57
#include <QIcon>
58
59
60
#include <kurifilter.h>
#include <kstringhandler.h>
#include <ktoolinvocation.h>
61
#include <krun.h>
Albert Astals Cid's avatar
compile    
Albert Astals Cid committed
62

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

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

103
static const int pageflags = PagePainter::Accessibility | PagePainter::EnhanceLinks |
104
                       PagePainter::EnhanceImages | PagePainter::Highlights |
105
                       PagePainter::TextSelection | PagePainter::Annotations;
106

Yuri Chornoivan's avatar
Yuri Chornoivan committed
107
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
108

109
110
111
112
113
114
// 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;

115
116
117
118
119
static inline double normClamp( double value, double def )
{
    return ( value < 0.0 || value > 1.0 ) ? def : value;
}

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

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

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

133
134
135
136
// structure used internally by PageView for data storage
class PageViewPrivate
{
public:
137
138
139
    PageViewPrivate( PageView *qq );

    FormWidgetsController* formWidgetsController();
140
#ifdef HAVE_SPEECH
141
    OkularTTS* tts();
142
#endif
143
    QString selectedText() const;
144

145
    // the document, pageviewItems and the 'visible cache'
146
    PageView *q;
147
    Okular::Document * document;
148
149
    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;
164
    QSet< int > pagesWithTextSelection;
165
    bool mouseOnRect;
166
    int mouseMode;
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;
Enrico Ros's avatar
Enrico Ros committed
179
    // auto scroll
180
    int scrollIncrement;
Enrico Ros's avatar
Enrico Ros committed
181
    QTimer * autoScrollTimer;
182
    // annotations
183
    PageViewAnnotator * annotator;
184
    //text annotation dialogs list
185
    QSet< AnnotWindow * > m_annowindows;
Enrico Ros's avatar
Enrico Ros committed
186
    // other stuff
187
    QTimer * delayResizeEventTimer;
188
    bool dirtyLayout;
Enrico Ros's avatar
Enrico Ros committed
189
    bool blockViewport;                 // prevents changes to viewport
190
191
    bool blockPixmapsRequest;           // prevent pixmap requests
    PageViewMessage * messageWindow;    // in pageviewutils.h
192
    bool m_formsVisible;
193
    FormWidgetsController *formsWidgetController;
194
#ifdef HAVE_SPEECH
195
    OkularTTS * m_tts;
196
#endif
197
    QTimer * refreshTimer;
198
    QSet<int> refreshPages;
199

Jake Linder's avatar
Jake Linder committed
200
201
    // bbox state for Trim to Selection mode
    Okular::NormalizedRect trimBoundingBox;
202

203
    // infinite resizing loop prevention
204
    bool verticalScrollBarVisible;
Thomas Fischer's avatar
Thomas Fischer committed
205
    bool horizontalScrollBarVisible;
206

207
208
209
210
    // drag scroll
    QPoint dragScrollVector;
    QTimer dragScrollTimer;

211
212
213
    // left click depress
    QTimer leftClickTimer;

214
    // actions
Vishesh Handa's avatar
Vishesh Handa committed
215
216
217
    QAction * aRotateClockwise;
    QAction * aRotateCounterClockwise;
    QAction * aRotateOriginal;
218
    KSelectAction * aPageSizes;
Jake Linder's avatar
Jake Linder committed
219
    KActionMenu * aTrimMode;
220
    KToggleAction * aTrimMargins;
Vishesh Handa's avatar
Vishesh Handa committed
221
222
223
224
225
    QAction * aMouseNormal;
    QAction * aMouseSelect;
    QAction * aMouseTextSelect;
    QAction * aMouseTableSelect;
    QAction * aMouseMagnifier;
Jake Linder's avatar
Jake Linder committed
226
    KToggleAction * aTrimToSelection;
227
    KToggleAction * aToggleAnnotator;
228
    KSelectAction * aZoom;
229
230
    QAction * aZoomIn;
    QAction * aZoomOut;
Nate Graham's avatar
Nate Graham committed
231
    QAction * aZoomActual;
232
233
    KToggleAction * aZoomFitWidth;
    KToggleAction * aZoomFitPage;
Thomas Fischer's avatar
Thomas Fischer committed
234
    KToggleAction * aZoomAutoFit;
235
236
    KActionMenu * aViewModeMenu;
    QActionGroup * viewModeActionGroup;
237
    KToggleAction * aViewContinuous;
Laurent Montel's avatar
Laurent Montel committed
238
    QAction * aPrevAction;
Vishesh Handa's avatar
Vishesh Handa committed
239
240
241
242
    QAction * aToggleForms;
    QAction * aSpeakDoc;
    QAction * aSpeakPage;
    QAction * aSpeakStop;
243
    QAction * aSpeakPauseResume;
244
    KActionCollection * actionCollection;
245
    QActionGroup * mouseModeActionGroup;
246
    ToggleActionMenu * aMouseModeMenu;
247
    QAction * aFitWindowToPage;
248

249
    int setting_viewCols;
250
    bool rtl_Mode;
251
252
    // Keep track of whether tablet pen is currently pressed down
    bool penDown;
253
254
255

    // Keep track of mouse over link object
    const Okular::ObjectRect * mouseOverLinkObject;
256
257

    QScroller * scroller;
258
259
};

260
261
PageViewPrivate::PageViewPrivate( PageView *qq )
    : q( qq )
262
#ifdef HAVE_SPEECH
263
    , m_tts( nullptr )
264
#endif
265
266
267
268
269
270
271
{
}

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

    return formsWidgetController;
}

298
#ifdef HAVE_SPEECH
299
300
301
302
OkularTTS* PageViewPrivate::tts()
{
    if ( !m_tts )
    {
303
        m_tts = new OkularTTS( q );
304
305
        if ( aSpeakStop )
        {
306
            QObject::connect( m_tts, &OkularTTS::canPauseOrResume,
Laurent Montel's avatar
Laurent Montel committed
307
                              aSpeakStop, &QAction::setEnabled );
308
        }
309
310
311
312
313
314

        if ( aSpeakPauseResume )
        {
             QObject::connect( m_tts, &OkularTTS::canPauseOrResume,
                               aSpeakPauseResume, &QAction::setEnabled );
        }
315
316
317
318
    }

    return m_tts;
}
319
#endif
320

321

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

Albert Astals Cid's avatar
Albert Astals Cid committed
402
    switch( Okular::Settings::zoomMode() )
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
    {
        case 0:
        {
            d->zoomFactor = 1;
            d->zoomMode = PageView::ZoomFixed;
            break;
        }
        case 1:
        {
            d->zoomMode = PageView::ZoomFitWidth;
            break;
        }
        case 2:
        {
            d->zoomMode = PageView::ZoomFitPage;
            break;
        }
Thomas Fischer's avatar
Thomas Fischer committed
420
421
422
423
424
        case 3:
        {
            d->zoomMode = PageView::ZoomFitAuto;
            break;
        }
425
426
    }

427
428
    d->delayResizeEventTimer = new QTimer( this );
    d->delayResizeEventTimer->setSingleShot( true );
429
    d->delayResizeEventTimer->setObjectName(QStringLiteral("delayResizeEventTimer"));
Laurent Montel's avatar
Laurent Montel committed
430
    connect( d->delayResizeEventTimer, &QTimer::timeout, this, &PageView::delayedResizeEvent );
431

432
433
    setFrameStyle(QFrame::NoFrame);

Pino Toscano's avatar
Pino Toscano committed
434
435
    setAttribute( Qt::WA_StaticContents );

Laurent Montel's avatar
Laurent Montel committed
436
    setObjectName( QStringLiteral( "okular::pageView" ) );
Pino Toscano's avatar
Pino Toscano committed
437

438
    // viewport setup: setup focus, and track mouse
439
    viewport()->setFocusProxy( this );
440
    viewport()->setFocusPolicy( Qt::StrongFocus );
441
442
443
    viewport()->setAttribute( Qt::WA_OpaquePaintEvent );
    viewport()->setAttribute( Qt::WA_NoSystemBackground );
    viewport()->setMouseTracking( true );
444
    viewport()->setAutoFillBackground( false );
445
446
447
448
449
450
451
452
453
454

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

    QScrollerProperties prop;
    prop.setScrollMetric(QScrollerProperties::DecelerationFactor, 0.3);
    prop.setScrollMetric(QScrollerProperties::MaximumVelocity, 1);
    prop.setScrollMetric(QScrollerProperties::OvershootScrollDistanceFactor, 0.05);
    prop.setScrollMetric(QScrollerProperties::OvershootDragDistanceFactor, 0.05);
    d->scroller->setScrollerProperties(prop);

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

457
    // the apparently "magic" value of 20 is the same used internally in QScrollArea
458
    verticalScrollBar()->setCursor( Qt::ArrowCursor );
459
    verticalScrollBar()->setSingleStep( 20 );
460
    horizontalScrollBar()->setCursor( Qt::ArrowCursor );
461
    horizontalScrollBar()->setSingleStep( 20 );
462

Yuri Chornoivan's avatar
Yuri Chornoivan committed
463
    // connect the padding of the viewport to pixmaps requests
Laurent Montel's avatar
Laurent Montel committed
464
465
    connect(horizontalScrollBar(), &QAbstractSlider::valueChanged, this, &PageView::slotRequestVisiblePixmaps);
    connect(verticalScrollBar(), &QAbstractSlider::valueChanged, this, &PageView::slotRequestVisiblePixmaps);
466
467
468
469
470
471
472
473
474
475

    auto update_scroller = [=](){
        d->scroller->scrollTo(QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()), 0); //sync scroller with scrollbar
    };
    connect(verticalScrollBar(), &QAbstractSlider::sliderReleased, this, update_scroller);
    connect(horizontalScrollBar(), &QAbstractSlider::sliderReleased, this, update_scroller);
    connect(verticalScrollBar(), &QAbstractSlider::sliderMoved, this, update_scroller);
    connect(horizontalScrollBar(), &QAbstractSlider::sliderMoved, this, update_scroller);


Laurent Montel's avatar
Laurent Montel committed
476
    connect( &d->dragScrollTimer, &QTimer::timeout, this, &PageView::slotDragScroll );
477

478
    d->leftClickTimer.setSingleShot( true );
Laurent Montel's avatar
Laurent Montel committed
479
    connect( &d->leftClickTimer, &QTimer::timeout, this, &PageView::slotShowSizeAllCursor );
480

481
    // set a corner button to resize the view to the page size
482
483
484
485
//    QPushButton * resizeButton = new QPushButton( viewport() );
//    resizeButton->setPixmap( SmallIcon("crop") );
//    setCornerWidget( resizeButton );
//    resizeButton->setEnabled( false );
486
    // connect(...);
Tobias Koenig's avatar
Tobias Koenig committed
487
    setAttribute( Qt::WA_InputMethodEnabled, true );
488

489
    // Grab pinch gestures to zoom and rotate the view
490
491
    grabGesture(Qt::PinchGesture);

Michal Humpula's avatar
Michal Humpula committed
492
493
    d->magnifierView = new MagnifierView(document, this);
    d->magnifierView->hide();
494
    d->magnifierView->setGeometry(0, 0, 351, 201); // TODO: more dynamic?
Michal Humpula's avatar
Michal Humpula committed
495

Laurent Montel's avatar
Laurent Montel committed
496
497
    connect(document, &Okular::Document::processMovieAction, this, &PageView::slotProcessMovieAction);
    connect(document, &Okular::Document::processRenditionAction, this, &PageView::slotProcessRenditionAction);
498

499
    // schedule the welcome message
500
    QMetaObject::invokeMethod(this, "slotShowWelcome", Qt::QueuedConnection);
501
502
503
504
}

PageView::~PageView()
{
505
#ifdef HAVE_SPEECH
Pino Toscano's avatar
Pino Toscano committed
506
507
    if ( d->m_tts )
        d->m_tts->stopAllSpeechs();
508
#endif
Pino Toscano's avatar
Pino Toscano committed
509

510
    delete d->mouseAnnotation;
Pino Toscano's avatar
Pino Toscano committed
511

512
    // delete the local storage structure
513

514
515
    // We need to assign it to a different list otherwise slotAnnotationWindowDestroyed
    // will bite us and clear d->m_annowindows
516
    QSet< AnnotWindow * > annowindows = d->m_annowindows;
517
518
519
    d->m_annowindows.clear();
    qDeleteAll( annowindows );

520
    // delete all widgets
521
    qDeleteAll( d->items );
522
    delete d->formsWidgetController;
Albert Astals Cid's avatar
Albert Astals Cid committed
523
    d->document->removeObserver( this );
524
525
526
    delete d;
}

527
528
529
530
531
void PageView::setupBaseActions( KActionCollection * ac )
{
    d->actionCollection = ac;

    // Zoom actions ( higher scales takes lots of memory! )
Laurent Montel's avatar
Laurent Montel committed
532
533
    d->aZoom  = new KSelectAction(QIcon::fromTheme( QStringLiteral("page-zoom") ), i18n("Zoom"), this);
    ac->addAction(QStringLiteral("zoom_to"), d->aZoom );
534
    d->aZoom->setEditable( true );
Yuri Chornoivan's avatar
Yuri Chornoivan committed
535
    d->aZoom->setMaxComboViewCount( kZoomValues.size() + 3 );
536
    connect( d->aZoom, QOverload<QAction*>::of(&KSelectAction::triggered), this, &PageView::slotZoom );
537
538
    updateZoomText();

Laurent Montel's avatar
Laurent Montel committed
539
    d->aZoomIn = KStandardAction::zoomIn( this, SLOT(slotZoomIn()), ac );
540

Laurent Montel's avatar
Laurent Montel committed
541
    d->aZoomOut = KStandardAction::zoomOut( this, SLOT(slotZoomOut()), ac );
Nate Graham's avatar
Nate Graham committed
542
543
544

    d->aZoomActual = KStandardAction::actualSize( this, &PageView::slotZoomActual, ac );
    d->aZoomActual->setText(i18n("Zoom to 100%"));
545
546
}

547
void PageView::setupViewerActions( KActionCollection * ac )
548
{
549
550
    d->actionCollection = ac;

551
552
    ac->setDefaultShortcut(d->aZoomIn, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Plus));
    ac->setDefaultShortcut(d->aZoomOut, QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Minus));
553

554
    // orientation menu actions
Laurent Montel's avatar
Laurent Montel committed
555
    d->aRotateClockwise = new QAction( QIcon::fromTheme( QStringLiteral("object-rotate-right") ), i18n( "Rotate &Right" ), this );
556
    d->aRotateClockwise->setIconText( i18nc( "Rotate right", "Right" ) );
Laurent Montel's avatar
Laurent Montel committed
557
    ac->addAction( QStringLiteral("view_orientation_rotate_cw"), d->aRotateClockwise );
558
    d->aRotateClockwise->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
559
560
    connect( d->aRotateClockwise, &QAction::triggered, this, &PageView::slotRotateClockwise );
    d->aRotateCounterClockwise = new QAction( QIcon::fromTheme( QStringLiteral("object-rotate-left") ), i18n( "Rotate &Left" ), this );
561
    d->aRotateCounterClockwise->setIconText( i18nc( "Rotate left", "Left" ) );
Laurent Montel's avatar
Laurent Montel committed
562
    ac->addAction( QStringLiteral("view_orientation_rotate_ccw"), d->aRotateCounterClockwise );
563
    d->aRotateCounterClockwise->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
564
    connect( d->aRotateCounterClockwise, &QAction::triggered, this, &PageView::slotRotateCounterClockwise );
Vishesh Handa's avatar
Vishesh Handa committed
565
    d->aRotateOriginal = new QAction( i18n( "Original Orientation" ), this );
Laurent Montel's avatar
Laurent Montel committed
566
    ac->addAction( QStringLiteral("view_orientation_original"), d->aRotateOriginal );
567
    d->aRotateOriginal->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
568
    connect( d->aRotateOriginal, &QAction::triggered, this, &PageView::slotRotateOriginal );
569

Laurent Montel's avatar
Laurent Montel committed
570
    d->aPageSizes = new KSelectAction(i18n("&Page Size"), this);
Laurent Montel's avatar
Laurent Montel committed
571
    ac->addAction(QStringLiteral("view_pagesizes"), d->aPageSizes);
572
    d->aPageSizes->setEnabled( false );
573

574
575
    connect( d->aPageSizes , QOverload<int>::of(&KSelectAction::triggered),
         this, &PageView::slotPageSizes );
576

Jake Linder's avatar
Jake Linder committed
577
578
579
    // Trim View actions
    d->aTrimMode = new KActionMenu(i18n( "&Trim View" ), this );
    d->aTrimMode->setDelayed( false );
Laurent Montel's avatar
Laurent Montel committed
580
    ac->addAction(QStringLiteral("view_trim_mode"), d->aTrimMode );
Jake Linder's avatar
Jake Linder committed
581

582
    d->aTrimMargins  = new KToggleAction(QIcon::fromTheme( QStringLiteral("trim-margins") ), i18n( "&Trim Margins" ), d->aTrimMode->menu() );
Jake Linder's avatar
Jake Linder committed
583
    d->aTrimMode->addAction( d->aTrimMargins  );
Laurent Montel's avatar
Laurent Montel committed
584
    ac->addAction( QStringLiteral("view_trim_margins"), d->aTrimMargins  );
Laurent Montel's avatar
Laurent Montel committed
585
    d->aTrimMargins->setData( QVariant::fromValue( (int)Okular::Settings::EnumTrimMode::Margins ) );
Laurent Montel's avatar
Laurent Montel committed
586
    connect( d->aTrimMargins, &QAction::toggled, this, &PageView::slotTrimMarginsToggled );
587
    d->aTrimMargins->setChecked( Okular::Settings::trimMargins() );
588

589
    d->aTrimToSelection  = new KToggleAction(QIcon::fromTheme( QStringLiteral("trim-to-selection") ), i18n( "Trim To &Selection" ), d->aTrimMode->menu() );
Jake Linder's avatar
Jake Linder committed
590
    d->aTrimMode->addAction( d->aTrimToSelection);
Laurent Montel's avatar
Laurent Montel committed
591
    ac->addAction( QStringLiteral("view_trim_selection"), d->aTrimToSelection);
Laurent Montel's avatar
Laurent Montel committed
592
    d->aTrimToSelection->setData( QVariant::fromValue( (int)Okular::Settings::EnumTrimMode::Selection ) );
Laurent Montel's avatar
Laurent Montel committed
593
    connect( d->aTrimToSelection, &QAction::toggled, this, &PageView::slotTrimToSelectionToggled );
Jake Linder's avatar
Jake Linder committed
594

Laurent Montel's avatar
Laurent Montel committed
595
596
597
    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 );
598

Laurent Montel's avatar
Laurent Montel committed
599
600
601
    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 );
602

Laurent Montel's avatar
Laurent Montel committed
603
604
605
    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 );
Thomas Fischer's avatar
Thomas Fischer committed
606

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

613
614
615
616
617
618
619
620
621
622
623
624
625
    // View Mode action menu (Single Page, Facing Pages,...(choose), and Continuous (on/off))
    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 );
    };
626
627
    addViewMode( new QAction( QIcon::fromTheme(QStringLiteral("view-pages-single")),
                              i18nc( "@item:inmenu", "&Single Page" ), this ),
628
629
                 QStringLiteral("view_render_mode_single"),
                 Okular::Settings::EnumViewMode::Single );
630
631
    addViewMode( new QAction( QIcon::fromTheme(QStringLiteral("view-pages-facing")),
                              i18nc( "@item:inmenu", "&Facing Pages" ), this ),
632
                 QStringLiteral("view_render_mode_facing"),
633
                 Okular::Settings::EnumViewMode::Facing );
634
635
    addViewMode( new QAction( QIcon::fromTheme(QStringLiteral("view-pages-facing-first-centered")),
                              i18nc( "@item:inmenu", "Facing Pages (&Center First Page)" ), this ),
636
                 QStringLiteral("view_render_mode_facing_center_first"),
637
                 Okular::Settings::EnumViewMode::FacingFirstCentered );
638
639
    addViewMode( new QAction( QIcon::fromTheme(QStringLiteral("view-pages-overview")),
                              i18nc( "@item:inmenu", "&Overview" ), this ),
640
                 QStringLiteral("view_render_mode_overview"),
641
642
643
                 Okular::Settings::EnumViewMode::Summary );
    const QList< QAction* > viewModeActions = d->viewModeActionGroup->actions();
    for ( QAction * viewModeAction : viewModeActions )
644
    {
645
        if ( viewModeAction->data().toInt() == Okular::Settings::viewMode() )
646
647
        {
            viewModeAction->setChecked( true );
648
            break;
649
650
        }
    }
651
    connect( d->viewModeActionGroup, &QActionGroup::triggered, this, &PageView::slotViewMode );
652

653
654
    // Continuous view action, add to view mode action menu.
    d->aViewModeMenu->addSeparator();
655
    d->aViewContinuous  = new KToggleAction(QIcon::fromTheme( QStringLiteral("view-pages-continuous") ), i18n("&Continuous"), this);
656
    d->aViewModeMenu->addAction(d->aViewContinuous);
Laurent Montel's avatar
Laurent Montel committed
657
658
    ac->addAction(QStringLiteral("view_continuous"), d->aViewContinuous );
    connect( d->aViewContinuous, &QAction::toggled, this, &PageView::slotContinuousToggled );
659
    d->aViewContinuous->setChecked( Okular::Settings::viewContinuous() );
660

661
662
663
    // Mouse mode actions for viewer mode
    d->mouseModeActionGroup = new QActionGroup( this );
    d->mouseModeActionGroup->setExclusive( true );
664
    d->aMouseNormal  = new QAction( QIcon::fromTheme( QStringLiteral("transform-browse") ), i18n( "&Browse" ), this );
Laurent Montel's avatar
Laurent Montel committed
665
666
    ac->addAction(QStringLiteral("mouse_drag"), d->aMouseNormal );
    connect( d->aMouseNormal, &QAction::triggered, this, &PageView::slotSetMouseNormal );
Pino Toscano's avatar
Pino Toscano committed
667
    d->aMouseNormal->setCheckable( true );
668
    ac->setDefaultShortcut(d->aMouseNormal, QKeySequence(Qt::CTRL + Qt::Key_1));
669
    d->aMouseNormal->setActionGroup( d->mouseModeActionGroup );
670
    d->aMouseNormal->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Browse );
671

Nate Graham's avatar
Nate Graham committed
672
    QAction * mz  = new QAction(QIcon::fromTheme( QStringLiteral("page-zoom") ), i18n("&Zoom"), this);
Laurent Montel's avatar
Laurent Montel committed
673
674
    ac->addAction(QStringLiteral("mouse_zoom"), mz );
    connect( mz, &QAction::triggered, this, &PageView::slotSetMouseZoom );
Pino Toscano's avatar
Pino Toscano committed
675
    mz->setCheckable( true );
676
    ac->setDefaultShortcut(mz, QKeySequence(Qt::CTRL + Qt::Key_2));
677
    mz->setActionGroup( d->mouseModeActionGroup );
678
    mz->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Zoom );
679

Vishesh Handa's avatar
Vishesh Handa committed
680
    QAction * aToggleChangeColors  = new QAction(i18n("&Toggle Change Colors"), this);
Laurent Montel's avatar
Laurent Montel committed
681
682
    ac->addAction(QStringLiteral("toggle_change_colors"), aToggleChangeColors );
    connect( aToggleChangeColors, &QAction::triggered, this, &PageView::slotToggleChangeColors );
683
684
685
686
687
688
689
}

// WARNING: 'setupViewerActions' must have been called before this method
void PageView::setupActions( KActionCollection * ac )
{
    d->actionCollection = ac;

690
691
    ac->setDefaultShortcuts(d->aZoomIn, KStandardShortcut::zoomIn());
    ac->setDefaultShortcuts(d->aZoomOut, KStandardShortcut::zoomOut());
692
693

    // Mouse-Mode actions
Nate Graham's avatar
Nate Graham committed
694
    d->aMouseSelect  = new QAction(QIcon::fromTheme( QStringLiteral("select-rectangular") ), i18n("Area &Selection"), this);
Laurent Montel's avatar
Laurent Montel committed
695
696
    ac->addAction(QStringLiteral("mouse_select"), d->aMouseSelect );
    connect( d->aMouseSelect, &QAction::triggered, this, &PageView::slotSetMouseSelect );
Pino Toscano's avatar
Pino Toscano committed
697
    d->aMouseSelect->setCheckable( true );
698
699
    ac->setDefaultShortcut(d->aMouseSelect, Qt::CTRL + Qt::Key_3);

700
    d->aMouseSelect->setActionGroup( d->mouseModeActionGroup );
701
    d->aMouseSelect->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::RectSelect );
702

703
    d->aMouseTextSelect  = new QAction(QIcon::fromTheme( QStringLiteral("edit-select-text") ), i18n("&Text Selection"), this);
Laurent Montel's avatar
Laurent Montel committed
704
705
    ac->addAction(QStringLiteral("mouse_textselect"), d->aMouseTextSelect );
    connect( d->aMouseTextSelect, &QAction::triggered, this, &PageView::slotSetMouseTextSelect );
706
    d->aMouseTextSelect->setCheckable( true );
707
    ac->setDefaultShortcut(d->aMouseTextSelect, Qt::CTRL + Qt::Key_4);
708
    d->aMouseTextSelect->setActionGroup( d->mouseModeActionGroup );
709
    d->aMouseTextSelect->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::TextSelect );
710

Nate Graham's avatar
Nate Graham committed
711
    d->aMouseTableSelect  = new QAction(QIcon::fromTheme( QStringLiteral("table") ), i18n("T&able Selection"), this);
Laurent Montel's avatar
Laurent Montel committed
712
713
    ac->addAction(QStringLiteral("mouse_tableselect"), d->aMouseTableSelect );
    connect( d->aMouseTableSelect, &QAction::triggered, this, &PageView::slotSetMouseTableSelect );
Jiri Baum's avatar
Jiri Baum committed
714
    d->aMouseTableSelect->setCheckable( true );
715
    ac->setDefaultShortcut(d->aMouseTableSelect, Qt::CTRL + Qt::Key_5);
716
    d->aMouseTableSelect->setActionGroup( d->mouseModeActionGroup );
717
    d->aMouseTableSelect->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::TableSelect );
Jiri Baum's avatar
Jiri Baum committed
718

Laurent Montel's avatar
Laurent Montel committed
719
720
721
    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 );
Michal Humpula's avatar
Michal Humpula committed
722
    d->aMouseMagnifier->setCheckable( true );
723
    ac->setDefaultShortcut(d->aMouseMagnifier, Qt::CTRL + Qt::Key_6);
Michal Humpula's avatar
Michal Humpula committed
724
725
726
    d->aMouseMagnifier->setActionGroup( d->mouseModeActionGroup );
    d->aMouseMagnifier->setChecked( Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Magnifier );

727
728
729
730
731
732
733
734
735
736
737
    // Mouse-Mode action menu
    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 );

Laurent Montel's avatar
Laurent Montel committed
738
739
    d->aToggleAnnotator  = new KToggleAction(QIcon::fromTheme( QStringLiteral("draw-freehand") ), i18n("&Review"), this);
    ac->addAction(QStringLiteral("mouse_toggle_annotate"), d->aToggleAnnotator );
Pino Toscano's avatar
Pino Toscano committed
740
    d->aToggleAnnotator->setCheckable( true );
Laurent Montel's avatar
Laurent Montel committed
741
    connect( d->aToggleAnnotator, &QAction::toggled, this, &PageView::slotToggleAnnotator );
742
    ac->setDefaultShortcut(d->aToggleAnnotator, Qt::Key_F6);
743

744
    // speak actions
745
#ifdef HAVE_SPEECH
Laurent Montel's avatar
Laurent Montel committed
746
747
    d->aSpeakDoc = new QAction( QIcon::fromTheme( QStringLiteral("text-speak") ), i18n( "Speak Whole Document" ), this );
    ac->addAction( QStringLiteral("speak_document"), d->aSpeakDoc );
748
    d->aSpeakDoc->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
749
    connect( d->aSpeakDoc, &QAction::triggered, this, &PageView::slotSpeakDocument );
750

Laurent Montel's avatar
Laurent Montel committed
751
752
    d->aSpeakPage = new QAction( QIcon::fromTheme( QStringLiteral("text-speak") ), i18n( "Speak Current Page" ), this );
    ac->addAction( QStringLiteral("speak_current_page"), d->aSpeakPage );
753
    d->aSpeakPage->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
754
    connect( d->aSpeakPage, &QAction::triggered, this, &PageView::slotSpeakCurrentPage );
755

Laurent Montel's avatar
Laurent Montel committed
756
757
    d->aSpeakStop = new QAction( QIcon::fromTheme( QStringLiteral("media-playback-stop") ), i18n( "Stop Speaking" ), this );
    ac->addAction( QStringLiteral("speak_stop_all"), d->aSpeakStop );
758
    d->aSpeakStop->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
759
    connect( d->aSpeakStop, &QAction::triggered, this, &PageView::slotStopSpeaks );
760
761
762
763
764

    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 );
765
#else
Albert Astals Cid's avatar