pageview.cpp 172 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 <tsdgeos@terra.es>       *
4
 *                                                                         *
5
 *   With portions of code from kpdf/kpdf_pagewidget.cc by:                *
6
7
8
9
10
11
 *     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
12
 *     Copyright (C) 2011 by Jiri Baum - NICTA <jiri@baum.com.au>          *
13
14
15
16
17
18
19
 *                                                                         *
 *   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
20
21
#include "pageview.h"

22
// qt/kde includes
23
#include <qcursor.h>
24
#include <qevent.h>
25
#include <qimage.h>
26
27
#include <qpainter.h>
#include <qtimer.h>
28
#include <qset.h>
29
#include <qscrollbar.h>
30
#include <qtooltip.h>
Enrico Ros's avatar
Enrico Ros committed
31
32
#include <qapplication.h>
#include <qclipboard.h>
33

34
#include <kaction.h>
35
#include <kactionmenu.h>
Laurent Montel's avatar
Laurent Montel committed
36
#include <kstandardaction.h>
37
#include <kactioncollection.h>
38
#include <kmenu.h>
39
#include <klocale.h>
40
#include <kfiledialog.h>
Pino Toscano's avatar
Pino Toscano committed
41
#include <kglobal.h>
42
#include <kglobalsettings.h>
43
#include <kinputdialog.h>
Albert Astals Cid's avatar
compile    
Albert Astals Cid committed
44
45
#include <kselectaction.h>
#include <ktoggleaction.h>
46
#include <kdebug.h>
Albert Astals Cid's avatar
Albert Astals Cid committed
47
#include <kdeversion.h>
48
#include <kmessagebox.h>
Laurent Montel's avatar
Laurent Montel committed
49
#include <kicon.h>
50
51
52
#include <kurifilter.h>
#include <kstringhandler.h>
#include <ktoolinvocation.h>
Albert Astals Cid's avatar
compile    
Albert Astals Cid committed
53

54
// system includes
55
#include <math.h>
56
#include <stdlib.h>
57

58
// local includes
59
#include "formwidgets.h"
60
#include "pageviewutils.h"
61
#include "pagepainter.h"
62
#include "core/annotations.h"
63
#include "annotwindow.h"
Pino Toscano's avatar
Pino Toscano committed
64
#include "guiutils.h"
65
#include "annotationpopup.h"
66
#include "pageviewannotator.h"
67
#include "toolaction.h"
68
#include "tts.h"
69
#include "videowidget.h"
Pino Toscano's avatar
Pino Toscano committed
70
#include "core/action.h"
71
#include "core/area.h"
72
#include "core/document.h"
73
#include "core/form.h"
74
#include "core/page.h"
75
#include "core/misc.h"
76
#include "core/generator.h"
77
#include "core/movie.h"
78
#include "core/sourcereference.h"
79
#include "settings.h"
80

81
82
static int pageflags = PagePainter::Accessibility | PagePainter::EnhanceLinks |
                       PagePainter::EnhanceImages | PagePainter::Highlights |
83
                       PagePainter::TextSelection | PagePainter::Annotations;
84

85
86
87
88
89
static inline double normClamp( double value, double def )
{
    return ( value < 0.0 || value > 1.0 ) ? def : value;
}

Jiri Baum's avatar
Jiri Baum committed
90
91
92
93
94
struct TableSelectionPart {
    PageViewItem * item;
    Okular::NormalizedRect rectInItem;
    Okular::NormalizedRect rectInSelection;

95
    TableSelectionPart(PageViewItem * item_p, const Okular::NormalizedRect &rectInItem_p, const Okular::NormalizedRect &rectInSelection_p);
Jiri Baum's avatar
Jiri Baum committed
96
97
};

98
TableSelectionPart::TableSelectionPart(  PageViewItem * item_p, const Okular::NormalizedRect &rectInItem_p, const Okular::NormalizedRect &rectInSelection_p)
Jiri Baum's avatar
Jiri Baum committed
99
100
101
102
    : item ( item_p ), rectInItem (rectInItem_p), rectInSelection (rectInSelection_p)
{
}

103
104
105
106
// structure used internally by PageView for data storage
class PageViewPrivate
{
public:
107
108
109
    PageViewPrivate( PageView *qq );

    FormWidgetsController* formWidgetsController();
110
    OkularTTS* tts();
111
    QString selectedText() const;
112

113
    // the document, pageviewItems and the 'visible cache'
114
    PageView *q;
115
    Okular::Document * document;
116
117
    QVector< PageViewItem * > items;
    QLinkedList< PageViewItem * > visibleItems;
118

119
    // view layout (columns and continuous in Settings), zoom and mouse
120
121
122
123
    PageView::ZoomMode zoomMode;
    float zoomFactor;
    PageView::MouseMode mouseMode;
    QPoint mouseGrabPos;
124
    QPoint mousePressPos;
125
    QPoint mouseSelectPos;
126
127
    bool mouseMidZooming;
    int mouseMidLastY;
128
    bool mouseSelecting;
129
    QRect mouseSelectionRect;
130
    QColor mouseSelectionColor;
131
    bool mouseTextSelecting;
132
    QSet< int > pagesWithTextSelection;
133
    bool mouseOnRect;
134
135
    Okular::Annotation * mouseAnn;
    QPoint mouseAnnPos;
136

Jiri Baum's avatar
Jiri Baum committed
137
138
139
140
    // table selection
    QList<double> tableSelectionCols;
    QList<double> tableSelectionRows;
    QList<TableSelectionPart> tableSelectionParts;
141
    bool tableDividersGuessed;
Jiri Baum's avatar
Jiri Baum committed
142

Enrico Ros's avatar
Enrico Ros committed
143
144
145
146
    // viewport move
    bool viewportMoveActive;
    QTime viewportMoveTime;
    QPoint viewportMoveDest;
147
148
149
    int lastSourceLocationViewportPageNumber;
    double lastSourceLocationViewportNormalizedX;
    double lastSourceLocationViewportNormalizedY;
Enrico Ros's avatar
Enrico Ros committed
150
151
    QTimer * viewportMoveTimer;
    // auto scroll
152
    int scrollIncrement;
Enrico Ros's avatar
Enrico Ros committed
153
    QTimer * autoScrollTimer;
154
    // annotations
155
    PageViewAnnotator * annotator;
156
    //text annotation dialogs list
157
    QHash< Okular::Annotation *, AnnotWindow * > m_annowindows;
Enrico Ros's avatar
Enrico Ros committed
158
    // other stuff
159
    QTimer * delayResizeEventTimer;
160
    bool dirtyLayout;
Enrico Ros's avatar
Enrico Ros committed
161
    bool blockViewport;                 // prevents changes to viewport
162
163
    bool blockPixmapsRequest;           // prevent pixmap requests
    PageViewMessage * messageWindow;    // in pageviewutils.h
164
    bool m_formsVisible;
165
    FormWidgetsController *formsWidgetController;
166
    OkularTTS * m_tts;
167
168
    QTimer * refreshTimer;
    int refreshPage;
169

170
    // infinite resizing loop prevention
171
    bool verticalScrollBarVisible;
172

173
174
175
176
    // drag scroll
    QPoint dragScrollVector;
    QTimer dragScrollTimer;

177
178
179
    // left click depress
    QTimer leftClickTimer;

180
    // actions
181
182
183
    KAction * aRotateClockwise;
    KAction * aRotateCounterClockwise;
    KAction * aRotateOriginal;
184
    KSelectAction * aPageSizes;
185
    KToggleAction * aTrimMargins;
186
187
188
    KAction * aMouseNormal;
    KAction * aMouseSelect;
    KAction * aMouseTextSelect;
Jiri Baum's avatar
Jiri Baum committed
189
    KAction * aMouseTableSelect;
190
    KToggleAction * aToggleAnnotator;
191
    KSelectAction * aZoom;
192
193
    KAction * aZoomIn;
    KAction * aZoomOut;
194
195
196
    KToggleAction * aZoomFitWidth;
    KToggleAction * aZoomFitPage;
    KToggleAction * aZoomFitText;
197
    KActionMenu * aViewMode;
198
    KToggleAction * aViewContinuous;
Laurent Montel's avatar
Laurent Montel committed
199
    QAction * aPrevAction;
200
    KAction * aToggleForms;
201
202
    KAction * aSpeakDoc;
    KAction * aSpeakPage;
203
    KAction * aSpeakStop;
204
    KActionCollection * actionCollection;
205
    QActionGroup * mouseModeActionGroup;
206

207
    int setting_viewCols;
208
209
};

210
211
212
213
214
215
216
217
218
219
PageViewPrivate::PageViewPrivate( PageView *qq )
    : q( qq )
{
}

FormWidgetsController* PageViewPrivate::formWidgetsController()
{
    if ( !formsWidgetController )
    {
        formsWidgetController = new FormWidgetsController();
Laurent Montel's avatar
Laurent Montel committed
220
221
222
223
        QObject::connect( formsWidgetController, SIGNAL(changed(FormWidgetIface*)),
                          q, SLOT(slotFormWidgetChanged(FormWidgetIface*)) );
        QObject::connect( formsWidgetController, SIGNAL(action(Okular::Action*)),
                          q, SLOT(slotAction(Okular::Action*)) );
224
225
226
227
228
    }

    return formsWidgetController;
}

229
230
231
232
OkularTTS* PageViewPrivate::tts()
{
    if ( !m_tts )
    {
233
        m_tts = new OkularTTS( q );
234
235
        if ( aSpeakStop )
        {
Laurent Montel's avatar
Laurent Montel committed
236
237
238
239
            QObject::connect( m_tts, SIGNAL(hasSpeechs(bool)),
                              aSpeakStop, SLOT(setEnabled(bool)) );
            QObject::connect( m_tts, SIGNAL(errorMessage(QString)),
                              q, SLOT(errorMessage(QString)) );
240
        }
241
242
243
244
245
    }

    return m_tts;
}

246

247
248
249
250
/* 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)
251
 *  550 - events: mouse, keyboard, drag/drop
252
 *  170 - slotRelayoutPages: set contents of the scrollview on continuous/single modes
253
 *  100 - zoom: zooming pages in different ways, keeping update the toolbar actions, etc..
254
 *  other misc functions: only slotRequestVisiblePixmaps and pickItemOnPoint noticeable,
255
 * and many insignificant stuff like this comment :-)
256
 */
257
PageView::PageView( QWidget *parent, Okular::Document *document )
258
    : QAbstractScrollArea( parent )
259
    , Okular::View( QString::fromLatin1( "PageView" ) )
260
261
{
    // create and initialize private storage structure
262
    d = new PageViewPrivate( this );
263
    d->document = document;
264
265
266
    d->aRotateClockwise = 0;
    d->aRotateCounterClockwise = 0;
    d->aRotateOriginal = 0;
267
    d->aViewMode = 0;
268
    d->zoomMode = PageView::ZoomFitWidth;
269
    d->zoomFactor = 1.0;
270
    d->mouseMode = MouseNormal;
271
    d->mouseMidZooming = false;
272
    d->mouseSelecting = false;
273
    d->mouseTextSelecting = false;
274
    d->mouseOnRect = false;
275
    d->mouseAnn = 0;
276
    d->tableDividersGuessed = false;
Enrico Ros's avatar
Enrico Ros committed
277
    d->viewportMoveActive = false;
278
279
280
    d->lastSourceLocationViewportPageNumber = -1;
    d->lastSourceLocationViewportNormalizedX = 0.0;
    d->lastSourceLocationViewportNormalizedY = 0.0;
Enrico Ros's avatar
Enrico Ros committed
281
    d->viewportMoveTimer = 0;
282
    d->scrollIncrement = 0;
Enrico Ros's avatar
Enrico Ros committed
283
    d->autoScrollTimer = 0;
284
    d->annotator = 0;
285
    d->dirtyLayout = false;
286
    d->blockViewport = false;
287
    d->blockPixmapsRequest = false;
288
    d->messageWindow = new PageViewMessage(this);
289
    d->m_formsVisible = false;
290
    d->formsWidgetController = 0;
291
    d->m_tts = 0;
292
293
    d->refreshTimer = 0;
    d->refreshPage = -1;
294
295
296
297
    d->aRotateClockwise = 0;
    d->aRotateCounterClockwise = 0;
    d->aRotateOriginal = 0;
    d->aPageSizes = 0;
298
    d->aTrimMargins = 0;
299
300
301
302
303
304
305
306
307
    d->aMouseNormal = 0;
    d->aMouseSelect = 0;
    d->aMouseTextSelect = 0;
    d->aToggleAnnotator = 0;
    d->aZoomFitWidth = 0;
    d->aZoomFitPage = 0;
    d->aZoomFitText = 0;
    d->aViewMode = 0;
    d->aViewContinuous = 0;
308
    d->aPrevAction = 0;
309
    d->aToggleForms = 0;
310
311
    d->aSpeakDoc = 0;
    d->aSpeakPage = 0;
312
    d->aSpeakStop = 0;
313
    d->actionCollection = 0;
314
    d->aPageSizes=0;
315
    d->setting_viewCols = Okular::Settings::viewColumns();
316
    d->mouseModeActionGroup = 0;
317

Albert Astals Cid's avatar
Albert Astals Cid committed
318
    switch( Okular::Settings::zoomMode() )
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
    {
        case 0:
        {
            d->zoomFactor = 1;
            d->zoomMode = PageView::ZoomFixed;
            break;
        }
        case 1:
        {
            d->zoomMode = PageView::ZoomFitWidth;
            break;
        }
        case 2:
        {
            d->zoomMode = PageView::ZoomFitPage;
            break;
        }
    }

338
339
    d->delayResizeEventTimer = new QTimer( this );
    d->delayResizeEventTimer->setSingleShot( true );
Laurent Montel's avatar
Laurent Montel committed
340
    connect( d->delayResizeEventTimer, SIGNAL(timeout()), this, SLOT(delayedResizeEvent()) );
341

342
343
    setFrameStyle(QFrame::NoFrame);

Pino Toscano's avatar
Pino Toscano committed
344
345
    setAttribute( Qt::WA_StaticContents );

Albert Astals Cid's avatar
Albert Astals Cid committed
346
    setObjectName( QLatin1String( "okular::pageView" ) );
Pino Toscano's avatar
Pino Toscano committed
347

348
    // viewport setup: setup focus, accept drops and track mouse
349
    viewport()->setFocusProxy( this );
350
    viewport()->setFocusPolicy( Qt::StrongFocus );
351
352
    viewport()->setAttribute( Qt::WA_OpaquePaintEvent );
    viewport()->setAttribute( Qt::WA_NoSystemBackground );
353
    setAcceptDrops( true );
354
    viewport()->setMouseTracking( true );
355
    viewport()->setAutoFillBackground( false );
356
357
358
    // the apparently "magic" value of 20 is the same used internally in QScrollArea
    verticalScrollBar()->setSingleStep( 20 );
    horizontalScrollBar()->setSingleStep( 20 );
359
360

    // conntect the padding of the viewport to pixmaps requests
361
362
    connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRequestVisiblePixmaps(int)));
    connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(slotRequestVisiblePixmaps(int)));
363
    connect( &d->dragScrollTimer, SIGNAL(timeout()), this, SLOT(slotDragScroll()) );
364

365
366
367
    d->leftClickTimer.setSingleShot( true );
    connect( &d->leftClickTimer, SIGNAL(timeout()), this, SLOT(slotShowSizeAllCursor()) );

368
    // set a corner button to resize the view to the page size
369
370
371
372
//    QPushButton * resizeButton = new QPushButton( viewport() );
//    resizeButton->setPixmap( SmallIcon("crop") );
//    setCornerWidget( resizeButton );
//    resizeButton->setEnabled( false );
373
    // connect(...);
Tobias Koenig's avatar
Tobias Koenig committed
374
    setAttribute( Qt::WA_InputMethodEnabled, true );
375
376

    // schedule the welcome message
377
    QMetaObject::invokeMethod(this, "slotShowWelcome", Qt::QueuedConnection);
378
379
380
381
}

PageView::~PageView()
{
Pino Toscano's avatar
Pino Toscano committed
382
383
384
    if ( d->m_tts )
        d->m_tts->stopAllSpeechs();

385
    // delete the local storage structure
386
    qDeleteAll(d->m_annowindows);
387
    // delete all widgets
Laurent Montel's avatar
Laurent Montel committed
388
    QVector< PageViewItem * >::const_iterator dIt = d->items.constBegin(), dEnd = d->items.constEnd();
389
390
    for ( ; dIt != dEnd; ++dIt )
        delete *dIt;
391
    delete d->formsWidgetController;
Albert Astals Cid's avatar
Albert Astals Cid committed
392
    d->document->removeObserver( this );
393
394
395
    delete d;
}

396
397
398
399
400
void PageView::setupBaseActions( KActionCollection * ac )
{
    d->actionCollection = ac;

    // Zoom actions ( higher scales takes lots of memory! )
401
    d->aZoom  = new KSelectAction(KIcon( "page-zoom" ), i18n("Zoom"), this);
402
403
404
    ac->addAction("zoom_to", d->aZoom );
    d->aZoom->setEditable( true );
    d->aZoom->setMaxComboViewCount( 13 );
Laurent Montel's avatar
Laurent Montel committed
405
    connect( d->aZoom, SIGNAL(triggered(QAction*)), this, SLOT(slotZoom()) );
406
407
    updateZoomText();

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

Laurent Montel's avatar
Laurent Montel committed
410
    d->aZoomOut = KStandardAction::zoomOut( this, SLOT(slotZoomOut()), ac );
411
412
}

413
void PageView::setupViewerActions( KActionCollection * ac )
414
{
415
416
    d->actionCollection = ac;

417
418
419
    d->aZoomIn->setShortcut( QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Plus) );
    d->aZoomOut->setShortcut( QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Minus) );

420
    // orientation menu actions
421
    d->aRotateClockwise = new KAction( KIcon( "object-rotate-right" ), i18n( "Rotate &Right" ), this );
422
    d->aRotateClockwise->setIconText( i18nc( "Rotate right", "Right" ) );
423
    ac->addAction( "view_orientation_rotate_cw", d->aRotateClockwise );
424
    d->aRotateClockwise->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
425
    connect( d->aRotateClockwise, SIGNAL(triggered()), this, SLOT(slotRotateClockwise()) );
426
    d->aRotateCounterClockwise = new KAction( KIcon( "object-rotate-left" ), i18n( "Rotate &Left" ), this );
427
    d->aRotateCounterClockwise->setIconText( i18nc( "Rotate left", "Left" ) );
428
    ac->addAction( "view_orientation_rotate_ccw", d->aRotateCounterClockwise );
429
    d->aRotateCounterClockwise->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
430
    connect( d->aRotateCounterClockwise, SIGNAL(triggered()), this, SLOT(slotRotateCounterClockwise()) );
431
432
    d->aRotateOriginal = new KAction( i18n( "Original Orientation" ), this );
    ac->addAction( "view_orientation_original", d->aRotateOriginal );
433
    d->aRotateOriginal->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
434
    connect( d->aRotateOriginal, SIGNAL(triggered()), this, SLOT(slotRotateOriginal()) );
435

Laurent Montel's avatar
Laurent Montel committed
436
437
    d->aPageSizes = new KSelectAction(i18n("&Page Size"), this);
    ac->addAction("view_pagesizes", d->aPageSizes);
438
    d->aPageSizes->setEnabled( false );
439

Laurent Montel's avatar
Laurent Montel committed
440
441
    connect( d->aPageSizes , SIGNAL(triggered(int)),
         this, SLOT(slotPageSizes(int)) );
442

443
    d->aTrimMargins  = new KToggleAction( i18n( "&Trim Margins" ), this );
444
    ac->addAction("view_trim_margins", d->aTrimMargins );
Laurent Montel's avatar
Laurent Montel committed
445
    connect( d->aTrimMargins, SIGNAL(toggled(bool)), SLOT(slotTrimMarginsToggled(bool)) );
446
    d->aTrimMargins->setChecked( Okular::Settings::trimMargins() );
447

448
    d->aZoomFitWidth  = new KToggleAction(KIcon( "zoom-fit-width" ), i18n("Fit &Width"), this);
Pino Toscano's avatar
Pino Toscano committed
449
    ac->addAction("view_fit_to_width", d->aZoomFitWidth );
Laurent Montel's avatar
Laurent Montel committed
450
    connect( d->aZoomFitWidth, SIGNAL(toggled(bool)), SLOT(slotFitToWidthToggled(bool)) );
451

452
    d->aZoomFitPage  = new KToggleAction(KIcon( "zoom-fit-best" ), i18n("Fit &Page"), this);
Pino Toscano's avatar
Pino Toscano committed
453
    ac->addAction("view_fit_to_page", d->aZoomFitPage );
Laurent Montel's avatar
Laurent Montel committed
454
    connect( d->aZoomFitPage, SIGNAL(toggled(bool)), SLOT(slotFitToPageToggled(bool)) );
455

456
/*
457
    d->aZoomFitText  = new KToggleAction(KIcon( "zoom-fit-best" ), i18n("Fit &Text"), this);
Laurent Montel's avatar
Laurent Montel committed
458
    ac->addAction("zoom_fit_text", d->aZoomFitText );
Laurent Montel's avatar
Laurent Montel committed
459
    connect( d->aZoomFitText, SIGNAL(toggled(bool)), SLOT(slotFitToTextToggled(bool)) );
460
*/
461

462
    // View-Layout actions
463
464
465
466
467
468
469
470
471
472
473
    d->aViewMode = new KActionMenu( KIcon( "view-split-left-right" ), i18n( "&View Mode" ), this );
    d->aViewMode->setDelayed( false );
#define ADD_VIEWMODE_ACTION( text, name, id ) \
do { \
    KAction *vm = new KAction( text, d->aViewMode->menu() ); \
    vm->setCheckable( true ); \
    vm->setData( qVariantFromValue( id ) ); \
    d->aViewMode->addAction( vm ); \
    ac->addAction( name, vm ); \
    vmGroup->addAction( vm ); \
} while( 0 )
474
    ac->addAction("view_render_mode", d->aViewMode );
475
    QActionGroup *vmGroup = new QActionGroup( d->aViewMode->menu() );
476
477
    ADD_VIEWMODE_ACTION( i18n( "Single Page" ), "view_render_mode_single", (int)Okular::Settings::EnumViewMode::Single );
    ADD_VIEWMODE_ACTION( i18n( "Facing Pages" ), "view_render_mode_facing", (int)Okular::Settings::EnumViewMode::Facing );
478
    ADD_VIEWMODE_ACTION( i18n( "Facing Pages (Center First Page)" ), "view_render_mode_facing_center_first", (int)Okular::Settings::EnumViewMode::FacingFirstCentered );
479
480
481
482
483
484
485
486
487
    ADD_VIEWMODE_ACTION( i18n( "Overview" ), "view_render_mode_overview", (int)Okular::Settings::EnumViewMode::Summary );
    const QList<QAction *> viewModeActions = d->aViewMode->menu()->actions();
    foreach(QAction *viewModeAction, viewModeActions)
    {
        if (viewModeAction->data().toInt() == Okular::Settings::viewMode())
        {
            viewModeAction->setChecked( true );
        }
    }
Laurent Montel's avatar
Laurent Montel committed
488
    connect( vmGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotViewMode(QAction*)) );
489
#undef ADD_VIEWMODE_ACTION
490

491
    d->aViewContinuous  = new KToggleAction(KIcon( "view-list-text" ), i18n("&Continuous"), this);
Laurent Montel's avatar
Laurent Montel committed
492
    ac->addAction("view_continuous", d->aViewContinuous );
Laurent Montel's avatar
Laurent Montel committed
493
    connect( d->aViewContinuous, SIGNAL(toggled(bool)), SLOT(slotContinuousToggled(bool)) );
494
    d->aViewContinuous->setChecked( Okular::Settings::viewContinuous() );
495

496
497
498
    // Mouse mode actions for viewer mode
    d->mouseModeActionGroup = new QActionGroup( this );
    d->mouseModeActionGroup->setExclusive( true );
499
    d->aMouseNormal  = new KAction( KIcon( "input-mouse" ), i18n( "&Browse Tool" ), this );
Laurent Montel's avatar
Laurent Montel committed
500
    ac->addAction("mouse_drag", d->aMouseNormal );
Laurent Montel's avatar
Laurent Montel committed
501
    connect( d->aMouseNormal, SIGNAL(triggered()), this, SLOT(slotSetMouseNormal()) );
Pino Toscano's avatar
Pino Toscano committed
502
    d->aMouseNormal->setIconText( i18nc( "Browse Tool", "Browse" ) );
Pino Toscano's avatar
Pino Toscano committed
503
    d->aMouseNormal->setCheckable( true );
504
    d->aMouseNormal->setShortcut( Qt::CTRL + Qt::Key_1 );
505
    d->aMouseNormal->setActionGroup( d->mouseModeActionGroup );
506
    d->aMouseNormal->setChecked( true );
507

508
    KAction * mz  = new KAction(KIcon( "page-zoom" ), i18n("&Zoom Tool"), this);
Laurent Montel's avatar
Laurent Montel committed
509
    ac->addAction("mouse_zoom", mz );
Laurent Montel's avatar
Laurent Montel committed
510
    connect( mz, SIGNAL(triggered()), this, SLOT(slotSetMouseZoom()) );
Pino Toscano's avatar
Pino Toscano committed
511
    mz->setIconText( i18nc( "Zoom Tool", "Zoom" ) );
Pino Toscano's avatar
Pino Toscano committed
512
    mz->setCheckable( true );
513
    mz->setShortcut( Qt::CTRL + Qt::Key_2 );
514
    mz->setActionGroup( d->mouseModeActionGroup );
515

516
517
518
519
520
521
522
523
524
525
526
}

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

    d->aZoomIn->setShortcut( KStandardShortcut::zoomIn() );
    d->aZoomOut->setShortcut( KStandardShortcut::zoomOut() );

    // Mouse-Mode actions
527
    d->aMouseSelect  = new KAction(KIcon( "select-rectangular" ), i18n("&Selection Tool"), this);
Laurent Montel's avatar
Laurent Montel committed
528
    ac->addAction("mouse_select", d->aMouseSelect );
Laurent Montel's avatar
Laurent Montel committed
529
    connect( d->aMouseSelect, SIGNAL(triggered()), this, SLOT(slotSetMouseSelect()) );
Pino Toscano's avatar
Pino Toscano committed
530
    d->aMouseSelect->setIconText( i18nc( "Select Tool", "Selection" ) );
Pino Toscano's avatar
Pino Toscano committed
531
    d->aMouseSelect->setCheckable( true );
532
    d->aMouseSelect->setShortcut( Qt::CTRL + Qt::Key_3 );
533
    d->aMouseSelect->setActionGroup( d->mouseModeActionGroup );
534

535
    d->aMouseTextSelect  = new KAction(KIcon( "draw-text" ), i18n("&Text Selection Tool"), this);
Laurent Montel's avatar
Laurent Montel committed
536
    ac->addAction("mouse_textselect", d->aMouseTextSelect );
Laurent Montel's avatar
Laurent Montel committed
537
    connect( d->aMouseTextSelect, SIGNAL(triggered()), this, SLOT(slotSetMouseTextSelect()) );
Pino Toscano's avatar
Pino Toscano committed
538
    d->aMouseTextSelect->setIconText( i18nc( "Text Selection Tool", "Text Selection" ) );
539
    d->aMouseTextSelect->setCheckable( true );
540
    d->aMouseTextSelect->setShortcut( Qt::CTRL + Qt::Key_4 );
541
    d->aMouseTextSelect->setActionGroup( d->mouseModeActionGroup );
542

Jiri Baum's avatar
Jiri Baum committed
543
544
545
546
547
548
    d->aMouseTableSelect  = new KAction(KIcon( "select-table" ), i18n("T&able Selection Tool"), this);
    ac->addAction("mouse_tableselect", d->aMouseTableSelect );
    connect( d->aMouseTableSelect, SIGNAL( triggered() ), this, SLOT( slotSetMouseTableSelect() ) );
    d->aMouseTableSelect->setIconText( i18nc( "Table Selection Tool", "Table Selection" ) );
    d->aMouseTableSelect->setCheckable( true );
    d->aMouseTableSelect->setShortcut( Qt::CTRL + Qt::Key_5 );
549
    d->aMouseTableSelect->setActionGroup( d->mouseModeActionGroup );
Jiri Baum's avatar
Jiri Baum committed
550

Pino Toscano's avatar
Pino Toscano committed
551
    d->aToggleAnnotator  = new KToggleAction(KIcon( "draw-freehand" ), i18n("&Review"), this);
Laurent Montel's avatar
Laurent Montel committed
552
    ac->addAction("mouse_toggle_annotate", d->aToggleAnnotator );
Pino Toscano's avatar
Pino Toscano committed
553
    d->aToggleAnnotator->setCheckable( true );
Laurent Montel's avatar
Laurent Montel committed
554
    connect( d->aToggleAnnotator, SIGNAL(toggled(bool)), SLOT(slotToggleAnnotator(bool)) );
Albert Astals Cid's avatar
Albert Astals Cid committed
555
    d->aToggleAnnotator->setShortcut( Qt::Key_F6 );
556

557
558
559
560
    ToolAction *ta = new ToolAction( this );
    ac->addAction( "mouse_selecttools", ta );
    ta->addAction( d->aMouseSelect );
    ta->addAction( d->aMouseTextSelect );
561
    ta->addAction( d->aMouseTableSelect );
562

563
    // speak actions
564
565
566
    d->aSpeakDoc = new KAction( KIcon( "text-speak" ), i18n( "Speak Whole Document" ), this );
    ac->addAction( "speak_document", d->aSpeakDoc );
    d->aSpeakDoc->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
567
    connect( d->aSpeakDoc, SIGNAL(triggered()), SLOT(slotSpeakDocument()) );
568

569
570
571
    d->aSpeakPage = new KAction( KIcon( "text-speak" ), i18n( "Speak Current Page" ), this );
    ac->addAction( "speak_current_page", d->aSpeakPage );
    d->aSpeakPage->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
572
    connect( d->aSpeakPage, SIGNAL(triggered()), SLOT(slotSpeakCurrentPage()) );
573

574
575
576
    d->aSpeakStop = new KAction( KIcon( "media-playback-stop" ), i18n( "Stop Speaking" ), this );
    ac->addAction( "speak_stop_all", d->aSpeakStop );
    d->aSpeakStop->setEnabled( false );
Laurent Montel's avatar
Laurent Montel committed
577
    connect( d->aSpeakStop, SIGNAL(triggered()), SLOT(slotStopSpeaks()) );
578

579
    // Other actions
Laurent Montel's avatar
Laurent Montel committed
580
581
    KAction * su  = new KAction(i18n("Scroll Up"), this);
    ac->addAction("view_scroll_up", su );
Laurent Montel's avatar
Laurent Montel committed
582
    connect( su, SIGNAL(triggered()), this, SLOT(slotScrollUp()) );
583
    su->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_Up) );
Albert Astals Cid's avatar
Albert Astals Cid committed
584
    addAction(su);
585

Laurent Montel's avatar
Laurent Montel committed
586
587
    KAction * sd  = new KAction(i18n("Scroll Down"), this);
    ac->addAction("view_scroll_down", sd );
Laurent Montel's avatar
Laurent Montel committed
588
    connect( sd, SIGNAL(triggered()), this, SLOT(slotScrollDown()) );
589
    sd->setShortcut( QKeySequence(Qt::SHIFT + Qt::Key_Down) );
Albert Astals Cid's avatar
Albert Astals Cid committed
590
    addAction(sd);
591
592
593

    d->aToggleForms = new KAction( this );
    ac->addAction( "view_toggle_forms", d->aToggleForms );
Laurent Montel's avatar
Laurent Montel committed
594
    connect( d->aToggleForms, SIGNAL(triggered()), this, SLOT(slotToggleForms()) );
595
    d->aToggleForms->setEnabled( false );
596
    toggleFormWidgets( false );
597
598
}

Pino Toscano's avatar
Pino Toscano committed
599
bool PageView::canFitPageWidth() const
600
{
601
    return Okular::Settings::viewMode() != Okular::Settings::EnumViewMode::Single || d->zoomMode != ZoomFitWidth;
602
603
}

604
void PageView::fitPageWidth( int page )
605
{
606
    // zoom: Fit Width, columns: 1. setActions + relayout + setPage + update
607
    d->zoomMode = ZoomFitWidth;
608
    Okular::Settings::setViewMode( 0 );
609
610
    d->aZoomFitWidth->setChecked( true );
    d->aZoomFitPage->setChecked( false );
611
//    d->aZoomFitText->setChecked( false );
612
    d->aViewMode->menu()->actions().at( 0 )->setChecked( true );
613
614
615
    viewport()->setUpdatesEnabled( false );
    slotRelayoutPages();
    viewport()->setUpdatesEnabled( true );
616
    d->document->setViewportPage( page );
617
    updateZoomText();
Enrico Ros's avatar
Enrico Ros committed
618
    setFocus();
619
620
}

621
void PageView::setAnnotationWindow( Okular::Annotation * annotation )
622
{
623
    if ( !annotation )
624
        return;
625
626
627

    // find the annot window
    AnnotWindow* existWindow = 0;
Laurent Montel's avatar
Laurent Montel committed
628
629
    QHash< Okular::Annotation *, AnnotWindow * >::ConstIterator it = d->m_annowindows.constFind( annotation );
    if ( it != d->m_annowindows.constEnd() )
630
    {
631
        existWindow = *it;
632
    }
633
634

    if ( existWindow == 0 )
635
    {
636
637
        existWindow = new AnnotWindow( this, annotation );

638
        d->m_annowindows.insert( annotation, existWindow );
639
    }
640
641
642
643
644
645

    existWindow->show();
}

void PageView::removeAnnotationWindow( Okular::Annotation *annotation )
{
646
647
    QHash< Okular::Annotation *, AnnotWindow * >::Iterator it = d->m_annowindows.find( annotation );
    if ( it != d->m_annowindows.end() )
648
    {
649
650
        delete *it;
        d->m_annowindows.erase( it );
651
    }
652
653
}

654
void PageView::displayMessage( const QString & message, const QString & details, PageViewMessage::Icon icon, int duration )
655
{
656
    if ( !Okular::Settings::showOSD() )
657
658
    {
        if (icon == PageViewMessage::Error)
659
660
661
662
663
664
665
        {
            if ( !details.isEmpty() )
                KMessageBox::detailedError( this, message, details );
            else
                KMessageBox::error( this, message );
        }
        return;
666
    }
667
668
669
670
671
672

    // hide messageWindow if string is empty
    if ( message.isEmpty() )
        return d->messageWindow->hide();

    // display message (duration is length dependant)
673
    if (duration==-1)
674
    {
675
        duration = 500 + 100 * message.length();
676
677
678
679
        if ( !details.isEmpty() )
            duration += 500 + 100 * details.length();
    }
    d->messageWindow->display( message, details, icon, duration );
680
681
}

682
683
684
685
686
687
688
689
690
691
692
void PageView::reparseConfig()
{
    // set the scroll bars policies
    Qt::ScrollBarPolicy scrollBarMode = Okular::Settings::showScrollBars() ?
        Qt::ScrollBarAsNeeded : Qt::ScrollBarAlwaysOff;
    if ( horizontalScrollBarPolicy() != scrollBarMode )
    {
        setHorizontalScrollBarPolicy( scrollBarMode );
        setVerticalScrollBarPolicy( scrollBarMode );
    }

693
694
    if ( Okular::Settings::viewMode() == Okular::Settings::EnumViewMode::Summary &&
         ( (int)Okular::Settings::viewColumns() != d->setting_viewCols ) )
695
    {
696
        d->setting_viewCols = Okular::Settings::viewColumns();
697
698
699

        slotRelayoutPages();
    }
700
701
702

    updatePageStep();

703
704
}

705
706
707
708
709
KAction *PageView::toggleFormsAction() const
{
    return d->aToggleForms;
}

710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
int PageView::contentAreaWidth() const
{
    return horizontalScrollBar()->maximum() + viewport()->width();
}

int PageView::contentAreaHeight() const
{
    return verticalScrollBar()->maximum() + viewport()->height();
}

QPoint PageView::contentAreaPosition() const
{
    return QPoint( horizontalScrollBar()->value(), verticalScrollBar()->value() );
}

QPoint PageView::contentAreaPoint( const QPoint & pos ) const
{
    return pos + contentAreaPosition();
}

730
QString PageViewPrivate::selectedText() const
731
{
732
733
    if ( pagesWithTextSelection.isEmpty() )
        return QString();
734
735

    QString text;
736
    QList< int > selpages = pagesWithTextSelection.toList();
737
738
739
740
    qSort( selpages );
    const Okular::Page * pg = 0;
    if ( selpages.count() == 1 )
    {
741
        pg = document->page( selpages.first() );
742
        text.append( pg->text( pg->textSelection(), Okular::TextPage::CentralPixelTextAreaInclusionBehaviour ) );
743
744
745
    }
    else
    {
746
        pg = document->page( selpages.first() );
747
        text.append( pg->text( pg->textSelection(), Okular::TextPage::CentralPixelTextAreaInclusionBehaviour ) );
748
749
750
        int end = selpages.count() - 1;
        for( int i = 1; i < end; ++i )
        {
751
            pg = document->page( selpages.at( i ) );
752
            text.append( pg->text( 0, Okular::TextPage::CentralPixelTextAreaInclusionBehaviour ) );
753
        }
754
        pg = document->page( selpages.last() );
755
        text.append( pg->text( pg->textSelection(), Okular::TextPage::CentralPixelTextAreaInclusionBehaviour ) );
756
    }
757
758
759
760
761
762
    return text;
}

void PageView::copyTextSelection() const
{
    const QString text = d->selectedText();
763
764
765
766
767
768
769
    if ( !text.isEmpty() )
    {
        QClipboard *cb = QApplication::clipboard();
        cb->setText( text, QClipboard::Clipboard );
    }
}

770
771
772
773
774
void PageView::selectAll()
{
    if ( d->mouseMode != MouseTextSelect )
        return;

Laurent Montel's avatar
Laurent Montel committed
775
    QVector< PageViewItem * >::const_iterator it = d->items.constBegin(), itEnd = d->items.constEnd();
776
777
778
779
780
781
782
783
    for ( ; it < itEnd; ++it )
    {
        Okular::RegularAreaRect * area = textSelectionForItem( *it );
        d->pagesWithTextSelection.insert( (*it)->pageNumber() );
        d->document->setPageTextSelection( (*it)->pageNumber(), area, palette().color( QPalette::Active, QPalette::Highlight ) );
    }
}

784
//BEGIN DocumentObserver inherited methods
785
void PageView::notifySetup( const QVector< Okular::Page * > & pageSet, int setupFlags )
Enrico Ros's avatar
Enrico Ros committed
786
{
787
    bool documentChanged = setupFlags & Okular::DocumentObserver::DocumentChanged;
Enrico Ros's avatar
Enrico Ros committed
788
    // reuse current pages if nothing new
789
    if ( ( pageSet.count() == d->items.count() ) && !documentChanged && !( setupFlags & Okular::DocumentObserver::NewLayoutForPages ) )
Enrico Ros's avatar
Enrico Ros committed
790
791
792
    {
        int count = pageSet.count();
        for ( int i = 0; (i < count) && !documentChanged; i++ )
793
            if ( (int)pageSet[i]->number() != d->items[i]->pageNumber() )
Enrico Ros's avatar
Enrico Ros committed
794
795
796
797
                documentChanged = true;
        if ( !documentChanged )
            return;
    }
798

Enrico Ros's avatar
Enrico Ros committed
799
    // delete all widgets (one for each page in pageSet)
Laurent Montel's avatar
Laurent Montel committed
800
    QVector< PageViewItem * >::const_iterator dIt = d->items.constBegin(), dEnd = d->items.constEnd();