part.cpp 128 KB
Newer Older
1
/***************************************************************************
2 3 4 5 6 7 8 9 10 11 12 13 14
 *   Copyright (C) 2002 by Wilco Greven <greven@kde.org>                   *
 *   Copyright (C) 2002 by Chris Cheney <ccheney@cheney.cx>                *
 *   Copyright (C) 2002 by Malcolm Hunter <malcolm.hunter@gmx.co.uk>       *
 *   Copyright (C) 2003-2004 by Christophe Devriese                        *
 *                         <Christophe.Devriese@student.kuleuven.ac.be>    *
 *   Copyright (C) 2003 by Daniel Molkentin <molkentin@kde.org>            *
 *   Copyright (C) 2003 by Andy Goossens <andygoossens@telenet.be>         *
 *   Copyright (C) 2003 by Dirk Mueller <mueller@kde.org>                  *
 *   Copyright (C) 2003 by Laurent Montel <montel@kde.org>                 *
 *   Copyright (C) 2004 by Dominique Devriese <devriese@kde.org>           *
 *   Copyright (C) 2004 by Christoph Cullmann <crossfire@babylon2k.de>     *
 *   Copyright (C) 2004 by Henrique Pinto <stampede@coltec.ufmg.br>        *
 *   Copyright (C) 2004 by Waldo Bastian <bastian@kde.org>                 *
15
 *   Copyright (C) 2004-2008 by Albert Astals Cid <aacid@kde.org>          *
16
 *   Copyright (C) 2004 by Antti Markus <antti.markus@starman.ee>          *
17 18 19
 *   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             *
20 21 22 23 24 25 26
 *                                                                         *
 *   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
27 28
#include "part.h"

29
// qt/kde includes
30
#include <QApplication>
Albert Astals Cid's avatar
Albert Astals Cid committed
31
#include <QContextMenuEvent>
32 33
#include <QDialog>
#include <QDialogButtonBox>
34
#include <QFile>
35 36 37
#include <QFileDialog>
#include <QIcon>
#include <QInputDialog>
38
#include <QLabel>
Albert Astals Cid's avatar
Albert Astals Cid committed
39
#include <QLayout>
40 41 42
#include <QMenu>
#include <QPrintDialog>
#include <QPrintPreviewDialog>
Albert Astals Cid's avatar
Albert Astals Cid committed
43
#include <QPrinter>
44
#include <QScrollBar>
45 46
#include <QSlider>
#include <QSpinBox>
47
#include <QStandardPaths>
Albert Astals Cid's avatar
Albert Astals Cid committed
48 49
#include <QTemporaryFile>
#include <QTimer>
50
#include <QWidgetAction>
51

52
#include <KAboutPluginDialog>
53 54 55 56 57 58
#include <KActionCollection>
#include <KBookmarkAction>
#include <KDirWatch>
#include <KFilterBase>
#include <KFilterDev>
#include <KIO/Job>
59
#include <KIO/OpenFileManagerWindowJob>
60
#include <KJobWidgets>
61
#include <KMessageBox>
62
#include <KParts/GUIActivateEvent>
63 64
#include <KPasswordDialog>
#include <KPluginFactory>
65
#include <KPluginMetaData>
66 67 68 69
#include <KSharedDataCache>
#include <KStandardShortcut>
#include <KToggleAction>
#include <KToggleFullScreenAction>
70 71 72
#include <Kdelibs4ConfigMigrator>
#include <Kdelibs4Migration>
#ifdef WITH_KWALLET
73
#include <KWallet>
74
#endif
75 76 77
#include <KXMLGUIClient>
#include <KXMLGUIFactory>

Kevin Ottens's avatar
Kevin Ottens committed
78 79 80 81 82
#if PURPOSE_FOUND
#include <Purpose/AlternativesModel>
#include <PurposeWidgets/Menu>
#endif

83
#if 0
84
#include <knewstuff2/engine.h>
85
#endif
86

87
// local includes
88
#include "aboutdata.h"
89
#include "conf/preferencesdialog.h"
90
#include "core/action.h"
91
#include "core/annotations.h"
92
#include "core/bookmarkmanager.h"
93
#include "core/document.h"
94
#include "core/document_p.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
95
#include "core/fileprinter.h"
96
#include "core/form.h"
97
#include "core/generator.h"
98
#include "core/page.h"
99
#include "core/printoptionswidget.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
#include "extensions.h"
#include "settings.h"
#include "ui/bookmarklist.h"
#include "ui/debug_ui.h"
#include "ui/drawingtoolactions.h"
#include "ui/embeddedfilesdialog.h"
#include "ui/fileprinterpreview.h"
#include "ui/findbar.h"
#include "ui/guiutils.h"
#include "ui/layers.h"
#include "ui/minibar.h"
#include "ui/okmenutitle.h"
#include "ui/pagesizelabel.h"
#include "ui/pageview.h"
#include "ui/presentationwidget.h"
#include "ui/propertiesdialog.h"
#include "ui/searchwidget.h"
#include "ui/side_reviews.h"
#include "ui/sidebar.h"
119
#include "ui/signatureguiutils.h"
Albert Astals Cid's avatar
Albert Astals Cid committed
120 121 122
#include "ui/signaturepanel.h"
#include "ui/thumbnaillist.h"
#include "ui/toc.h"
123
#include "xmlgui_helper.h"
124
#include <memory>
125

126
#ifdef OKULAR_KEEP_FILE_OPEN
127 128
class FileKeeper
{
Albert Astals Cid's avatar
Albert Astals Cid committed
129 130 131 132 133
public:
    FileKeeper()
        : m_handle(nullptr)
    {
    }
134

Albert Astals Cid's avatar
Albert Astals Cid committed
135 136 137
    ~FileKeeper()
    {
    }
138

Albert Astals Cid's avatar
Albert Astals Cid committed
139 140 141 142 143
    void open(const QString &path)
    {
        if (!m_handle)
            m_handle = std::fopen(QFile::encodeName(path).constData(), "r");
    }
144

Albert Astals Cid's avatar
Albert Astals Cid committed
145 146 147 148 149 150
    void close()
    {
        if (m_handle) {
            int ret = std::fclose(m_handle);
            Q_UNUSED(ret)
            m_handle = nullptr;
151
        }
Albert Astals Cid's avatar
Albert Astals Cid committed
152
    }
153

Albert Astals Cid's avatar
Albert Astals Cid committed
154 155 156 157
    QTemporaryFile *copyToTemporary() const
    {
        if (!m_handle)
            return nullptr;
158

Albert Astals Cid's avatar
Albert Astals Cid committed
159 160
        QTemporaryFile *retFile = new QTemporaryFile;
        retFile->open();
161

Albert Astals Cid's avatar
Albert Astals Cid committed
162 163 164 165 166 167 168 169 170
        std::rewind(m_handle);
        int c = -1;
        do {
            c = std::fgetc(m_handle);
            if (c == EOF)
                break;
            if (!retFile->putChar((char)c))
                break;
        } while (!feof(m_handle));
171

Albert Astals Cid's avatar
Albert Astals Cid committed
172
        retFile->flush();
173

Albert Astals Cid's avatar
Albert Astals Cid committed
174 175
        return retFile;
    }
176

Albert Astals Cid's avatar
Albert Astals Cid committed
177 178
private:
    std::FILE *m_handle;
179
};
180
#endif
181

Frederik Gladhorn's avatar
Frederik Gladhorn committed
182 183
K_PLUGIN_FACTORY(OkularPartFactory, registerPlugin<Okular::Part>();)

Albert Astals Cid's avatar
Albert Astals Cid committed
184
static QAction *actionForExportFormat(const Okular::ExportFormat &format, QObject *parent = Q_NULLPTR)
185
{
Albert Astals Cid's avatar
Albert Astals Cid committed
186 187 188
    QAction *act = new QAction(format.description(), parent);
    if (!format.icon().isNull()) {
        act->setIcon(format.icon());
189 190 191 192
    }
    return act;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
193
static KFilterDev::CompressionType compressionTypeFor(const QString &mime_to_check)
194
{
195 196 197
    // The compressedMimeMap is here in case you have a very old shared mime database
    // that doesn't have inheritance info for things like gzeps, etc
    // Otherwise the "is()" calls below are just good enough
Albert Astals Cid's avatar
Albert Astals Cid committed
198
    static QHash<QString, KFilterDev::CompressionType> compressedMimeMap;
199 200
    static bool supportBzip = false;
    static bool supportXz = false;
Albert Astals Cid's avatar
Albert Astals Cid committed
201 202 203 204 205 206
    const QString app_gzip(QStringLiteral("application/x-gzip"));
    const QString app_bzip(QStringLiteral("application/x-bzip"));
    const QString app_xz(QStringLiteral("application/x-xz"));
    if (compressedMimeMap.isEmpty()) {
        std::unique_ptr<KFilterBase> f;
        compressedMimeMap[QStringLiteral("image/x-gzeps")] = KFilterDev::GZip;
207
        // check we can read bzip2-compressed files
Albert Astals Cid's avatar
Albert Astals Cid committed
208 209
        f.reset(KCompressionDevice::filterForCompressionType(KCompressionDevice::BZip2));
        if (f.get()) {
210
            supportBzip = true;
Albert Astals Cid's avatar
Albert Astals Cid committed
211 212 213 214
            compressedMimeMap[QStringLiteral("application/x-bzpdf")] = KFilterDev::BZip2;
            compressedMimeMap[QStringLiteral("application/x-bzpostscript")] = KFilterDev::BZip2;
            compressedMimeMap[QStringLiteral("application/x-bzdvi")] = KFilterDev::BZip2;
            compressedMimeMap[QStringLiteral("image/x-bzeps")] = KFilterDev::BZip2;
215
        }
216
        // check if we can read XZ-compressed files
Albert Astals Cid's avatar
Albert Astals Cid committed
217 218
        f.reset(KCompressionDevice::filterForCompressionType(KCompressionDevice::Xz));
        if (f.get()) {
219
            supportXz = true;
220
        }
221
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
222 223
    QHash<QString, KFilterDev::CompressionType>::const_iterator it = compressedMimeMap.constFind(mime_to_check);
    if (it != compressedMimeMap.constEnd())
224 225
        return it.value();

Frederik Gladhorn's avatar
Frederik Gladhorn committed
226
    QMimeDatabase db;
Albert Astals Cid's avatar
Albert Astals Cid committed
227 228 229
    QMimeType mime = db.mimeTypeForName(mime_to_check);
    if (mime.isValid()) {
        if (mime.inherits(app_gzip))
230
            return KFilterDev::GZip;
Albert Astals Cid's avatar
Albert Astals Cid committed
231
        else if (supportBzip && mime.inherits(app_bzip))
232
            return KFilterDev::BZip2;
Albert Astals Cid's avatar
Albert Astals Cid committed
233
        else if (supportXz && mime.inherits(app_xz))
234
            return KFilterDev::Xz;
235
    }
236

237
    return KFilterDev::None;
238 239
}

Albert Astals Cid's avatar
Albert Astals Cid committed
240
static Okular::EmbedMode detectEmbedMode(QWidget *parentWidget, QObject *parent, const QVariantList &args)
241
{
Albert Astals Cid's avatar
Albert Astals Cid committed
242
    Q_UNUSED(parentWidget);
243

Albert Astals Cid's avatar
Albert Astals Cid committed
244
    if (parent && (parent->objectName().startsWith(QLatin1String("okular::Shell")) || parent->objectName().startsWith(QLatin1String("okular/okular__Shell"))))
245
        return Okular::NativeShellMode;
246

Albert Astals Cid's avatar
Albert Astals Cid committed
247
    if (parent && (QByteArray("KHTMLPart") == parent->metaObject()->className()))
248
        return Okular::KHTMLPartMode;
249

Albert Astals Cid's avatar
Albert Astals Cid committed
250 251 252
    for (const QVariant &arg : args) {
        if (arg.type() == QVariant::String) {
            if (arg.toString() == QLatin1String("Print/Preview")) {
253
                return Okular::PrintPreviewMode;
Albert Astals Cid's avatar
Albert Astals Cid committed
254
            } else if (arg.toString() == QLatin1String("ViewerWidget")) {
255
                return Okular::ViewerWidgetMode;
256
            }
257 258 259
        }
    }

260
    return Okular::UnknownEmbedMode;
261 262
}

Albert Astals Cid's avatar
Albert Astals Cid committed
263
static QString detectConfigFileName(const QVariantList &args)
264
{
Albert Astals Cid's avatar
Albert Astals Cid committed
265 266
    for (const QVariant &arg : args) {
        if (arg.type() == QVariant::String) {
267
            QString argString = arg.toString();
Albert Astals Cid's avatar
Albert Astals Cid committed
268 269 270
            int separatorIndex = argString.indexOf(QStringLiteral("="));
            if (separatorIndex >= 0 && argString.left(separatorIndex) == QLatin1String("ConfigFileName")) {
                return argString.mid(separatorIndex + 1);
271 272 273 274
            }
        }
    }

Michel Ludwig's avatar
Michel Ludwig committed
275
    return QString();
276 277
}

278 279 280 281 282 283 284 285 286 287
#undef OKULAR_KEEP_FILE_OPEN

#ifdef OKULAR_KEEP_FILE_OPEN
static bool keepFileOpen()
{
    static bool keep_file_open = !qgetenv("OKULAR_NO_KEEP_FILE_OPEN").toInt();
    return keep_file_open;
}
#endif

288 289
int Okular::Part::numberOfParts = 0;

290 291
namespace Okular
{
Albert Astals Cid's avatar
Albert Astals Cid committed
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList &args)
    : KParts::ReadWritePart(parent)
    , m_tempfile(nullptr)
    , m_documentOpenWithPassword(false)
    , m_swapInsteadOfOpening(false)
    , m_tocEnabled(false)
    , m_isReloading(false)
    , m_fileWasRemoved(false)
    , m_showMenuBarAction(nullptr)
    , m_showFullScreenAction(nullptr)
    , m_actionsSearched(false)
    , m_cliPresentation(false)
    , m_cliPrint(false)
    , m_cliPrintAndExit(false)
    , m_embedMode(detectEmbedMode(parentWidget, parent, args))
    , m_generatorGuiClient(nullptr)
    , m_keeper(nullptr)
309
{
310 311 312 313
    // make sure that the component name is okular otherwise the XMLGUI .rc files are not found
    // when this part is used in an application other than okular (e.g. unit tests)
    setComponentName(QStringLiteral("okular"), QString());

314 315
    const QLatin1String configFileName("okularpartrc");

316
    // first, we check if a config file name has been specified
Albert Astals Cid's avatar
Albert Astals Cid committed
317
    QString configFilePath = detectConfigFileName(args);
318

Albert Astals Cid's avatar
Albert Astals Cid committed
319
    if (configFilePath.isEmpty()) {
320 321 322 323
        configFilePath = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QLatin1Char('/') + configFileName;
    }

    // Migrate old config
Albert Astals Cid's avatar
Albert Astals Cid committed
324
    if (!QFile::exists(configFilePath)) {
325 326 327
        qCDebug(OkularUiDebug) << "Did not find a config file, attempting to look for old config";

        // Migrate old config + UI
Albert Astals Cid's avatar
Albert Astals Cid committed
328
        Kdelibs4ConfigMigrator configMigrator(componentName());
329
        // UI file is handled automatically, we only need to specify config name because we're a part
Albert Astals Cid's avatar
Albert Astals Cid committed
330
        configMigrator.setConfigFiles(QStringList(configFileName));
331 332

        // If there's no old okular config to migrate, look for kpdf
Albert Astals Cid's avatar
Albert Astals Cid committed
333
        if (!configMigrator.migrate()) {
334 335 336 337
            qCDebug(OkularUiDebug) << "Did not find an old okular config file, attempting to look for kpdf config";

            // First try the automatic detection, using $KDEHOME etc.
            Kdelibs4Migration migration;
Albert Astals Cid's avatar
Albert Astals Cid committed
338
            QString kpdfConfig = migration.locateLocal("config", QStringLiteral("kpdfpartrc"));
339 340

            // Fallback just in case it tried e. g. ~/.kde4
Albert Astals Cid's avatar
Albert Astals Cid committed
341
            if (kpdfConfig.isEmpty()) {
342 343 344
                kpdfConfig = QDir::homePath() + QStringLiteral("/.kde/share/config/kpdfpartrc");
            }

Albert Astals Cid's avatar
Albert Astals Cid committed
345
            if (QFile::exists(kpdfConfig)) {
346
                qCDebug(OkularUiDebug) << "Found old kpdf config" << kpdfConfig << "copying to" << configFilePath;
Albert Astals Cid's avatar
Albert Astals Cid committed
347
                QFile::copy(kpdfConfig, configFilePath);
348 349 350 351 352 353
            } else {
                qCDebug(OkularUiDebug) << "Did not find an old kpdf config file";
            }
        } else {
            qCDebug(OkularUiDebug) << "Migrated old okular config";
        }
354
    }
355

Albert Astals Cid's avatar
Albert Astals Cid committed
356
    Okular::Settings::instance(configFilePath);
357

358 359 360 361 362 363 364
    // In part.rc 47 we introduced a new mandatory toolbar that kxmlgui doesn't know how to merge properly
    // so unfortunately we have to remove any customized part.rc that is older than 47
    const QStringList files = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("kxmlgui5/okular/part.rc"));
    for (const QString &file : files) {
        removeRCFileIfVersionSmallerThan(file, 47);
    }

365 366
    numberOfParts++;
    if (numberOfParts == 1) {
Albert Astals Cid's avatar
Albert Astals Cid committed
367
        m_registerDbusName = QStringLiteral("/okular");
368
    } else {
Albert Astals Cid's avatar
Albert Astals Cid committed
369
        m_registerDbusName = QStringLiteral("/okular%1").arg(numberOfParts);
370
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
371
    QDBusConnection::sessionBus().registerObject(m_registerDbusName, this, QDBusConnection::ExportScriptableSlots);
372

373 374
    // connect the started signal to tell the job the mimetypes we like,
    // and get some more information from it
Laurent Montel's avatar
Laurent Montel committed
375
    connect(this, &KParts::ReadOnlyPart::started, this, &Part::slotJobStarted);
376 377

    // connect the completed signal so we can put the window caption when loading remote files
378
    connect(this, QOverload<>::of(&Part::completed), this, &Part::setWindowTitleFromDocument);
Laurent Montel's avatar
Laurent Montel committed
379
    connect(this, &KParts::ReadOnlyPart::canceled, this, &Part::loadCancelled);
380

381 382
    // create browser extension (for printing when embedded into browser)
    m_bExtension = new BrowserExtension(this);
383
    // create live connect extension (for integrating with browser scripting)
Albert Astals Cid's avatar
Albert Astals Cid committed
384
    new OkularLiveConnectExtension(this);
385

Albert Astals Cid's avatar
Albert Astals Cid committed
386
    GuiUtils::addIconLoader(iconLoader());
387

Albert Astals Cid's avatar
Albert Astals Cid committed
388 389 390
    m_sidebar = new Sidebar(parentWidget);
    setWidget(m_sidebar);
    connect(m_sidebar, &Sidebar::urlsDropped, this, &Part::handleDroppedUrls);
Pino Toscano's avatar
Pino Toscano committed
391

392
    // build the document
393
    m_document = new Okular::Document(widget());
Albert Astals Cid's avatar
Albert Astals Cid committed
394 395 396 397 398 399 400 401 402 403 404
    connect(m_document, &Document::linkFind, this, &Part::slotFind);
    connect(m_document, &Document::linkGoToPage, this, &Part::slotGoToPage);
    connect(m_document, &Document::linkPresentation, this, &Part::slotShowPresentation);
    connect(m_document, &Document::linkEndPresentation, this, &Part::slotHidePresentation);
    connect(m_document, &Document::openUrl, this, &Part::openUrlFromDocument);
    connect(m_document->bookmarkManager(), &BookmarkManager::openUrl, this, &Part::openUrlFromBookmarks);
    connect(m_document, &Document::close, this, &Part::close);
    connect(m_document, &Document::undoHistoryCleanChanged, this, [this](bool clean) {
        setModified(!clean);
        setWindowTitleFromDocument();
    });
405

Albert Astals Cid's avatar
Albert Astals Cid committed
406 407
    if (parent && parent->metaObject()->indexOfSlot(QMetaObject::normalizedSignature("slotQuit()").constData()) != -1)
        connect(m_document, SIGNAL(quit()), parent, SLOT(slotQuit())); // clazy:exclude=old-style-connect
408
    else
Albert Astals Cid's avatar
Albert Astals Cid committed
409
        connect(m_document, &Document::quit, this, &Part::cannotQuit);
410 411 412 413 414 415 416 417
    // widgets: ^searchbar (toolbar containing label and SearchWidget)
    //      m_searchToolBar = new KToolBar( parentWidget, "searchBar" );
    //      m_searchToolBar->boxLayout()->setSpacing( KDialog::spacingHint() );
    //      QLabel * sLabel = new QLabel( i18n( "&Search:" ), m_searchToolBar, "kde toolbar widget" );
    //      m_searchWidget = new SearchWidget( m_searchToolBar, m_document );
    //      sLabel->setBuddy( m_searchWidget );
    //      m_searchToolBar->setStretchableWidget( m_searchWidget );

418
    // [left toolbox optional item: Table of Contents] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
419 420 421
    m_toc = new TOC(nullptr, m_document);
    connect(m_toc.data(), &TOC::hasTOC, this, &Part::enableTOC);
    connect(m_toc.data(), &TOC::rightClick, this, &Part::slotShowTOCMenu);
422

423
    // [left toolbox optional item: Layers] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
424 425
    m_layers = new Layers(nullptr, m_document);
    connect(m_layers.data(), &Layers::hasLayers, this, &Part::enableLayers);
426

427
    // [left toolbox: Thumbnails and Bookmarks] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
428 429 430
    QWidget *thumbsBox = new ThumbnailsBox(nullptr);
    thumbsBox->layout()->setSpacing(6);
    m_searchWidget = new SearchWidget(thumbsBox, m_document);
Laurent Montel's avatar
Laurent Montel committed
431
    thumbsBox->layout()->addWidget(m_searchWidget);
Albert Astals Cid's avatar
Albert Astals Cid committed
432
    m_thumbnailList = new ThumbnailList(thumbsBox, m_document);
Laurent Montel's avatar
Laurent Montel committed
433
    thumbsBox->layout()->addWidget(m_thumbnailList);
434
    //	ThumbnailController * m_tc = new ThumbnailController( thumbsBox, m_thumbnailList );
Albert Astals Cid's avatar
Albert Astals Cid committed
435 436
    connect(m_thumbnailList.data(), &ThumbnailList::rightClick, this, &Part::slotShowMenu);
    m_sidebar->addItem(thumbsBox, QIcon::fromTheme(QStringLiteral("view-preview")), i18n("Thumbnails"));
437

Albert Astals Cid's avatar
Albert Astals Cid committed
438
    m_sidebar->setCurrentItem(thumbsBox);
439 440

    // [left toolbox: Reviews] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
441 442
    m_reviewsWidget = new Reviews(nullptr, m_document);
    m_sidebar->addItem(m_reviewsWidget, QIcon::fromTheme(QStringLiteral("draw-freehand")), i18n("Annotations"));
443 444

    // [left toolbox: Bookmarks] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
445 446
    m_bookmarkList = new BookmarkList(m_document, nullptr);
    m_sidebar->addItem(m_bookmarkList, QIcon::fromTheme(QStringLiteral("bookmarks")), i18n("Bookmarks"));
447

448
    // [left toolbox optional item: Signature Panel] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
449 450
    m_signaturePanel = new SignaturePanel(m_document, nullptr);
    connect(m_signaturePanel.data(), &SignaturePanel::documentHasSignatures, this, &Part::enableSidebarSignaturesItem);
451

452
    // widgets: [../miniBarContainer] | []
453
#ifdef OKULAR_ENABLE_MINIBAR
Albert Astals Cid's avatar
Albert Astals Cid committed
454 455 456 457
    QWidget *miniBarContainer = new QWidget(0);
    m_sidebar->setBottomWidget(miniBarContainer);
    QVBoxLayout *miniBarLayout = new QVBoxLayout(miniBarContainer);
    miniBarLayout->setContentsMargins(0, 0, 0, 0);
458
    // widgets: [../[spacer/..]] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
459
    miniBarLayout->addItem(new QSpacerItem(6, 6, QSizePolicy::Fixed, QSizePolicy::Fixed));
460
    // widgets: [../[../MiniBar]] | []
Albert Astals Cid's avatar
Albert Astals Cid committed
461 462 463 464 465 466 467 468
    QFrame *bevelContainer = new QFrame(miniBarContainer);
    bevelContainer->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
    QVBoxLayout *bevelContainerLayout = new QVBoxLayout(bevelContainer);
    bevelContainerLayout->setContentsMargins(4, 4, 4, 4);
    m_progressWidget = new ProgressWidget(bevelContainer, m_document);
    bevelContainerLayout->addWidget(m_progressWidget);
    miniBarLayout->addWidget(bevelContainer);
    miniBarLayout->addItem(new QSpacerItem(6, 6, QSizePolicy::Fixed, QSizePolicy::Fixed));
469
#endif
470 471

    // widgets: [] | [right 'pageView']
Albert Astals Cid's avatar
Albert Astals Cid committed
472 473 474 475 476
    QWidget *rightContainer = new QWidget(nullptr);
    m_sidebar->setMainWidget(rightContainer);
    QVBoxLayout *rightLayout = new QVBoxLayout(rightContainer);
    rightLayout->setContentsMargins(0, 0, 0, 0);
    rightLayout->setSpacing(0);
477 478
    //	KToolBar * rtb = new KToolBar( rightContainer, "mainToolBarSS" );
    //	rightLayout->addWidget( rtb );
Albert Astals Cid's avatar
Albert Astals Cid committed
479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
    m_migrationMessage = new KMessageWidget(rightContainer);
    m_migrationMessage->setVisible(false);
    m_migrationMessage->setWordWrap(true);
    m_migrationMessage->setMessageType(KMessageWidget::Warning);
    m_migrationMessage->setText(
        i18n("This document contains annotations or form data that were saved internally by a previous Okular version. Internal storage is <b>no longer supported</b>.<br/>Please save to a file in order to move them if you want to continue "
             "to edit the document."));
    rightLayout->addWidget(m_migrationMessage);
    m_topMessage = new KMessageWidget(rightContainer);
    m_topMessage->setVisible(false);
    m_topMessage->setWordWrap(true);
    m_topMessage->setMessageType(KMessageWidget::Information);
    m_topMessage->setText(i18n("This document has embedded files. <a href=\"okular:/embeddedfiles\">Click here to see them</a> or go to File -> Embedded Files."));
    m_topMessage->setIcon(QIcon::fromTheme(QStringLiteral("mail-attachment")));
    connect(m_topMessage, &KMessageWidget::linkActivated, this, &Part::slotShowEmbeddedFiles);
    rightLayout->addWidget(m_topMessage);
    m_formsMessage = new KMessageWidget(rightContainer);
    m_formsMessage->setVisible(false);
    m_formsMessage->setWordWrap(true);
    m_formsMessage->setMessageType(KMessageWidget::Information);
    rightLayout->addWidget(m_formsMessage);
    m_infoMessage = new KMessageWidget(rightContainer);
    m_infoMessage->setVisible(false);
    m_infoMessage->setWordWrap(true);
    m_infoMessage->setMessageType(KMessageWidget::Information);
    rightLayout->addWidget(m_infoMessage);
505
    m_infoTimer = new QTimer();
Albert Astals Cid's avatar
Albert Astals Cid committed
506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
    m_infoTimer->setSingleShot(true);
    connect(m_infoTimer, &QTimer::timeout, m_infoMessage, &KMessageWidget::animatedHide);
    m_signatureMessage = new KMessageWidget(rightContainer);
    m_signatureMessage->setVisible(false);
    m_signatureMessage->setWordWrap(true);
    rightLayout->addWidget(m_signatureMessage);
    m_pageView = new PageView(rightContainer, m_document);
    QMetaObject::invokeMethod(m_pageView, "setFocus", Qt::QueuedConnection); // usability setting
    //    m_splitter->setFocusProxy(m_pageView);
    connect(m_pageView.data(), &PageView::rightClick, this, &Part::slotShowMenu);
    connect(m_pageView, &PageView::triggerSearch, this, [this](const QString &searchText) {
        m_findBar->startSearch(searchText);
        slotShowFindBar();
    });
    connect(m_document, &Document::error, this, &Part::errorMessage);
    connect(m_document, &Document::warning, this, &Part::warningMessage);
    connect(m_document, &Document::notice, this, &Part::noticeMessage);
    connect(m_document, &Document::sourceReferenceActivated, this, &Part::slotHandleActivatedSourceReference);
    connect(m_pageView.data(), &PageView::fitWindowToPage, this, &Part::fitWindowToPage);
    rightLayout->addWidget(m_pageView);
    m_layers->setPageView(m_pageView);
    m_signaturePanel->setPageView(m_pageView);
    m_findBar = new FindBar(m_document, rightContainer);
    rightLayout->addWidget(m_findBar);
    m_bottomBar = new QWidget(rightContainer);
    QHBoxLayout *bottomBarLayout = new QHBoxLayout(m_bottomBar);
    m_pageSizeLabel = new PageSizeLabel(m_bottomBar, m_document);
    bottomBarLayout->setContentsMargins(0, 0, 0, 0);
    bottomBarLayout->setSpacing(0);
    bottomBarLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Expanding, QSizePolicy::Minimum));
    m_miniBarLogic = new MiniBarLogic(this, m_document);
    m_miniBar = new MiniBar(m_bottomBar, m_miniBarLogic);
    bottomBarLayout->addWidget(m_miniBar);
    bottomBarLayout->addWidget(m_pageSizeLabel);
    rightLayout->addWidget(m_bottomBar);

    m_pageNumberTool = new MiniBar(nullptr, m_miniBarLogic);

    connect(m_findBar, &FindBar::forwardKeyPressEvent, m_pageView, &PageView::externalKeyPressEvent);
    connect(m_findBar, &FindBar::onCloseButtonPressed, m_pageView, QOverload<>::of(&PageView::setFocus));
    connect(m_miniBar, &MiniBar::forwardKeyPressEvent, m_pageView, &PageView::externalKeyPressEvent);
    connect(m_pageView.data(), &PageView::escPressed, m_findBar, &FindBar::resetSearch);
    connect(m_pageNumberTool, &MiniBar::forwardKeyPressEvent, m_pageView, &PageView::externalKeyPressEvent);

    connect(m_reviewsWidget.data(), &Reviews::openAnnotationWindow, m_pageView.data(), &PageView::openAnnotationWindow);
551 552

    // add document observers
Albert Astals Cid's avatar
Albert Astals Cid committed
553 554 555 556 557 558
    m_document->addObserver(this);
    m_document->addObserver(m_thumbnailList);
    m_document->addObserver(m_pageView);
    m_document->registerView(m_pageView);
    m_document->addObserver(m_toc);
    m_document->addObserver(m_miniBarLogic);
559
#ifdef OKULAR_ENABLE_MINIBAR
Albert Astals Cid's avatar
Albert Astals Cid committed
560
    m_document->addObserver(m_progressWidget);
561
#endif
Albert Astals Cid's avatar
Albert Astals Cid committed
562 563 564 565
    m_document->addObserver(m_reviewsWidget);
    m_document->addObserver(m_pageSizeLabel);
    m_document->addObserver(m_bookmarkList);
    m_document->addObserver(m_signaturePanel);
566

Albert Astals Cid's avatar
Albert Astals Cid committed
567
    connect(m_document->bookmarkManager(), &BookmarkManager::saved, this, &Part::slotRebuildBookmarkMenu);
568

569 570
    setupViewerActions();

Albert Astals Cid's avatar
Albert Astals Cid committed
571
    if (m_embedMode != ViewerWidgetMode) {
572
        setupActions();
Albert Astals Cid's avatar
Albert Astals Cid committed
573
    } else {
574 575
        setViewerShortcuts();
    }
576 577

    // document watcher and reloader
Albert Astals Cid's avatar
Albert Astals Cid committed
578 579 580 581 582 583 584
    m_watcher = new KDirWatch(this);
    connect(m_watcher, &KDirWatch::dirty, this, &Part::slotFileDirty);
    connect(m_watcher, &KDirWatch::created, this, &Part::slotFileDirty);
    connect(m_watcher, &KDirWatch::deleted, this, &Part::slotFileDirty);
    m_dirtyHandler = new QTimer(this);
    m_dirtyHandler->setSingleShot(true);
    connect(m_dirtyHandler, &QTimer::timeout, this, [this] { slotAttemptReload(); });
585 586 587

    slotNewConfig();

588
    // keep us informed when the user changes settings
Albert Astals Cid's avatar
Albert Astals Cid committed
589
    connect(Okular::Settings::self(), &KCoreConfigSkeleton::configChanged, this, &Part::slotNewConfig);
590

591 592
#ifdef HAVE_SPEECH
    // [SPEECH] check for TTS presence and usability
Albert Astals Cid's avatar
Albert Astals Cid committed
593
    Okular::Settings::setUseTTS(true);
594
    Okular::Settings::self()->save();
595
#endif
596

Albert Astals Cid's avatar
Albert Astals Cid committed
597
    rebuildBookmarkMenu(false);
598

Albert Astals Cid's avatar
Albert Astals Cid committed
599
    if (m_embedMode == ViewerWidgetMode) {
600
        // set the XML-UI resource file for the viewer mode
Laurent Montel's avatar
Laurent Montel committed
601
        setXMLFile(QStringLiteral("part-viewermode.rc"));
Albert Astals Cid's avatar
Albert Astals Cid committed
602
    } else {
603
        // set our main XML-UI resource file
Laurent Montel's avatar
Laurent Montel committed
604
        setXMLFile(QStringLiteral("part.rc"));
605 606
    }

Albert Astals Cid's avatar
Albert Astals Cid committed
607
    m_pageView->setupBaseActions(actionCollection());
608

Albert Astals Cid's avatar
Albert Astals Cid committed
609 610
    m_sidebar->setSidebarVisibility(false);
    if (m_embedMode != PrintPreviewMode) {
611
        // now set up actions that are required for all remaining modes
Albert Astals Cid's avatar
Albert Astals Cid committed
612
        m_pageView->setupViewerActions(actionCollection());
613
        // and if we are not in viewer mode, we want the full GUI
Albert Astals Cid's avatar
Albert Astals Cid committed
614
        if (m_embedMode != ViewerWidgetMode) {
615 616 617 618 619 620 621
            unsetDummyMode();
        }
    }

    // ensure history actions are in the correct state
    updateViewActions();

622
    // also update the state of the actions in the page view
Albert Astals Cid's avatar
Albert Astals Cid committed
623
    m_pageView->updateActionState(false, false);
624

Albert Astals Cid's avatar
Albert Astals Cid committed
625 626
    if (m_embedMode == NativeShellMode)
        m_sidebar->setAutoFillBackground(false);
627 628 629 630 631 632 633 634

#ifdef OKULAR_KEEP_FILE_OPEN
    m_keeper = new FileKeeper();
#endif
}

void Part::setupViewerActions()
{
635
    // ACTIONS
Albert Astals Cid's avatar
Albert Astals Cid committed
636
    KActionCollection *ac = actionCollection();
637 638

    // Page Traversal actions
Albert Astals Cid's avatar
Albert Astals Cid committed
639
    m_gotoPage = KStandardAction::gotoPage(this, SLOT(slotGoToPage()), ac);
640
    ac->setDefaultShortcuts(m_gotoPage, KStandardShortcut::gotoLine());
641
    // dirty way to activate gotopage when pressing miniBar's button
Albert Astals Cid's avatar
Albert Astals Cid committed
642 643
    connect(m_miniBar.data(), &MiniBar::gotoPage, m_gotoPage, &QAction::trigger);
    connect(m_pageNumberTool.data(), &MiniBar::gotoPage, m_gotoPage, &QAction::trigger);
644 645

    m_prevPage = KStandardAction::prior(this, SLOT(slotPreviousPage()), ac);
Albert Astals Cid's avatar
Albert Astals Cid committed
646 647 648
    m_prevPage->setIconText(i18nc("Previous page", "Previous"));
    m_prevPage->setToolTip(i18n("Go back to the Previous Page"));
    m_prevPage->setWhatsThis(i18n("Moves to the previous page of the document"));
649
    ac->setDefaultShortcut(m_prevPage, QKeySequence());
650
    // dirty way to activate prev page when pressing miniBar's button
Albert Astals Cid's avatar
Albert Astals Cid committed
651 652
    connect(m_miniBar.data(), &MiniBar::prevPage, m_prevPage, &QAction::trigger);
    connect(m_pageNumberTool.data(), &MiniBar::prevPage, m_prevPage, &QAction::trigger);
653
#ifdef OKULAR_ENABLE_MINIBAR
Albert Astals Cid's avatar
Albert Astals Cid committed
654
    connect(m_progressWidget, SIGNAL(prevPage()), m_prevPage, SLOT(trigger()));
655
#endif
656

Albert Astals Cid's avatar
Albert Astals Cid committed
657 658 659 660
    m_nextPage = KStandardAction::next(this, SLOT(slotNextPage()), ac);
    m_nextPage->setIconText(i18nc("Next page", "Next"));
    m_nextPage->setToolTip(i18n("Advance to the Next Page"));
    m_nextPage->setWhatsThis(i18n("Moves to the next page of the document"));
661
    ac->setDefaultShortcut(m_nextPage, QKeySequence());
662
    // dirty way to activate next page when pressing miniBar's button
Albert Astals Cid's avatar
Albert Astals Cid committed
663 664
    connect(m_miniBar.data(), &MiniBar::nextPage, m_nextPage, &QAction::trigger);
    connect(m_pageNumberTool.data(), &MiniBar::nextPage, m_nextPage, &QAction::trigger);
665
#ifdef OKULAR_ENABLE_MINIBAR
Albert Astals Cid's avatar
Albert Astals Cid committed
666
    connect(m_progressWidget, SIGNAL(nextPage()), m_nextPage, SLOT(trigger()));
667
#endif
668

Albert Astals Cid's avatar
Albert Astals Cid committed
669
    m_beginningOfDocument = KStandardAction::firstPage(this, SLOT(slotGotoFirst()), ac);
Laurent Montel's avatar
Laurent Montel committed
670
    ac->addAction(QStringLiteral("first_page"), m_beginningOfDocument);
Albert Astals Cid's avatar
Albert Astals Cid committed
671 672
    m_beginningOfDocument->setText(i18n("Beginning of the document"));
    m_beginningOfDocument->setWhatsThis(i18n("Moves to the beginning of the document"));
673

Albert Astals Cid's avatar
Albert Astals Cid committed
674 675 676 677
    m_endOfDocument = KStandardAction::lastPage(this, SLOT(slotGotoLast()), ac);
    ac->addAction(QStringLiteral("last_page"), m_endOfDocument);
    m_endOfDocument->setText(i18n("End of the document"));
    m_endOfDocument->setWhatsThis(i18n("Moves to the end of the document"));
678

Albert Astals Cid's avatar
Albert Astals Cid committed
679
    // we do not want back and next in history in the dummy mode
680 681
    m_historyBack = nullptr;
    m_historyNext = nullptr;
682

Albert Astals Cid's avatar
Albert Astals Cid committed
683
    m_addBookmark = KStandardAction::addBookmark(this, SLOT(slotAddBookmark()), ac);
684
    m_addBookmarkText = m_addBookmark->text();
685
    m_addBookmarkIcon = m_addBookmark->icon();
686

Laurent Montel's avatar
Laurent Montel committed
687
    m_renameBookmark = ac->addAction(QStringLiteral("rename_bookmark"));
Albert Astals Cid's avatar
Albert Astals Cid committed
688 689 690 691
    m_renameBookmark->setText(i18n("Rename Bookmark"));
    m_renameBookmark->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
    m_renameBookmark->setWhatsThis(i18n("Rename the current bookmark"));
    connect(m_renameBookmark, &QAction::triggered, this, &Part::slotRenameCurrentViewportBookmark);
692

Laurent Montel's avatar
Laurent Montel committed
693
    m_prevBookmark = ac->addAction(QStringLiteral("previous_bookmark"));
Albert Astals Cid's avatar
Albert Astals Cid committed
694 695 696 697
    m_prevBookmark->setText(i18n("Previous Bookmark"));
    m_prevBookmark->setIcon(QIcon::fromTheme(QStringLiteral("go-up-search")));
    m_prevBookmark->setWhatsThis(i18n("Go to the previous bookmark"));
    connect(m_prevBookmark, &QAction::triggered, this, &Part::slotPreviousBookmark);
698

Laurent Montel's avatar
Laurent Montel committed
699
    m_nextBookmark = ac->addAction(QStringLiteral("next_bookmark"));
Albert Astals Cid's avatar
Albert Astals Cid committed
700 701 702 703
    m_nextBookmark->setText(i18n("Next Bookmark"));
    m_nextBookmark->setIcon(QIcon::fromTheme(QStringLiteral("go-down-search")));
    m_nextBookmark->setWhatsThis(i18n("Go to the next bookmark"));
    connect(m_nextBookmark, &QAction::triggered, this, &Part::slotNextBookmark);
704

705
    m_copy = nullptr;
706

707
    m_selectAll = nullptr;
708
    m_selectCurrentPage = nullptr;
709

710
    // Find and other actions
Albert Astals Cid's avatar
Albert Astals Cid committed
711
    m_find = KStandardAction::find(this, SLOT(slotShowFindBar()), ac);
712
    QList<QKeySequence> s = m_find->shortcuts();
Albert Astals Cid's avatar
Albert Astals Cid committed
713
    s.append(QKeySequence(Qt::Key_Slash));
714
    ac->setDefaultShortcuts(m_find, s);
Albert Astals Cid's avatar
Albert Astals Cid committed
715
    m_find->setEnabled(false);
716

Albert Astals Cid's avatar
Albert Astals Cid committed
717 718
    m_findNext = KStandardAction::findNext(this, SLOT(slotFindNext()), ac);
    m_findNext->setEnabled(false);
719

Albert Astals Cid's avatar
Albert Astals Cid committed
720 721
    m_findPrev = KStandardAction::findPrev(this, SLOT(slotFindPrev()), ac);
    m_findPrev->setEnabled(false);
Pino Toscano's avatar
Pino Toscano committed
722

723
    m_save = nullptr;
724
    m_saveAs = nullptr;
725
    m_openContainingFolder = nullptr;
Pino Toscano's avatar
Pino Toscano committed
726

Albert Astals Cid's avatar
Albert Astals Cid committed
727 728 729 730
    QAction *prefs = KStandardAction::preferences(this, SLOT(slotPreferences()), ac);
    if (m_embedMode == NativeShellMode) {
        prefs->setText(i18n("Configure Okular..."));
    } else {
Pino Toscano's avatar
Pino Toscano committed
731
        // TODO: improve this message
Albert Astals Cid's avatar
Albert Astals Cid committed
732
        prefs->setText(i18n("Configure Viewer..."));
733 734
    }

Albert Astals Cid's avatar
Albert Astals Cid committed
735
    QAction *genPrefs = new QAction(ac);
Laurent Montel's avatar
Laurent Montel committed
736
    ac->addAction(QStringLiteral("options_configure_generators"), genPrefs);
Albert Astals Cid's avatar
Albert Astals Cid committed
737 738 739 740
    if (m_embedMode == ViewerWidgetMode) {
        genPrefs->setText(i18n("Configure Viewer Backends..."));
    } else {
        genPrefs->setText(i18n("Configure Backends..."));
741
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
742 743 744
    genPrefs->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
    genPrefs->setEnabled(m_document->configurableGenerators() > 0);
    connect(genPrefs, &QAction::triggered, this, &Part::slotGeneratorPreferences);
745

Albert Astals Cid's avatar
Albert Astals Cid committed
746 747
    m_printPreview = KStandardAction::printPreview(this, SLOT(slotPrintPreview()), ac);
    m_printPreview->setEnabled(false);
748

749 750
    m_showLeftPanel = nullptr;
    m_showBottomBar = nullptr;
751
    m_showSignaturePanel = nullptr;
752

Laurent Montel's avatar
Laurent Montel committed
753
    m_showProperties = ac->addAction(QStringLiteral("properties"));
754
    m_showProperties->setText(i18n("&Properties"));
Laurent Montel's avatar
Laurent Montel committed
755
    m_showProperties->setIcon(QIcon::fromTheme(QStringLiteral("document-properties")));
756
    ac->setDefaultShortcuts(m_showProperties, {QKeySequence(Qt::ALT + Qt::Key_Return)});
Laurent Montel's avatar
Laurent Montel committed
757
    connect(m_showProperties, &QAction::triggered, this, &Part::slotShowProperties);
Albert Astals Cid's avatar
Albert Astals Cid committed
758
    m_showProperties->setEnabled(false);
759

760 761
    m_showEmbeddedFiles = nullptr;
    m_showPresentation = nullptr;
762

763 764 765 766
    m_exportAs = nullptr;
    m_exportAsMenu = nullptr;
    m_exportAsText = nullptr;
    m_exportAsDocArchive = nullptr;
Kevin Ottens's avatar
Kevin Ottens committed
767 768 769 770 771 772

#if PURPOSE_FOUND
    m_share = nullptr;
    m_shareMenu = nullptr;
#endif

773
    m_presentationDrawingActions = nullptr;
774

Laurent Montel's avatar
Laurent Montel committed
775
    m_aboutBackend = ac->addAction(QStringLiteral("help_about_backend"));
776
    m_aboutBackend->setText(i18n("About Backend"));
Albert Astals Cid's avatar
Albert Astals Cid committed
777
    m_aboutBackend->setEnabled(false);
Laurent Montel's avatar
Laurent Montel committed
778
    connect(m_aboutBackend, &QAction::triggered, this, &Part::slotAboutBackend);
779

Albert Astals Cid's avatar
Albert Astals Cid committed
780 781 782 783 784
    QAction *reload = ac->add<QAction>(QStringLiteral("file_reload"));
    reload->setText(i18n("Reloa&d"));
    reload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
    reload->setWhatsThis(i18n("Reload the current document from disk."));
    connect(reload, &QAction::triggered, this, &Part::slotReload);
785
    ac->setDefaultShortcuts(reload, KStandardShortcut::reload());
786 787
    m_reload = reload;

Albert Astals Cid's avatar
Albert Astals Cid committed
788 789
    m_closeFindBar = ac->addAction(QStringLiteral("close_find_bar"), this, SLOT(slotHideFindBar()));
    m_closeFindBar->setText(i18n("Close &Find Bar"));
790
    ac->setDefaultShortcut(m_closeFindBar, QKeySequence(Qt::Key_Escape));
Albert Astals Cid's avatar
Albert Astals Cid committed
791
    m_closeFindBar->setEnabled(false);
792

Albert Astals Cid's avatar
Albert Astals Cid committed
793 794 795 796
    QWidgetAction *pageno = new QWidgetAction(ac);
    pageno->setText(i18n("Page Number"));
    pageno->setDefaultWidget(m_pageNumberTool);
    ac->addAction(QStringLiteral("page_number"), pageno);
797