1/***************************************************************************
2 * Copyright (C) 2002 by Wilco Greven <greven@kde.org> *
3 * Copyright (C) 2002 by Chris Cheney <ccheney@cheney.cx> *
4 * Copyright (C) 2002 by Malcolm Hunter <malcolm.hunter@gmx.co.uk> *
5 * Copyright (C) 2003-2004 by Christophe Devriese *
6 * <Christophe.Devriese@student.kuleuven.ac.be> *
7 * Copyright (C) 2003 by Daniel Molkentin <molkentin@kde.org> *
8 * Copyright (C) 2003 by Andy Goossens <andygoossens@telenet.be> *
9 * Copyright (C) 2003 by Dirk Mueller <mueller@kde.org> *
10 * Copyright (C) 2003 by Laurent Montel <montel@kde.org> *
11 * Copyright (C) 2004 by Dominique Devriese <devriese@kde.org> *
12 * Copyright (C) 2004 by Christoph Cullmann <crossfire@babylon2k.de> *
13 * Copyright (C) 2004 by Henrique Pinto <stampede@coltec.ufmg.br> *
14 * 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 * This program is free software; you can redistribute it and/or modify *
19 * it under the terms of the GNU General Public License as published by *
20 * the Free Software Foundation; either version 2 of the License, or *
21 * (at your option) any later version. *
22 ***************************************************************************/
23
24#include "part.h"
25
26// qt/kde includes
27#include <qapplication.h>
28#include <qfile.h>
29#include <qlayout.h>
30#include <qlabel.h>
31#include <qtimer.h>
32#include <QtGui/QPrinter>
33#include <QtGui/QPrintDialog>
34#include <QScrollBar>
35
36#include <kvbox.h>
37#include <kaboutapplicationdialog.h>
38#include <kaction.h>
39#include <kactioncollection.h>
40#include <kdirwatch.h>
41#include <kstandardaction.h>
42#include <kpluginfactory.h>
43#include <kfiledialog.h>
44#include <kinputdialog.h>
45#include <kmessagebox.h>
46#include <knuminput.h>
47#include <kio/netaccess.h>
48#include <kmenu.h>
49#include <kxmlguiclient.h>
50#include <kxmlguifactory.h>
51#include <kservicetypetrader.h>
52#include <kstandarddirs.h>
53#include <kstandardshortcut.h>
54#include <ktemporaryfile.h>
55#include <ktoggleaction.h>
56#include <ktogglefullscreenaction.h>
57#include <kio/job.h>
58#include <kicon.h>
59#include <kfilterdev.h>
60#include <kfilterbase.h>
61#if 0
62#include <knewstuff2/engine.h>
63#endif
64#include <kdeprintdialog.h>
65#include <kprintpreview.h>
66#include <kbookmarkmenu.h>
67#include <kpassworddialog.h>
68#include <kwallet.h>
69
70// local includes
71#include "aboutdata.h"
72#include "extensions.h"
73#include "ui/pageview.h"
74#include "ui/toc.h"
75#include "ui/searchwidget.h"
76#include "ui/thumbnaillist.h"
77#include "ui/side_reviews.h"
78#include "ui/minibar.h"
79#include "ui/embeddedfilesdialog.h"
80#include "ui/propertiesdialog.h"
81#include "ui/presentationwidget.h"
82#include "ui/pagesizelabel.h"
83#include "ui/bookmarklist.h"
84#include "ui/findbar.h"
85#include "ui/sidebar.h"
86#include "ui/fileprinterpreview.h"
87#include "ui/guiutils.h"
88#include "conf/preferencesdialog.h"
89#include "settings.h"
90#include "core/action.h"
91#include "core/annotations.h"
92#include "core/bookmarkmanager.h"
93#include "core/document.h"
94#include "core/generator.h"
95#include "core/page.h"
96#include "core/fileprinter.h"
97
98#include <cstdio>
99#include <memory>
100
101class FileKeeper
102{
103 public:
104 FileKeeper()
105 : m_handle( NULL )
106 {
107 }
108
109 ~FileKeeper()
110 {
111 }
112
113 void open( const QString & path )
114 {
115 if ( !m_handle )
116 m_handle = std::fopen( QFile::encodeName( path ), "r" );
117 }
118
119 void close()
120 {
121 if ( m_handle )
122 {
123 int ret = std::fclose( m_handle );
124 Q_UNUSED( ret )
125 m_handle = NULL;
126 }
127 }
128
129 KTemporaryFile* copyToTemporary() const
130 {
131 if ( !m_handle )
132 return 0;
133
134 KTemporaryFile * retFile = new KTemporaryFile;
135 retFile->open();
136
137 std::rewind( m_handle );
138 int c = -1;
139 do
140 {
141 c = std::fgetc( m_handle );
142 if ( c == EOF )
143 break;
144 if ( !retFile->putChar( (char)c ) )
145 break;
146 } while ( !feof( m_handle ) );
147
148 retFile->flush();
149
150 return retFile;
151 }
152
153 private:
154 std::FILE * m_handle;
155};
156
157Okular::PartFactory::PartFactory()
158: KPluginFactory(okularAboutData( "okular", I18N_NOOP( "Okular" ) ))
159{
160}
161
162Okular::PartFactory::~PartFactory()
163{
164}
165
166QObject *Okular::PartFactory::create(const char *iface, QWidget *parentWidget, QObject *parent, const QVariantList &args, const QString &keyword)
167{
168 Q_UNUSED ( keyword );
169
170 Okular::Part *object = new Okular::Part( parentWidget, parent, args, componentData() );
171 object->setReadWrite( QLatin1String(iface) == QLatin1String("KParts::ReadWritePart") );
172 return object;
173}
174
175K_EXPORT_PLUGIN( Okular::PartFactory() )
176
177static QAction* actionForExportFormat( const Okular::ExportFormat& format, QObject *parent = 0 )
178{
179 QAction *act = new QAction( format.description(), parent );
180 if ( !format.icon().isNull() )
181 {
182 act->setIcon( format.icon() );
183 }
184 return act;
185}
186
187static QString compressedMimeFor( const QString& mime_to_check )
188{
189 // The compressedMimeMap is here in case you have a very old shared mime database
190 // that doesn't have inheritance info for things like gzeps, etc
191 // Otherwise the "is()" calls below are just good enough
192 static QHash< QString, QString > compressedMimeMap;
193 static bool supportBzip = false;
194 static bool supportXz = false;
195 const QString app_gzip( QString::fromLatin1( "application/x-gzip" ) );
196 const QString app_bzip( QString::fromLatin1( "application/x-bzip" ) );
197 const QString app_xz( QString::fromLatin1( "application/x-xz" ) );
198 if ( compressedMimeMap.isEmpty() )
199 {
200 std::auto_ptr< KFilterBase > f;
201 compressedMimeMap[ QString::fromLatin1( "image/x-gzeps" ) ] = app_gzip;
202 // check we can read bzip2-compressed files
203 f.reset( KFilterBase::findFilterByMimeType( app_bzip ) );
204 if ( f.get() )
205 {
206 supportBzip = true;
207 compressedMimeMap[ QString::fromLatin1( "application/x-bzpdf" ) ] = app_bzip;
208 compressedMimeMap[ QString::fromLatin1( "application/x-bzpostscript" ) ] = app_bzip;
209 compressedMimeMap[ QString::fromLatin1( "application/x-bzdvi" ) ] = app_bzip;
210 compressedMimeMap[ QString::fromLatin1( "image/x-bzeps" ) ] = app_bzip;
211 }
212 // check we can read XZ-compressed files
213 f.reset( KFilterBase::findFilterByMimeType( app_xz ) );
214 if ( f.get() )
215 {
216 supportXz = true;
217 }
218 }
219 QHash< QString, QString >::const_iterator it = compressedMimeMap.constFind( mime_to_check );
220 if ( it != compressedMimeMap.constEnd() )
221 return it.value();
222
223 KMimeType::Ptr mime = KMimeType::mimeType( mime_to_check );
224 if ( mime )
225 {
226 if ( mime->is( app_gzip ) )
227 return app_gzip;
228 else if ( supportBzip && mime->is( app_bzip ) )
229 return app_bzip;
230 else if ( supportXz && mime->is( app_xz ) )
231 return app_xz;
232 }
233
234 return QString();
235}
236
237static Okular::EmbedMode detectEmbedMode( QWidget *parentWidget, QObject *parent, const QVariantList &args )
238{
239 Q_UNUSED( parentWidget );
240
241 if ( parent
242 && ( parent->objectName() == QLatin1String( "okular::Shell" )
243 || parent->objectName() == QLatin1String( "okular/okular__Shell" ) ) )
244 return Okular::NativeShellMode;
245
246 if ( parent
247 && ( QByteArray( "KHTMLPart" ) == parent->metaObject()->className() ) )
248 return Okular::KHTMLPartMode;
249
250 Q_FOREACH ( const QVariant &arg, args )
251 {
252 if ( arg.type() == QVariant::String )
253 {
254 if ( arg.toString() == QLatin1String( "Print/Preview" ) )
255 {
256 return Okular::PrintPreviewMode;
257 }
258 else if ( arg.toString() == QLatin1String( "ViewerWidget" ) )
259 {
260 return Okular::ViewerWidgetMode;
261 }
262 }
263 }
264
265 return Okular::UnknownEmbedMode;
266}
267
268static QString detectConfigFileName( const QVariantList &args )
269{
270 Q_FOREACH ( const QVariant &arg, args )
271 {
272 if ( arg.type() == QVariant::String )
273 {
274 QString argString = arg.toString();
275 int separatorIndex = argString.indexOf( "=" );
276 if ( separatorIndex >= 0 && argString.left( separatorIndex ) == QLatin1String( "ConfigFileName" ) )
277 {
278 return argString.mid( separatorIndex + 1 );
279 }
280 }
281 }
282
283 return QString();
284}
285
286#undef OKULAR_KEEP_FILE_OPEN
287
288#ifdef OKULAR_KEEP_FILE_OPEN
289static bool keepFileOpen()
290{
291 static bool keep_file_open = !qgetenv("OKULAR_NO_KEEP_FILE_OPEN").toInt();
292 return keep_file_open;
293}
294#endif
295
296int Okular::Part::numberOfParts = 0;
297
298namespace Okular
299{
300
301Part::Part(QWidget *parentWidget,
302QObject *parent,
303const QVariantList &args,
304KComponentData componentData )
305: KParts::ReadWritePart(parent),
306m_tempfile( 0 ), m_fileWasRemoved( false ), m_showMenuBarAction( 0 ), m_showFullScreenAction( 0 ), m_actionsSearched( false ),
307m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentWidget, parent, args)), m_generatorGuiClient(0), m_keeper( 0 )
308{
309 // first, we check if a config file name has been specified
310 QString configFileName = detectConfigFileName( args );
311 if ( configFileName.isEmpty() )
312 {
313 configFileName = KStandardDirs::locateLocal( "config", "okularpartrc" );
314 // first necessary step: copy the configuration from kpdf, if available
315 if ( !QFile::exists( configFileName ) )
316 {
317 QString oldkpdfconffile = KStandardDirs::locateLocal( "config", "kpdfpartrc" );
318 if ( QFile::exists( oldkpdfconffile ) )
319 QFile::copy( oldkpdfconffile, configFileName );
320 }
321 }
322 Okular::Settings::instance( configFileName );
323
324 numberOfParts++;
325 if (numberOfParts == 1) {
326 QDBusConnection::sessionBus().registerObject("/okular", this, QDBusConnection::ExportScriptableSlots);
327 } else {
328 QDBusConnection::sessionBus().registerObject(QString("/okular%1").arg(numberOfParts), this, QDBusConnection::ExportScriptableSlots);
329 }
330
331 // connect the started signal to tell the job the mimetypes we like,
332 // and get some more information from it
333 connect(this, SIGNAL(started(KIO::Job*)), this, SLOT(slotJobStarted(KIO::Job*)));
334
335 // connect the completed signal so we can put the window caption when loading remote files
336 connect(this, SIGNAL(completed()), this, SLOT(setWindowTitleFromDocument()));
337 connect(this, SIGNAL(canceled(QString)), this, SLOT(loadCancelled(QString)));
338
339 // create browser extension (for printing when embedded into browser)
340 m_bExtension = new BrowserExtension(this);
341 // create live connect extension (for integrating with browser scripting)
342 new OkularLiveConnectExtension( this );
343
344 // we need an instance
345 setComponentData( componentData );
346
347 GuiUtils::addIconLoader( iconLoader() );
348
349 m_sidebar = new Sidebar( parentWidget );
350 setWidget( m_sidebar );
351 connect( m_sidebar, SIGNAL(urlsDropped(KUrl::List)), SLOT(handleDroppedUrls(KUrl::List)) );
352
353 // build the document
354 m_document = new Okular::Document(widget());
355 connect( m_document, SIGNAL(linkFind()), this, SLOT(slotFind()) );
356 connect( m_document, SIGNAL(linkGoToPage()), this, SLOT(slotGoToPage()) );
357 connect( m_document, SIGNAL(linkPresentation()), this, SLOT(slotShowPresentation()) );
358 connect( m_document, SIGNAL(linkEndPresentation()), this, SLOT(slotHidePresentation()) );
359 connect( m_document, SIGNAL(openUrl(KUrl)), this, SLOT(openUrlFromDocument(KUrl)) );
360 connect( m_document->bookmarkManager(), SIGNAL(openUrl(KUrl)), this, SLOT(openUrlFromBookmarks(KUrl)) );
361 connect( m_document, SIGNAL(close()), this, SLOT(close()) );
362
363 if ( parent && parent->metaObject()->indexOfSlot( QMetaObject::normalizedSignature( "slotQuit()" ) ) != -1 )
364 connect( m_document, SIGNAL(quit()), parent, SLOT(slotQuit()) );
365 else
366 connect( m_document, SIGNAL(quit()), this, SLOT(cannotQuit()) );
367 // widgets: ^searchbar (toolbar containing label and SearchWidget)
368 // m_searchToolBar = new KToolBar( parentWidget, "searchBar" );
369 // m_searchToolBar->boxLayout()->setSpacing( KDialog::spacingHint() );
370 // QLabel * sLabel = new QLabel( i18n( "&Search:" ), m_searchToolBar, "kde toolbar widget" );
371 // m_searchWidget = new SearchWidget( m_searchToolBar, m_document );
372 // sLabel->setBuddy( m_searchWidget );
373 // m_searchToolBar->setStretchableWidget( m_searchWidget );
374
375 int tbIndex;
376 // [left toolbox: Table of Contents] | []
377 m_toc = new TOC( 0, m_document );
378 connect( m_toc, SIGNAL(hasTOC(bool)), this, SLOT(enableTOC(bool)) );
379 tbIndex = m_sidebar->addItem( m_toc, KIcon(QApplication::isLeftToRight() ? "format-justify-left" : "format-justify-right"), i18n("Contents") );
380 enableTOC( false );
381
382 // [left toolbox: Thumbnails and Bookmarks] | []
383 KVBox * thumbsBox = new ThumbnailsBox( 0 );
384 thumbsBox->setSpacing( 6 );
385 m_searchWidget = new SearchWidget( thumbsBox, m_document );
386 m_thumbnailList = new ThumbnailList( thumbsBox, m_document );
387 // ThumbnailController * m_tc = new ThumbnailController( thumbsBox, m_thumbnailList );
388 connect( m_thumbnailList, SIGNAL(rightClick(const Okular::Page*,QPoint)), this, SLOT(slotShowMenu(const Okular::Page*,QPoint)) );
389 tbIndex = m_sidebar->addItem( thumbsBox, KIcon( "view-preview" ), i18n("Thumbnails") );
390 m_sidebar->setCurrentIndex( tbIndex );
391
392 // [left toolbox: Reviews] | []
393 m_reviewsWidget = new Reviews( 0, m_document );
394 m_sidebar->addItem( m_reviewsWidget, KIcon("draw-freehand"), i18n("Reviews") );
395 m_sidebar->setItemEnabled( 2, false );
396
397 // [left toolbox: Bookmarks] | []
398 m_bookmarkList = new BookmarkList( m_document, 0 );
399 m_sidebar->addItem( m_bookmarkList, KIcon("bookmarks"), i18n("Bookmarks") );
400 m_sidebar->setItemEnabled( 3, false );
401
402 // widgets: [../miniBarContainer] | []
403#ifdef OKULAR_ENABLE_MINIBAR
404 QWidget * miniBarContainer = new QWidget( 0 );
405 m_sidebar->setBottomWidget( miniBarContainer );
406 QVBoxLayout * miniBarLayout = new QVBoxLayout( miniBarContainer );
407 miniBarLayout->setMargin( 0 );
408 // widgets: [../[spacer/..]] | []
409 miniBarLayout->addItem( new QSpacerItem( 6, 6, QSizePolicy::Fixed, QSizePolicy::Fixed ) );
410 // widgets: [../[../MiniBar]] | []
411 QFrame * bevelContainer = new QFrame( miniBarContainer );
412 bevelContainer->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken );
413 QVBoxLayout * bevelContainerLayout = new QVBoxLayout( bevelContainer );
414 bevelContainerLayout->setMargin( 4 );
415 m_progressWidget = new ProgressWidget( bevelContainer, m_document );
416 bevelContainerLayout->addWidget( m_progressWidget );
417 miniBarLayout->addWidget( bevelContainer );
418 miniBarLayout->addItem( new QSpacerItem( 6, 6, QSizePolicy::Fixed, QSizePolicy::Fixed ) );
419#endif
420
421 // widgets: [] | [right 'pageView']
422 QWidget * rightContainer = new QWidget( 0 );
423 m_sidebar->setMainWidget( rightContainer );
424 QVBoxLayout * rightLayout = new QVBoxLayout( rightContainer );
425 rightLayout->setMargin( 0 );
426 rightLayout->setSpacing( 0 );
427 // KToolBar * rtb = new KToolBar( rightContainer, "mainToolBarSS" );
428 // rightLayout->addWidget( rtb );
429 m_topMessage = new KMessageWidget( rightContainer );
430 m_topMessage->setVisible( false );
431 m_topMessage->setWordWrap( true );
432 m_topMessage->setMessageType( KMessageWidget::Information );
433 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." ) );
434 m_topMessage->setIcon( KIcon( "mail-attachment" ) );
435 connect( m_topMessage, SIGNAL(linkActivated(QString)), this, SLOT(slotShowEmbeddedFiles()) );
436 rightLayout->addWidget( m_topMessage );
437 m_formsMessage = new KMessageWidget( rightContainer );
438 m_formsMessage->setVisible( false );
439 m_formsMessage->setWordWrap( true );
440 m_formsMessage->setMessageType( KMessageWidget::Information );
441 rightLayout->addWidget( m_formsMessage );
442 m_infoMessage = new KMessageWidget( rightContainer );
443 m_infoMessage->setVisible( false );
444 m_infoMessage->setWordWrap( true );
445 m_infoMessage->setMessageType( KMessageWidget::Information );
446 rightLayout->addWidget( m_infoMessage );
447 m_infoTimer = new QTimer();
448 m_infoTimer->setSingleShot( true );
449 connect( m_infoTimer, SIGNAL(timeout()), m_infoMessage, SLOT(animatedHide()) );
450 m_pageView = new PageView( rightContainer, m_document );
451 QMetaObject::invokeMethod( m_pageView, "setFocus", Qt::QueuedConnection ); //usability setting
452// m_splitter->setFocusProxy(m_pageView);
453 connect( m_pageView, SIGNAL(rightClick(const Okular::Page*,QPoint)), this, SLOT(slotShowMenu(const Okular::Page*,QPoint)) );
454 connect( m_document, SIGNAL(error(QString,int)), this, SLOT(errorMessage(QString,int)) );
455 connect( m_document, SIGNAL(warning(QString,int)), this, SLOT(warningMessage(QString,int)) );
456 connect( m_document, SIGNAL(notice(QString,int)), this, SLOT(noticeMessage(QString,int)) );
457 connect( m_document, SIGNAL(sourceReferenceActivated(const QString&,int,int,bool*)), this, SLOT(slotHandleActivatedSourceReference(const QString&,int,int,bool*)) );
458 rightLayout->addWidget( m_pageView );
459 m_findBar = new FindBar( m_document, rightContainer );
460 rightLayout->addWidget( m_findBar );
461 m_bottomBar = new QWidget( rightContainer );
462 QHBoxLayout * bottomBarLayout = new QHBoxLayout( m_bottomBar );
463 m_pageSizeLabel = new PageSizeLabel( m_bottomBar, m_document );
464 bottomBarLayout->setMargin( 0 );
465 bottomBarLayout->setSpacing( 0 );
466 bottomBarLayout->addItem( new QSpacerItem( 5, 5, QSizePolicy::Expanding, QSizePolicy::Minimum ) );
467 m_miniBarLogic = new MiniBarLogic( this, m_document );
468 m_miniBar = new MiniBar( m_bottomBar, m_miniBarLogic );
469 bottomBarLayout->addWidget( m_miniBar );
470 bottomBarLayout->addWidget( m_pageSizeLabel );
471 rightLayout->addWidget( m_bottomBar );
472
473 m_pageNumberTool = new MiniBar( 0, m_miniBarLogic );
474
475 connect( m_findBar, SIGNAL(forwardKeyPressEvent(QKeyEvent*)), m_pageView, SLOT(externalKeyPressEvent(QKeyEvent*)));
476 connect( m_miniBar, SIGNAL(forwardKeyPressEvent(QKeyEvent*)), m_pageView, SLOT(externalKeyPressEvent(QKeyEvent*)));
477 connect( m_pageView, SIGNAL(escPressed()), m_findBar, SLOT(resetSearch()) );
478 connect( m_pageNumberTool, SIGNAL(forwardKeyPressEvent(QKeyEvent*)), m_pageView, SLOT(externalKeyPressEvent(QKeyEvent*)));
479
480 connect( m_reviewsWidget, SIGNAL(openAnnotationWindow(Okular::Annotation*,int)),
481 m_pageView, SLOT(openAnnotationWindow(Okular::Annotation*,int)) );
482
483 // add document observers
484 m_document->addObserver( this );
485 m_document->addObserver( m_thumbnailList );
486 m_document->addObserver( m_pageView );
487 m_document->registerView( m_pageView );
488 m_document->addObserver( m_toc );
489 m_document->addObserver( m_miniBarLogic );
490#ifdef OKULAR_ENABLE_MINIBAR
491 m_document->addObserver( m_progressWidget );
492#endif
493 m_document->addObserver( m_reviewsWidget );
494 m_document->addObserver( m_pageSizeLabel );
495 m_document->addObserver( m_bookmarkList );
496
497 connect( m_document->bookmarkManager(), SIGNAL(saved()),
498 this, SLOT(slotRebuildBookmarkMenu()) );
499
500 setupViewerActions();
501
502 if ( m_embedMode != ViewerWidgetMode )
503 {
504 setupActions();
505 }
506 else
507 {
508 setViewerShortcuts();
509 }
510
511 // document watcher and reloader
512 m_watcher = new KDirWatch( this );
513 connect( m_watcher, SIGNAL(dirty(QString)), this, SLOT(slotFileDirty(QString)) );
514 m_dirtyHandler = new QTimer( this );
515 m_dirtyHandler->setSingleShot( true );
516 connect( m_dirtyHandler, SIGNAL(timeout()),this, SLOT(slotDoFileDirty()) );
517
518 slotNewConfig();
519
520 // keep us informed when the user changes settings
521 connect( Okular::Settings::self(), SIGNAL(configChanged()), this, SLOT(slotNewConfig()) );
522
523 // [SPEECH] check for KTTSD presence and usability
524 const KService::Ptr kttsd = KService::serviceByDesktopName("kttsd");
525 Okular::Settings::setUseKTTSD( kttsd );
526 Okular::Settings::self()->writeConfig();
527
528 rebuildBookmarkMenu( false );
529
530 if ( m_embedMode == ViewerWidgetMode ) {
531 // set the XML-UI resource file for the viewer mode
532 setXMLFile("part-viewermode.rc");
533 }
534 else
535 {
536 // set our main XML-UI resource file
537 setXMLFile("part.rc");
538 }
539
540 m_pageView->setupBaseActions( actionCollection() );
541
542 m_sidebar->setSidebarVisibility( false );
543 if ( m_embedMode != PrintPreviewMode )
544 {
545 // now set up actions that are required for all remaining modes
546 m_pageView->setupViewerActions( actionCollection() );
547 // and if we are not in viewer mode, we want the full GUI
548 if ( m_embedMode != ViewerWidgetMode )
549 {
550 unsetDummyMode();
551 }
552 }
553
554 // ensure history actions are in the correct state
555 updateViewActions();
556
557 // also update the state of the actions in the page view
558 m_pageView->updateActionState( false, false, false );
559
560 if ( m_embedMode == NativeShellMode )
561 m_sidebar->setAutoFillBackground( false );
562
563#ifdef OKULAR_KEEP_FILE_OPEN
564 m_keeper = new FileKeeper();
565#endif
566}
567
568void Part::setupViewerActions()
569{
570 // ACTIONS
571 KActionCollection * ac = actionCollection();
572
573 // Page Traversal actions
574 m_gotoPage = KStandardAction::gotoPage( this, SLOT(slotGoToPage()), ac );
575 m_gotoPage->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_G) );
576 // dirty way to activate gotopage when pressing miniBar's button
577 connect( m_miniBar, SIGNAL(gotoPage()), m_gotoPage, SLOT(trigger()) );
578 connect( m_pageNumberTool, SIGNAL(gotoPage()), m_gotoPage, SLOT(trigger()) );
579
580 m_prevPage = KStandardAction::prior(this, SLOT(slotPreviousPage()), ac);
581 m_prevPage->setIconText( i18nc( "Previous page", "Previous" ) );
582 m_prevPage->setToolTip( i18n( "Go back to the Previous Page" ) );
583 m_prevPage->setWhatsThis( i18n( "Moves to the previous page of the document" ) );
584 m_prevPage->setShortcut( 0 );
585 // dirty way to activate prev page when pressing miniBar's button
586 connect( m_miniBar, SIGNAL(prevPage()), m_prevPage, SLOT(trigger()) );
587 connect( m_pageNumberTool, SIGNAL(prevPage()), m_prevPage, SLOT(trigger()) );
588#ifdef OKULAR_ENABLE_MINIBAR
589 connect( m_progressWidget, SIGNAL(prevPage()), m_prevPage, SLOT(trigger()) );
590#endif
591
592 m_nextPage = KStandardAction::next(this, SLOT(slotNextPage()), ac );
593 m_nextPage->setIconText( i18nc( "Next page", "Next" ) );
594 m_nextPage->setToolTip( i18n( "Advance to the Next Page" ) );
595 m_nextPage->setWhatsThis( i18n( "Moves to the next page of the document" ) );
596 m_nextPage->setShortcut( 0 );
597 // dirty way to activate next page when pressing miniBar's button
598 connect( m_miniBar, SIGNAL(nextPage()), m_nextPage, SLOT(trigger()) );
599 connect( m_pageNumberTool, SIGNAL(nextPage()), m_nextPage, SLOT(trigger()) );
600#ifdef OKULAR_ENABLE_MINIBAR
601 connect( m_progressWidget, SIGNAL(nextPage()), m_nextPage, SLOT(trigger()) );
602#endif
603
604 m_beginningOfDocument = KStandardAction::firstPage( this, SLOT(slotGotoFirst()), ac );
605 ac->addAction("first_page", m_beginningOfDocument);
606 m_beginningOfDocument->setText(i18n( "Beginning of the document"));
607 m_beginningOfDocument->setWhatsThis( i18n( "Moves to the beginning of the document" ) );
608
609 m_endOfDocument = KStandardAction::lastPage( this, SLOT(slotGotoLast()), ac );
610 ac->addAction("last_page",m_endOfDocument);
611 m_endOfDocument->setText(i18n( "End of the document"));
612 m_endOfDocument->setWhatsThis( i18n( "Moves to the end of the document" ) );
613
614 // we do not want back and next in history in the dummy mode
615 m_historyBack = 0;
616 m_historyNext = 0;
617
618 m_addBookmark = KStandardAction::addBookmark( this, SLOT(slotAddBookmark()), ac );
619 m_addBookmarkText = m_addBookmark->text();
620 m_addBookmarkIcon = m_addBookmark->icon();
621
622 m_renameBookmark = ac->addAction("rename_bookmark");
623 m_renameBookmark->setText(i18n( "Rename Bookmark" ));
624 m_renameBookmark->setIcon(KIcon( "edit-rename" ));
625 m_renameBookmark->setWhatsThis( i18n( "Rename the current bookmark" ) );
626 connect( m_renameBookmark, SIGNAL(triggered()), this, SLOT(slotRenameCurrentViewportBookmark()) );
627
628 m_prevBookmark = ac->addAction("previous_bookmark");
629 m_prevBookmark->setText(i18n( "Previous Bookmark" ));
630 m_prevBookmark->setIcon(KIcon( "go-up-search" ));
631 m_prevBookmark->setWhatsThis( i18n( "Go to the previous bookmark" ) );
632 connect( m_prevBookmark, SIGNAL(triggered()), this, SLOT(slotPreviousBookmark()) );
633
634 m_nextBookmark = ac->addAction("next_bookmark");
635 m_nextBookmark->setText(i18n( "Next Bookmark" ));
636 m_nextBookmark->setIcon(KIcon( "go-down-search" ));
637 m_nextBookmark->setWhatsThis( i18n( "Go to the next bookmark" ) );
638 connect( m_nextBookmark, SIGNAL(triggered()), this, SLOT(slotNextBookmark()) );
639
640 m_copy = 0;
641
642 m_selectAll = 0;
643
644 // Find and other actions
645 m_find = KStandardAction::find( this, SLOT(slotShowFindBar()), ac );
646 QList<QKeySequence> s = m_find->shortcuts();
647 s.append( QKeySequence( Qt::Key_Slash ) );
648 m_find->setShortcuts( s );
649 m_find->setEnabled( false );
650
651 m_findNext = KStandardAction::findNext( this, SLOT(slotFindNext()), ac);
652 m_findNext->setEnabled( false );
653
654 m_findPrev = KStandardAction::findPrev( this, SLOT(slotFindPrev()), ac );
655 m_findPrev->setEnabled( false );
656
657 m_saveCopyAs = 0;
658 m_saveAs = 0;
659
660 QAction * prefs = KStandardAction::preferences( this, SLOT(slotPreferences()), ac);
661 if ( m_embedMode == NativeShellMode )
662 {
663 prefs->setText( i18n( "Configure Okular..." ) );
664 }
665 else
666 {
667 // TODO: improve this message
668 prefs->setText( i18n( "Configure Viewer..." ) );
669 }
670
671 KAction * genPrefs = new KAction( ac );
672 ac->addAction("options_configure_generators", genPrefs);
673 if ( m_embedMode == ViewerWidgetMode )
674 {
675 genPrefs->setText( i18n( "Configure Viewer Backends..." ) );
676 }
677 else
678 {
679 genPrefs->setText( i18n( "Configure Backends..." ) );
680 }
681 genPrefs->setIcon( KIcon( "configure" ) );
682 genPrefs->setEnabled( m_document->configurableGenerators() > 0 );
683 connect( genPrefs, SIGNAL(triggered(bool)), this, SLOT(slotGeneratorPreferences()) );
684
685 m_printPreview = KStandardAction::printPreview( this, SLOT(slotPrintPreview()), ac );
686 m_printPreview->setEnabled( false );
687
688 m_showLeftPanel = 0;
689 m_showBottomBar = 0;
690
691 m_showProperties = ac->addAction("properties");
692 m_showProperties->setText(i18n("&Properties"));
693 m_showProperties->setIcon(KIcon("document-properties"));
694 connect(m_showProperties, SIGNAL(triggered()), this, SLOT(slotShowProperties()));
695 m_showProperties->setEnabled( false );
696
697 m_showEmbeddedFiles = 0;
698 m_showPresentation = 0;
699
700 m_exportAs = 0;
701 m_exportAsMenu = 0;
702 m_exportAsText = 0;
703 m_exportAsDocArchive = 0;
704
705 m_aboutBackend = ac->addAction("help_about_backend");
706 m_aboutBackend->setText(i18n("About Backend"));
707 m_aboutBackend->setEnabled( false );
708 connect(m_aboutBackend, SIGNAL(triggered()), this, SLOT(slotAboutBackend()));
709
710 KAction *reload = ac->add<KAction>( "file_reload" );
711 reload->setText( i18n( "Reloa&d" ) );
712 reload->setIcon( KIcon( "view-refresh" ) );
713 reload->setWhatsThis( i18n( "Reload the current document from disk." ) );
714 connect( reload, SIGNAL(triggered()), this, SLOT(slotReload()) );
715 reload->setShortcut( KStandardShortcut::reload() );
716 m_reload = reload;
717
718 m_closeFindBar = ac->addAction( "close_find_bar", this, SLOT(slotHideFindBar()) );
719 m_closeFindBar->setText( i18n("Close &Find Bar") );
720 m_closeFindBar->setShortcut( QKeySequence(Qt::Key_Escape) );
721 m_closeFindBar->setEnabled( false );
722
723 KAction *pageno = new KAction( i18n( "Page Number" ), ac );
724 pageno->setDefaultWidget( m_pageNumberTool );
725 ac->addAction( "page_number", pageno );
726}
727
728void Part::setViewerShortcuts()
729{
730 KActionCollection * ac = actionCollection();
731
732 m_gotoPage->setShortcut( QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_G) );
733 m_find->setShortcuts( QList<QKeySequence>() );
734
735 m_findNext->setShortcut( QKeySequence() );
736 m_findPrev->setShortcut( QKeySequence() );
737
738 m_addBookmark->setShortcut( QKeySequence( Qt::CTRL + Qt::ALT + Qt::Key_B ) );
739
740 m_beginningOfDocument->setShortcut( QKeySequence( Qt::CTRL + Qt::ALT + Qt::Key_Home ) );
741 m_endOfDocument->setShortcut( QKeySequence( Qt::CTRL + Qt::ALT + Qt::Key_End ) );
742
743 KAction *action = static_cast<KAction*>( ac->action( "file_reload" ) );
744 if( action ) action->setShortcuts( QList<QKeySequence>() << QKeySequence( Qt::ALT + Qt::Key_F5 ) );
745}
746
747void Part::setupActions()
748{
749 KActionCollection * ac = actionCollection();
750
751 m_copy = KStandardAction::create( KStandardAction::Copy, m_pageView, SLOT(copyTextSelection()), ac );
752
753 m_selectAll = KStandardAction::selectAll( m_pageView, SLOT(selectAll()), ac );
754
755 m_saveCopyAs = KStandardAction::saveAs( this, SLOT(slotSaveCopyAs()), ac );
756 m_saveCopyAs->setText( i18n( "Save &Copy As..." ) );
757 m_saveCopyAs->setShortcut( KShortcut() );
758 ac->addAction( "file_save_copy", m_saveCopyAs );
759 m_saveCopyAs->setEnabled( false );
760
761 m_saveAs = KStandardAction::saveAs( this, SLOT(slotSaveFileAs()), ac );
762 m_saveAs->setEnabled( false );
763
764 m_showLeftPanel = ac->add<KToggleAction>("show_leftpanel");
765 m_showLeftPanel->setText(i18n( "Show &Navigation Panel"));
766 m_showLeftPanel->setIcon(KIcon( "view-sidetree" ));
767 connect( m_showLeftPanel, SIGNAL(toggled(bool)), this, SLOT(slotShowLeftPanel()) );
768 m_showLeftPanel->setShortcut( Qt::Key_F7 );
769 m_showLeftPanel->setChecked( Okular::Settings::showLeftPanel() );
770 slotShowLeftPanel();
771
772 m_showBottomBar = ac->add<KToggleAction>("show_bottombar");
773 m_showBottomBar->setText(i18n( "Show &Page Bar"));
774 connect( m_showBottomBar, SIGNAL(toggled(bool)), this, SLOT(slotShowBottomBar()) );
775 m_showBottomBar->setChecked( Okular::Settings::showBottomBar() );
776 slotShowBottomBar();
777
778 m_showEmbeddedFiles = ac->addAction("embedded_files");
779 m_showEmbeddedFiles->setText(i18n("&Embedded Files"));
780 m_showEmbeddedFiles->setIcon( KIcon( "mail-attachment" ) );
781 connect(m_showEmbeddedFiles, SIGNAL(triggered()), this, SLOT(slotShowEmbeddedFiles()));
782 m_showEmbeddedFiles->setEnabled( false );
783
784 m_exportAs = ac->addAction("file_export_as");
785 m_exportAs->setText(i18n("E&xport As"));
786 m_exportAs->setIcon( KIcon( "document-export" ) );
787 m_exportAsMenu = new QMenu();
788 connect(m_exportAsMenu, SIGNAL(triggered(QAction*)), this, SLOT(slotExportAs(QAction*)));
789 m_exportAs->setMenu( m_exportAsMenu );
790 m_exportAsText = actionForExportFormat( Okular::ExportFormat::standardFormat( Okular::ExportFormat::PlainText ), m_exportAsMenu );
791 m_exportAsMenu->addAction( m_exportAsText );
792 m_exportAs->setEnabled( false );
793 m_exportAsText->setEnabled( false );
794 m_exportAsDocArchive = actionForExportFormat( Okular::ExportFormat(
795 i18nc( "A document format, Okular-specific", "Document Archive" ),
796 KMimeType::mimeType( "application/vnd.kde.okular-archive" ) ), m_exportAsMenu );
797 m_exportAsMenu->addAction( m_exportAsDocArchive );
798 m_exportAsDocArchive->setEnabled( false );
799
800 m_showPresentation = ac->addAction("presentation");
801 m_showPresentation->setText(i18n("P&resentation"));
802 m_showPresentation->setIcon( KIcon( "view-presentation" ) );
803 connect(m_showPresentation, SIGNAL(triggered()), this, SLOT(slotShowPresentation()));
804 m_showPresentation->setShortcut( QKeySequence( Qt::CTRL + Qt::SHIFT + Qt::Key_P ) );
805 m_showPresentation->setEnabled( false );
806
807 QAction * importPS = ac->addAction("import_ps");
808 importPS->setText(i18n("&Import PostScript as PDF..."));
809 importPS->setIcon(KIcon("document-import"));
810 connect(importPS, SIGNAL(triggered()), this, SLOT(slotImportPSFile()));
811#if 0
812 QAction * ghns = ac->addAction("get_new_stuff");
813 ghns->setText(i18n("&Get Books From Internet..."));
814 ghns->setIcon(KIcon("get-hot-new-stuff"));
815 connect(ghns, SIGNAL(triggered()), this, SLOT(slotGetNewStuff()));
816 // TEMP, REMOVE ME!
817 ghns->setShortcut( Qt::Key_G );
818#endif
819
820 KToggleAction *blackscreenAction = new KToggleAction( i18n( "Switch Blackscreen Mode" ), ac );
821 ac->addAction( "switch_blackscreen_mode", blackscreenAction );
822 blackscreenAction->setShortcut( QKeySequence( Qt::Key_B ) );
823 blackscreenAction->setIcon( KIcon( "view-presentation" ) );
824 blackscreenAction->setEnabled( false );
825
826 KToggleAction *drawingAction = new KToggleAction( i18n( "Toggle Drawing Mode" ), ac );
827 ac->addAction( "presentation_drawing_mode", drawingAction );
828 drawingAction->setIcon( KIcon( "draw-freehand" ) );
829 drawingAction->setEnabled( false );
830
831 KAction *eraseDrawingAction = new KAction( i18n( "Erase Drawings" ), ac );
832 ac->addAction( "presentation_erase_drawings", eraseDrawingAction );
833 eraseDrawingAction->setIcon( KIcon( "draw-eraser" ) );
834 eraseDrawingAction->setEnabled( false );
835
836 KAction *configureAnnotations = new KAction( i18n( "Configure Annotations..." ), ac );
837 ac->addAction( "options_configure_annotations", configureAnnotations );
838 configureAnnotations->setIcon( KIcon( "configure" ) );
839 connect(configureAnnotations, SIGNAL(triggered()), this, SLOT(slotAnnotationPreferences()));
840
841 KAction *playPauseAction = new KAction( i18n( "Play/Pause Presentation" ), ac );
842 ac->addAction( "presentation_play_pause", playPauseAction );
843 playPauseAction->setEnabled( false );
844}
845
846Part::~Part()
847{
848 GuiUtils::removeIconLoader( iconLoader() );
849 m_document->removeObserver( this );
850
851 if ( m_document->isOpened() )
852 Part::closeUrl( false );
853
854 delete m_toc;
855 delete m_pageView;
856 delete m_thumbnailList;
857 delete m_miniBar;
858 delete m_pageNumberTool;
859 delete m_miniBarLogic;
860 delete m_bottomBar;
861#ifdef OKULAR_ENABLE_MINIBAR
862 delete m_progressWidget;
863#endif
864 delete m_pageSizeLabel;
865 delete m_reviewsWidget;
866 delete m_bookmarkList;
867 delete m_infoTimer;
868
869 delete m_document;
870
871 delete m_tempfile;
872
873 qDeleteAll( m_bookmarkActions );
874
875 delete m_exportAsMenu;
876
877#ifdef OKULAR_KEEP_FILE_OPEN
878 delete m_keeper;
879#endif
880}
881
882
883bool Part::openDocument(const KUrl& url, uint page)
884{
885 Okular::DocumentViewport vp( page - 1 );
886 vp.rePos.enabled = true;
887 vp.rePos.normalizedX = 0;
888 vp.rePos.normalizedY = 0;
889 vp.rePos.pos = Okular::DocumentViewport::TopLeft;
890 if ( vp.isValid() )
891 m_document->setNextDocumentViewport( vp );
892 return openUrl( url );
893}
894
895
896void Part::startPresentation()
897{
898 m_cliPresentation = true;
899}
900
901
902QStringList Part::supportedMimeTypes() const
903{
904 return m_document->supportedMimeTypes();
905}
906
907
908KUrl Part::realUrl() const
909{
910 if ( !m_realUrl.isEmpty() )
911 return m_realUrl;
912
913 return url();
914}
915
916// ViewerInterface
917
918void Part::showSourceLocation(const QString& fileName, int line, int column, bool showGraphically)
919{
920 const QString u = QString( "src:%1 %2" ).arg( line + 1 ).arg( fileName );
921 GotoAction action( QString(), u );
922 m_document->processAction( &action );
923 if( showGraphically )
924 {
925 m_pageView->setLastSourceLocationViewport( m_document->viewport() );
926 }
927}
928
929void Part::clearLastShownSourceLocation()
930{
931 m_pageView->clearLastSourceLocationViewport();
932}
933
934bool Part::isWatchFileModeEnabled() const
935{
936 return !m_watcher->isStopped();
937}
938
939void Part::setWatchFileModeEnabled(bool enabled)
940{
941 if ( enabled && m_watcher->isStopped() )
942 {
943 m_watcher->startScan();
944 }
945 else if( !enabled && !m_watcher->isStopped() )
946 {
947 m_dirtyHandler->stop();
948 m_watcher->stopScan();
949 }
950}
951
952bool Part::areSourceLocationsShownGraphically() const
953{
954 return m_pageView->areSourceLocationsShownGraphically();
955}
956
957void Part::setShowSourceLocationsGraphically(bool show)
958{
959 m_pageView->setShowSourceLocationsGraphically(show);
960}
961
962bool Part::openNewFilesInTabs() const
963{
964 return Okular::Settings::self()->shellOpenFileInTabs();
965}
966
967void Part::slotHandleActivatedSourceReference(const QString& absFileName, int line, int col, bool *handled)
968{
969 emit openSourceReference( absFileName, line, col );
970 if ( m_embedMode == Okular::ViewerWidgetMode )
971 {
972 *handled = true;
973 }
974}
975
976void Part::openUrlFromDocument(const KUrl &url)
977{
978 if ( m_embedMode == PrintPreviewMode )
979 return;
980
981 if (KIO::NetAccess::exists(url, KIO::NetAccess::SourceSide, widget()))
982 {
983 m_bExtension->openUrlNotify();
984 m_bExtension->setLocationBarUrl(url.prettyUrl());
985 openUrl(url);
986 } else {
987 KMessageBox::error( widget(), i18n("Could not open '%1'. File does not exist", url.pathOrUrl() ) );
988 }
989}
990
991void Part::openUrlFromBookmarks(const KUrl &_url)
992{
993 KUrl url = _url;
994 Okular::DocumentViewport vp( _url.htmlRef() );
995 if ( vp.isValid() )
996 m_document->setNextDocumentViewport( vp );
997 url.setHTMLRef( QString() );
998 if ( m_document->currentDocument() == url )
999 {
1000 if ( vp.isValid() )
1001 m_document->setViewport( vp );
1002 }
1003 else
1004 openUrl( url );
1005}
1006
1007void Part::handleDroppedUrls( const KUrl::List& urls )
1008{
1009 if ( urls.isEmpty() )
1010 return;
1011
1012 if ( m_embedMode != NativeShellMode || !openNewFilesInTabs() )
1013 {
1014 openUrlFromDocument( urls.first() );
1015 return;
1016 }
1017
1018 emit urlsDropped( urls );
1019}
1020
1021void Part::slotJobStarted(KIO::Job *job)
1022{
1023 if (job)
1024 {
1025 QStringList supportedMimeTypes = m_document->supportedMimeTypes();
1026 job->addMetaData("accept", supportedMimeTypes.join(", ") + ", */*;q=0.5");
1027
1028 connect(job, SIGNAL(result(KJob*)), this, SLOT(slotJobFinished(KJob*)));
1029 }
1030}
1031
1032void Part::slotJobFinished(KJob *job)
1033{
1034 if ( job->error() == KIO::ERR_USER_CANCELED )
1035 {
1036 m_pageView->displayMessage( i18n( "The loading of %1 has been canceled.", realUrl().pathOrUrl() ) );
1037 }
1038}
1039
1040void Part::loadCancelled(const QString &reason)
1041{
1042 emit setWindowCaption( QString() );
1043 resetStartArguments();
1044
1045 // when m_viewportDirty.pageNumber != -1 we come from slotDoFileDirty
1046 // so we don't want to show an ugly messagebox just because the document is
1047 // taking more than usual to be recreated
1048 if (m_viewportDirty.pageNumber == -1)
1049 {
1050 if (!reason.isEmpty())
1051 {
1052 KMessageBox::error( widget(), i18n("Could not open %1. Reason: %2", url().prettyUrl(), reason ) );
1053 }
1054 }
1055}
1056
1057void Part::setWindowTitleFromDocument()
1058{
1059 // If 'DocumentTitle' should be used, check if the document has one. If
1060 // either case is false, use the file name.
1061 QString title = Okular::Settings::displayDocumentNameOrPath() == Okular::Settings::EnumDisplayDocumentNameOrPath::Path ? realUrl().pathOrUrl() : realUrl().fileName();
1062
1063 if ( Okular::Settings::displayDocumentTitle() )
1064 {
1065 const QString docTitle = m_document->metaData( "DocumentTitle" ).toString();
1066 if ( !docTitle.isEmpty() && !docTitle.trimmed().isEmpty() )
1067 {
1068 title = docTitle;
1069 }
1070 }
1071
1072 emit setWindowCaption( title );
1073}
1074
1075KConfigDialog * Part::slotGeneratorPreferences( )
1076{
1077 // Create dialog
1078 KConfigDialog * dialog = new KConfigDialog( m_pageView, "generator_prefs", Okular::Settings::self() );
1079 dialog->setAttribute( Qt::WA_DeleteOnClose );
1080
1081 if( m_embedMode == ViewerWidgetMode )
1082 {
1083 dialog->setCaption( i18n( "Configure Viewer Backends" ) );
1084 }
1085 else
1086 {
1087 dialog->setCaption( i18n( "Configure Backends" ) );
1088 }
1089
1090 m_document->fillConfigDialog( dialog );
1091
1092 // Show it
1093 dialog->setWindowModality( Qt::ApplicationModal );
1094 dialog->show();
1095
1096 return dialog;
1097}
1098
1099
1100void Part::notifySetup( const QVector< Okular::Page * > & /*pages*/, int setupFlags )
1101{
1102 if ( !( setupFlags & Okular::DocumentObserver::DocumentChanged ) )
1103 return;
1104
1105 rebuildBookmarkMenu();
1106 updateAboutBackendAction();
1107 m_findBar->resetSearch();
1108 m_searchWidget->setEnabled( m_document->supportsSearching() );
1109}
1110
1111void Part::notifyViewportChanged( bool /*smoothMove*/ )
1112{
1113 updateViewActions();
1114}
1115
1116void Part::notifyPageChanged( int page, int flags )
1117{
1118 if ( flags & Okular::DocumentObserver::NeedSaveAs )
1119 setModified();
1120
1121 if ( !(flags & Okular::DocumentObserver::Bookmark ) )
1122 return;
1123
1124 rebuildBookmarkMenu();
1125 if ( page == m_document->viewport().pageNumber )
1126 updateBookmarksActions();
1127}
1128
1129
1130void Part::goToPage(uint i)
1131{
1132 if ( i <= m_document->pages() )
1133 m_document->setViewportPage( i - 1 );
1134}
1135
1136
1137void Part::openDocument( const QString &doc )
1138{
1139 openUrl( KUrl( doc ) );
1140}
1141
1142
1143uint Part::pages()
1144{
1145 return m_document->pages();
1146}
1147
1148
1149uint Part::currentPage()
1150{
1151 return m_document->pages() ? m_document->currentPage() + 1 : 0;
1152}
1153
1154
1155QString Part::currentDocument()
1156{
1157 return m_document->currentDocument().pathOrUrl();
1158}
1159
1160
1161QString Part::documentMetaData( const QString &metaData ) const
1162{
1163 const Okular::DocumentInfo * info = m_document->documentInfo();
1164 if ( info )
1165 {
1166 QDomElement docElement = info->documentElement();
1167 for ( QDomNode node = docElement.firstChild(); !node.isNull(); node = node.nextSibling() )
1168 {
1169 const QDomElement element = node.toElement();
1170 if ( metaData.compare( element.tagName(), Qt::CaseInsensitive ) == 0 )
1171 return element.attribute( "value" );
1172 }
1173 }
1174
1175 return QString();
1176}
1177
1178
1179bool Part::slotImportPSFile()
1180{
1181 QString app = KStandardDirs::findExe( "ps2pdf" );
1182 if ( app.isEmpty() )
1183 {
1184 // TODO point the user to their distro packages?
1185 KMessageBox::error( widget(), i18n( "The program \"ps2pdf\" was not found, so Okular can not import PS files using it." ), i18n("ps2pdf not found") );
1186 return false;
1187 }
1188
1189 KUrl url = KFileDialog::getOpenUrl( KUrl(), "application/postscript", this->widget() );
1190 if ( url.isLocalFile() )
1191 {
1192 KTemporaryFile tf;
1193 tf.setSuffix( ".pdf" );
1194 tf.setAutoRemove( false );
1195 if ( !tf.open() )
1196 return false;
1197 m_temporaryLocalFile = tf.fileName();
1198 tf.close();
1199
1200 setLocalFilePath( url.toLocalFile() );
1201 QStringList args;
1202 QProcess *p = new QProcess();
1203 args << url.toLocalFile() << m_temporaryLocalFile;
1204 m_pageView->displayMessage(i18n("Importing PS file as PDF (this may take a while)..."));
1205 connect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(psTransformEnded(int,QProcess::ExitStatus)));
1206 p->start(app, args);
1207 return true;
1208 }
1209
1210 m_temporaryLocalFile.clear();
1211 return false;
1212}
1213
1214static void addFileToWatcher( KDirWatch *watcher, const QString &filePath)
1215{
1216 if ( !watcher->contains( filePath ) ) watcher->addFile(filePath);
1217 const QFileInfo fi(filePath);
1218 if ( !watcher->contains( fi.absolutePath() ) ) watcher->addDir(fi.absolutePath());
1219 if ( fi.isSymLink() ) watcher->addFile( fi.readLink() );
1220}
1221
1222bool Part::openFile()
1223{
1224 KMimeType::Ptr mime;
1225 QString fileNameToOpen = localFilePath();
1226 const bool isstdin = url().isLocalFile() && url().fileName( KUrl::ObeyTrailingSlash ) == QLatin1String( "-" );
1227 const QFileInfo fileInfo( fileNameToOpen );
1228 if ( !isstdin && !fileInfo.exists() )
1229 return false;
1230 if ( !arguments().mimeType().isEmpty() )
1231 {
1232 mime = KMimeType::mimeType( arguments().mimeType() );
1233 }
1234 if ( !mime )
1235 {
1236 mime = KMimeType::findByPath( fileNameToOpen );
1237 }
1238 bool isCompressedFile = false;
1239 bool uncompressOk = true;
1240 QString compressedMime = compressedMimeFor( mime->name() );
1241 if ( compressedMime.isEmpty() )
1242 compressedMime = compressedMimeFor( mime->parentMimeType() );
1243 if ( !compressedMime.isEmpty() )
1244 {
1245 isCompressedFile = true;
1246 uncompressOk = handleCompressed( fileNameToOpen, localFilePath(), compressedMime );
1247 mime = KMimeType::findByPath( fileNameToOpen );
1248 }
1249 Document::OpenResult openResult = Document::OpenError;
1250 isDocumentArchive = false;
1251 if ( uncompressOk )
1252 {
1253 if ( mime->is( "application/vnd.kde.okular-archive" ) )
1254 {
1255 openResult = m_document->openDocumentArchive( fileNameToOpen, url() );
1256 isDocumentArchive = true;
1257 }
1258 else
1259 {
1260 openResult = m_document->openDocument( fileNameToOpen, url(), mime );
1261 }
1262
1263 // if the file didn't open correctly it might be encrypted, so ask for a pass
1264 QString walletName, walletFolder, walletKey;
1265 m_document->walletDataForFile(fileNameToOpen, &walletName, &walletFolder, &walletKey);
1266 bool firstInput = true;
1267 bool triedWallet = false;
1268 KWallet::Wallet * wallet = 0;
1269 bool keep = true;
1270 while ( openResult == Document::OpenNeedsPassword )
1271 {
1272 QString password;
1273
1274 // 1.A. try to retrieve the first password from the kde wallet system
1275 if ( !triedWallet && !walletKey.isNull() )
1276 {
1277 const WId parentwid = widget()->effectiveWinId();
1278 wallet = KWallet::Wallet::openWallet( walletName, parentwid );
1279 if ( wallet )
1280 {
1281 // use the KPdf folder (and create if missing)
1282 if ( !wallet->hasFolder( walletFolder ) )
1283 wallet->createFolder( walletFolder );
1284 wallet->setFolder( walletFolder );
1285
1286 // look for the pass in that folder
1287 QString retrievedPass;
1288 if ( !wallet->readPassword( walletKey, retrievedPass ) )
1289 password = retrievedPass;
1290 }
1291 triedWallet = true;
1292 }
1293
1294 // 1.B. if not retrieved, ask the password using the kde password dialog
1295 if ( password.isNull() )
1296 {
1297 QString prompt;
1298 if ( firstInput )
1299 prompt = i18n( "Please enter the password to read the document:" );
1300 else
1301 prompt = i18n( "Incorrect password. Try again:" );
1302 firstInput = false;
1303
1304 // if the user presses cancel, abort opening
1305 KPasswordDialog dlg( widget(), wallet ? KPasswordDialog::ShowKeepPassword : KPasswordDialog::KPasswordDialogFlags() );
1306 dlg.setCaption( i18n( "Document Password" ) );
1307 dlg.setPrompt( prompt );
1308 if( !dlg.exec() )
1309 break;
1310 password = dlg.password();
1311 if ( wallet )
1312 keep = dlg.keepPassword();
1313 }
1314
1315 // 2. reopen the document using the password
1316 if ( mime->is( "application/vnd.kde.okular-archive" ) )
1317 {
1318 openResult = m_document->openDocumentArchive( fileNameToOpen, url(), password );
1319 isDocumentArchive = true;
1320 }
1321 else
1322 {
1323 openResult = m_document->openDocument( fileNameToOpen, url(), mime, password );
1324 }
1325
1326 // 3. if the password is correct and the user chose to remember it, store it to the wallet
1327 if ( openResult == Document::OpenSuccess && wallet && /*safety check*/ wallet->isOpen() && keep )
1328 {
1329 wallet->writePassword( walletKey, password );
1330 }
1331 }
1332 }
1333
1334 bool canSearch = m_document->supportsSearching();
1335 emit mimeTypeChanged( mime );
1336
1337 // update one-time actions
1338 const bool ok = openResult == Document::OpenSuccess;
1339 emit enableCloseAction( ok );
1340 m_find->setEnabled( ok && canSearch );
1341 m_findNext->setEnabled( ok && canSearch );
1342 m_findPrev->setEnabled( ok && canSearch );
1343 if( m_saveAs ) m_saveAs->setEnabled( ok && (m_document->canSaveChanges() || isDocumentArchive) );
1344 if( m_saveCopyAs ) m_saveCopyAs->setEnabled( ok );
1345 emit enablePrintAction( ok && m_document->printingSupport() != Okular::Document::NoPrinting );
1346 m_printPreview->setEnabled( ok && m_document->printingSupport() != Okular::Document::NoPrinting );
1347 m_showProperties->setEnabled( ok );
1348 bool hasEmbeddedFiles = ok && m_document->embeddedFiles() && m_document->embeddedFiles()->count() > 0;
1349 if ( m_showEmbeddedFiles ) m_showEmbeddedFiles->setEnabled( hasEmbeddedFiles );
1350 m_topMessage->setVisible( hasEmbeddedFiles && Okular::Settings::showOSD() );
1351
1352 // Warn the user that XFA forms are not supported yet (NOTE: poppler generator only)
1353 if ( ok && m_document->metaData( "HasUnsupportedXfaForm" ).toBool() == true )
1354 {
1355 m_formsMessage->setText( i18n( "This document has XFA forms, which are currently <b>unsupported</b>." ) );
1356 m_formsMessage->setIcon( KIcon( "dialog-warning" ) );
1357 m_formsMessage->setMessageType( KMessageWidget::Warning );
1358 m_formsMessage->setVisible( true );
1359 }
1360 // m_pageView->toggleFormsAction() may be null on dummy mode
1361 else if ( ok && m_pageView->toggleFormsAction() && m_pageView->toggleFormsAction()->isEnabled() )
1362 {
1363 m_formsMessage->setText( i18n( "This document has forms. Click on the button to interact with them, or use View -> Show Forms." ) );
1364 m_formsMessage->setMessageType( KMessageWidget::Information );
1365 m_formsMessage->setVisible( true );
1366 }
1367 else
1368 {
1369 m_formsMessage->setVisible( false );
1370 }
1371
1372 if ( m_showPresentation ) m_showPresentation->setEnabled( ok );
1373 if ( ok )
1374 {
1375 if ( m_exportAs )
1376 {
1377 m_exportFormats = m_document->exportFormats();
1378 QList<Okular::ExportFormat>::ConstIterator it = m_exportFormats.constBegin();
1379 QList<Okular::ExportFormat>::ConstIterator itEnd = m_exportFormats.constEnd();
1380 QMenu *menu = m_exportAs->menu();
1381 for ( ; it != itEnd; ++it )
1382 {
1383 menu->addAction( actionForExportFormat( *it ) );
1384 }
1385 }
1386 if ( isCompressedFile )
1387 {
1388 m_realUrl = url();
1389 }
1390#ifdef OKULAR_KEEP_FILE_OPEN
1391 if ( keepFileOpen() )
1392 m_keeper->open( fileNameToOpen );
1393#endif
1394 }
1395 if ( m_exportAsText ) m_exportAsText->setEnabled( ok && m_document->canExportToText() );
1396 if ( m_exportAsDocArchive ) m_exportAsDocArchive->setEnabled( ok );
1397 if ( m_exportAs ) m_exportAs->setEnabled( ok );
1398
1399 // update viewing actions
1400 updateViewActions();
1401
1402 m_fileWasRemoved = false;
1403
1404 if ( !ok )
1405 {
1406 // if can't open document, update windows so they display blank contents
1407 m_pageView->viewport()->update();
1408 m_thumbnailList->update();
1409 setUrl( KUrl() );
1410 return false;
1411 }
1412
1413 // set the file to the fileWatcher
1414 if ( url().isLocalFile() )
1415 {
1416 addFileToWatcher( m_watcher, localFilePath() );
1417 }
1418
1419 // if the 'OpenTOC' flag is set, open the TOC
1420 if ( m_document->metaData( "OpenTOC" ).toBool() && m_sidebar->isItemEnabled( 0 ) && !m_sidebar->isCollapsed() )
1421 {
1422 m_sidebar->setCurrentIndex( 0, Sidebar::DoNotUncollapseIfCollapsed );
1423 }
1424 // if the 'StartFullScreen' flag is set, or the command line flag was
1425 // specified, start presentation
1426 if ( m_document->metaData( "StartFullScreen" ).toBool() || m_cliPresentation )
1427 {
1428 bool goAheadWithPresentationMode = true;
1429 if ( !m_cliPresentation )
1430 {
1431 const QString text = i18n( "The document requested to be launched in presentation mode.\n"
1432 "Do you want to allow it?" );
1433 const QString caption = i18n( "Presentation Mode" );
1434 const KGuiItem yesItem = KGuiItem( i18n( "Allow" ), "dialog-ok", i18n( "Allow the presentation mode" ) );
1435 const KGuiItem noItem = KGuiItem( i18n( "Do Not Allow" ), "process-stop", i18n( "Do not allow the presentation mode" ) );
1436 const int result = KMessageBox::questionYesNo( widget(), text, caption, yesItem, noItem );
1437 if ( result == KMessageBox::No )
1438 goAheadWithPresentationMode = false;
1439 }
1440 m_cliPresentation = false;
1441 if ( goAheadWithPresentationMode )
1442 QMetaObject::invokeMethod( this, "slotShowPresentation", Qt::QueuedConnection );
1443 }
1444 m_generatorGuiClient = factory() ? m_document->guiClient() : 0;
1445 if ( m_generatorGuiClient )
1446 factory()->addClient( m_generatorGuiClient );
1447 if ( m_cliPrint )
1448 {
1449 m_cliPrint = false;
1450 slotPrint();
1451 }
1452 return true;
1453}
1454
1455bool Part::openUrl(const KUrl &_url)
1456{
1457 // Close current document if any
1458 if ( !closeUrl() )
1459 return false;
1460
1461 KUrl url( _url );
1462 if ( url.hasHTMLRef() )
1463 {
1464 const QString dest = url.htmlRef();
1465 bool ok = true;
1466 const int page = dest.toInt( &ok );
1467 if ( ok )
1468 {
1469 Okular::DocumentViewport vp( page - 1 );
1470 vp.rePos.enabled = true;
1471 vp.rePos.normalizedX = 0;
1472 vp.rePos.normalizedY = 0;
1473 vp.rePos.pos = Okular::DocumentViewport::TopLeft;
1474 m_document->setNextDocumentViewport( vp );
1475 }
1476 else
1477 {
1478 m_document->setNextDocumentDestination( dest );
1479 }
1480 url.setHTMLRef( QString() );
1481 }
1482
1483 // this calls in sequence the 'closeUrl' and 'openFile' methods
1484 bool openOk = KParts::ReadWritePart::openUrl( url );
1485
1486 if ( openOk )
1487 {
1488 m_viewportDirty.pageNumber = -1;
1489
1490 setWindowTitleFromDocument();
1491 }
1492 else
1493 {
1494 resetStartArguments();
1495 KMessageBox::error( widget(), i18n("Could not open %1", url.pathOrUrl() ) );
1496 }
1497
1498 return openOk;
1499}
1500
1501bool Part::queryClose()
1502{
1503 if ( !isReadWrite() || !isModified() )
1504 return true;
1505
1506 const int res = KMessageBox::warningYesNoCancel( widget(),
1507 i18n( "Do you want to save your annotation changes or discard them?" ),
1508 i18n( "Close Document" ),
1509 KStandardGuiItem::saveAs(),
1510 KStandardGuiItem::discard() );
1511
1512 switch ( res )
1513 {
1514 case KMessageBox::Yes: // Save as
1515 slotSaveFileAs();
1516 return !isModified(); // Only allow closing if file was really saved
1517 case KMessageBox::No: // Discard
1518 return true;
1519 default: // Cancel
1520 return false;
1521 }
1522}
1523
1524bool Part::closeUrl(bool promptToSave)
1525{
1526 if ( promptToSave && !queryClose() )
1527 return false;
1528
1529 setModified( false );
1530
1531 if (!m_temporaryLocalFile.isNull() && m_temporaryLocalFile != localFilePath())
1532 {
1533 QFile::remove( m_temporaryLocalFile );
1534 m_temporaryLocalFile.clear();
1535 }
1536
1537 slotHidePresentation();
1538 emit enableCloseAction( false );
1539 m_find->setEnabled( false );
1540 m_findNext->setEnabled( false );
1541 m_findPrev->setEnabled( false );
1542 if( m_saveAs ) m_saveAs->setEnabled( false );
1543 if( m_saveCopyAs ) m_saveCopyAs->setEnabled( false );
1544 m_printPreview->setEnabled( false );
1545 m_showProperties->setEnabled( false );
1546 if ( m_showEmbeddedFiles ) m_showEmbeddedFiles->setEnabled( false );
1547 if ( m_exportAs ) m_exportAs->setEnabled( false );
1548 if ( m_exportAsText ) m_exportAsText->setEnabled( false );
1549 if ( m_exportAsDocArchive ) m_exportAsDocArchive->setEnabled( false );
1550 m_exportFormats.clear();
1551 if ( m_exportAs )
1552 {
1553 QMenu *menu = m_exportAs->menu();
1554 QList<QAction*> acts = menu->actions();
1555 int num = acts.count();
1556 for ( int i = 2; i < num; ++i )
1557 {
1558 menu->removeAction( acts.at(i) );
1559 delete acts.at(i);
1560 }
1561 }
1562 if ( m_showPresentation ) m_showPresentation->setEnabled( false );
1563 emit setWindowCaption("");
1564 emit enablePrintAction(false);
1565 m_realUrl = KUrl();
1566 if ( url().isLocalFile() )
1567 {
1568 m_watcher->removeFile( localFilePath() );
1569 QFileInfo fi(localFilePath());
1570 m_watcher->removeDir( fi.absolutePath() );
1571 if ( fi.isSymLink() ) m_watcher->removeFile( fi.readLink() );
1572 }
1573 m_fileWasRemoved = false;
1574 if ( m_generatorGuiClient )
1575 factory()->removeClient( m_generatorGuiClient );
1576 m_generatorGuiClient = 0;
1577 m_document->closeDocument();
1578 updateViewActions();
1579 delete m_tempfile;
1580 m_tempfile = 0;
1581 if ( widget() )
1582 {
1583 m_searchWidget->clearText();
1584 m_topMessage->setVisible( false );
1585 m_formsMessage->setVisible( false );
1586 }
1587#ifdef OKULAR_KEEP_FILE_OPEN
1588 m_keeper->close();
1589#endif
1590 bool r = KParts::ReadWritePart::closeUrl();
1591 setUrl(KUrl());
1592
1593 return r;
1594}
1595
1596bool Part::closeUrl()
1597{
1598 return closeUrl( true );
1599}
1600
1601void Part::guiActivateEvent(KParts::GUIActivateEvent *event)
1602{
1603 updateViewActions();
1604
1605 KParts::ReadWritePart::guiActivateEvent(event);
1606}
1607
1608void Part::close()
1609{
1610 if ( m_embedMode == NativeShellMode )
1611 {
1612 closeUrl();
1613 }
1614 else KMessageBox::information( widget(), i18n( "This link points to a close document action that does not work when using the embedded viewer." ), QString(), "warnNoCloseIfNotInOkular" );
1615}
1616
1617
1618void Part::cannotQuit()
1619{
1620 KMessageBox::information( widget(), i18n( "This link points to a quit application action that does not work when using the embedded viewer." ), QString(), "warnNoQuitIfNotInOkular" );
1621}
1622
1623
1624void Part::slotShowLeftPanel()
1625{
1626 bool showLeft = m_showLeftPanel->isChecked();
1627 Okular::Settings::setShowLeftPanel( showLeft );
1628 Okular::Settings::self()->writeConfig();
1629 // show/hide left panel
1630 m_sidebar->setSidebarVisibility( showLeft );
1631}
1632
1633void Part::slotShowBottomBar()
1634{
1635 const bool showBottom = m_showBottomBar->isChecked();
1636 Okular::Settings::setShowBottomBar( showBottom );
1637 Okular::Settings::self()->writeConfig();
1638 // show/hide bottom bar
1639 m_bottomBar->setVisible( showBottom );
1640}
1641
1642void Part::slotFileDirty( const QString& path )
1643{
1644 // The beauty of this is that each start cancels the previous one.
1645 // This means that timeout() is only fired when there have
1646 // no changes to the file for the last 750 milisecs.
1647 // This ensures that we don't update on every other byte that gets
1648 // written to the file.
1649 if ( path == localFilePath() )
1650 {
1651 // Only start watching the file in case if it wasn't removed
1652 if (QFile::exists(localFilePath()))
1653 m_dirtyHandler->start( 750 );
1654 else
1655 m_fileWasRemoved = true;
1656 }
1657 else
1658 {
1659 const QFileInfo fi(localFilePath());
1660 if ( fi.absolutePath() == path )
1661 {
1662 // Our parent has been dirtified
1663 if (!QFile::exists(localFilePath()))
1664 {
1665 m_fileWasRemoved = true;
1666 }
1667 else if (m_fileWasRemoved && QFile::exists(localFilePath()))
1668 {
1669 // we need to watch the new file
1670 m_watcher->removeFile(localFilePath());
1671 m_watcher->addFile(localFilePath());
1672 m_dirtyHandler->start( 750 );
1673 }
1674 }
1675 else if ( fi.isSymLink() && fi.readLink() == path )
1676 {
1677 if ( QFile::exists( fi.readLink() ))
1678 m_dirtyHandler->start( 750 );
1679 else
1680 m_fileWasRemoved = true;
1681 }
1682 }
1683}
1684
1685
1686void Part::slotDoFileDirty()
1687{
1688 bool tocReloadPrepared = false;
1689
1690 // do the following the first time the file is reloaded
1691 if ( m_viewportDirty.pageNumber == -1 )
1692 {
1693 // store the url of the current document
1694 m_oldUrl = url();
1695
1696 // store the current viewport
1697 m_viewportDirty = m_document->viewport();
1698
1699 // store the current toolbox pane
1700 m_dirtyToolboxIndex = m_sidebar->currentIndex();
1701 m_wasSidebarVisible = m_sidebar->isSidebarVisible();
1702 m_wasSidebarCollapsed = m_sidebar->isCollapsed();
1703
1704 // store if presentation view was open
1705 m_wasPresentationOpen = ((PresentationWidget*)m_presentationWidget != 0);
1706
1707 // preserves the toc state after reload
1708 m_toc->prepareForReload();
1709 tocReloadPrepared = true;
1710
1711 // store the page rotation
1712 m_dirtyPageRotation = m_document->rotation();
1713
1714 // inform the user about the operation in progress
1715 // TODO: Remove this line and integrate reload info in queryClose
1716 m_pageView->displayMessage( i18n("Reloading the document...") );
1717 }
1718
1719 // close and (try to) reopen the document
1720 if ( !closeUrl() )
1721 {
1722 m_viewportDirty.pageNumber = -1;
1723
1724 if ( tocReloadPrepared )
1725 {
1726 m_toc->rollbackReload();
1727 }
1728 return;
1729 }
1730
1731 if ( tocReloadPrepared )
1732 m_toc->finishReload();
1733
1734 // inform the user about the operation in progress
1735 m_pageView->displayMessage( i18n("Reloading the document...") );
1736
1737 if ( KParts::ReadWritePart::openUrl( m_oldUrl ) )
1738 {
1739 // on successful opening, restore the previous viewport
1740 if ( m_viewportDirty.pageNumber >= (int) m_document->pages() )
1741 m_viewportDirty.pageNumber = (int) m_document->pages() - 1;
1742 m_document->setViewport( m_viewportDirty );
1743 m_oldUrl = KUrl();
1744 m_viewportDirty.pageNumber = -1;
1745 m_document->setRotation( m_dirtyPageRotation );
1746 if ( m_sidebar->currentIndex() != m_dirtyToolboxIndex && m_sidebar->isItemEnabled( m_dirtyToolboxIndex )
1747 && !m_sidebar->isCollapsed() )
1748 {
1749 m_sidebar->setCurrentIndex( m_dirtyToolboxIndex );
1750 }
1751 if ( m_sidebar->isSidebarVisible() != m_wasSidebarVisible )
1752 {
1753 m_sidebar->setSidebarVisibility( m_wasSidebarVisible );
1754 }
1755 if ( m_sidebar->isCollapsed() != m_wasSidebarCollapsed )
1756 {
1757 m_sidebar->setCollapsed( m_wasSidebarCollapsed );
1758 }
1759 if (m_wasPresentationOpen) slotShowPresentation();
1760 emit enablePrintAction(true && m_document->printingSupport() != Okular::Document::NoPrinting);
1761 }
1762 else
1763 {
1764 // start watching the file again (since we dropped it on close)
1765 addFileToWatcher( m_watcher, localFilePath() );
1766 m_dirtyHandler->start( 750 );
1767 }
1768}
1769
1770
1771void Part::updateViewActions()
1772{
1773 bool opened = m_document->pages() > 0;
1774 if ( opened )
1775 {
1776 m_gotoPage->setEnabled( m_document->pages() > 1 );
1777
1778 // Check if you are at the beginning or not
1779 if (m_document->currentPage() != 0)
1780 {
1781 m_beginningOfDocument->setEnabled( true );
1782 m_prevPage->setEnabled( true );
1783 }
1784 else
1785 {
1786 if (m_pageView->verticalScrollBar()->value() != 0)
1787 {
1788 // The page isn't at the very beginning
1789 m_beginningOfDocument->setEnabled( true );
1790 }
1791 else
1792 {
1793 // The page is at the very beginning of the document
1794 m_beginningOfDocument->setEnabled( false );
1795 }
1796 // The document is at the first page, you can go to a page before
1797 m_prevPage->setEnabled( false );
1798 }
1799
1800 if (m_document->pages() == m_document->currentPage() + 1 )
1801 {
1802 // If you are at the end, disable go to next page
1803 m_nextPage->setEnabled( false );
1804 if (m_pageView->verticalScrollBar()->value() == m_pageView->verticalScrollBar()->maximum())
1805 {
1806 // If you are the end of the page of the last document, you can't go to the last page
1807 m_endOfDocument->setEnabled( false );
1808 }
1809 else
1810 {
1811 // Otherwise you can move to the endif
1812 m_endOfDocument->setEnabled( true );
1813 }
1814 }
1815 else
1816 {
1817 // If you are not at the end, enable go to next page
1818 m_nextPage->setEnabled( true );
1819 m_endOfDocument->setEnabled( true );
1820 }
1821
1822 if (m_historyBack) m_historyBack->setEnabled( !m_document->historyAtBegin() );
1823 if (m_historyNext) m_historyNext->setEnabled( !m_document->historyAtEnd() );
1824 m_reload->setEnabled( true );
1825 if (m_copy) m_copy->setEnabled( true );
1826 if (m_selectAll) m_selectAll->setEnabled( true );
1827 }
1828 else
1829 {
1830 m_gotoPage->setEnabled( false );
1831 m_beginningOfDocument->setEnabled( false );
1832 m_endOfDocument->setEnabled( false );
1833 m_prevPage->setEnabled( false );
1834 m_nextPage->setEnabled( false );
1835 if (m_historyBack) m_historyBack->setEnabled( false );
1836 if (m_historyNext) m_historyNext->setEnabled( false );
1837 m_reload->setEnabled( false );
1838 if (m_copy) m_copy->setEnabled( false );
1839 if (m_selectAll) m_selectAll->setEnabled( false );
1840 }
1841
1842 if ( factory() )
1843 {
1844 QWidget *menu = factory()->container("menu_okular_part_viewer", this);
1845 if (menu) menu->setEnabled( opened );
1846
1847 menu = factory()->container("view_orientation", this);
1848 if (menu) menu->setEnabled( opened );
1849 }
1850 emit viewerMenuStateChange( opened );
1851
1852 updateBookmarksActions();
1853}
1854
1855
1856void Part::updateBookmarksActions()
1857{
1858 bool opened = m_document->pages() > 0;
1859 if ( opened )
1860 {
1861 m_addBookmark->setEnabled( true );
1862 if ( m_document->bookmarkManager()->isBookmarked( m_document->viewport() ) )
1863 {
1864 m_addBookmark->setText( i18n( "Remove Bookmark" ) );
1865 m_addBookmark->setIcon( KIcon( "edit-delete-bookmark" ) );
1866 m_renameBookmark->setEnabled( true );
1867 }
1868 else
1869 {
1870 m_addBookmark->setText( m_addBookmarkText );
1871 m_addBookmark->setIcon( m_addBookmarkIcon );
1872 m_renameBookmark->setEnabled( false );
1873 }
1874 }
1875 else
1876 {
1877 m_addBookmark->setEnabled( false );
1878 m_addBookmark->setText( m_addBookmarkText );
1879 m_addBookmark->setIcon( m_addBookmarkIcon );
1880 m_renameBookmark->setEnabled( false );
1881 }
1882}
1883
1884
1885void Part::enableTOC(bool enable)
1886{
1887 m_sidebar->setItemEnabled(0, enable);
1888
1889 // If present, show the TOC when a document is opened
1890 if ( enable )
1891 {
1892 m_sidebar->setCurrentIndex( 0, Sidebar::DoNotUncollapseIfCollapsed );
1893 }
1894}
1895
1896void Part::slotRebuildBookmarkMenu()
1897{
1898 rebuildBookmarkMenu();
1899}
1900
1901void Part::slotShowFindBar()
1902{
1903 m_findBar->show();
1904 m_findBar->focusAndSetCursor();
1905 m_closeFindBar->setEnabled( true );
1906}
1907
1908void Part::slotHideFindBar()
1909{
1910 if ( m_findBar->maybeHide() )
1911 {
1912 m_pageView->setFocus();
1913 m_closeFindBar->setEnabled( false );
1914 }
1915}
1916
1917//BEGIN go to page dialog
1918class GotoPageDialog : public KDialog
1919{
1920 public:
1921 GotoPageDialog(QWidget *p, int current, int max) : KDialog(p)
1922 {
1923 setCaption(i18n("Go to Page"));
1924 setButtons(Ok | Cancel);
1925 setDefaultButton(Ok);
1926
1927 QWidget *w = new QWidget(this);
1928 setMainWidget(w);
1929
1930 QVBoxLayout *topLayout = new QVBoxLayout(w);
1931 topLayout->setMargin(0);
1932 topLayout->setSpacing(spacingHint());
1933 e1 = new KIntNumInput(current, w);
1934 e1->setRange(1, max);
1935 e1->setEditFocus(true);
1936 e1->setSliderEnabled(true);
1937
1938 QLabel *label = new QLabel(i18n("&Page:"), w);
1939 label->setBuddy(e1);
1940 topLayout->addWidget(label);
1941 topLayout->addWidget(e1);
1942 // A little bit extra space
1943 topLayout->addSpacing(spacingHint());
1944 topLayout->addStretch(10);
1945 e1->setFocus();
1946 }
1947
1948 int getPage() const
1949 {
1950 return e1->value();
1951 }
1952
1953 protected:
1954 KIntNumInput *e1;
1955};
1956//END go to page dialog
1957
1958void Part::slotGoToPage()
1959{
1960 GotoPageDialog pageDialog( m_pageView, m_document->currentPage() + 1, m_document->pages() );
1961 if ( pageDialog.exec() == QDialog::Accepted )
1962 m_document->setViewportPage( pageDialog.getPage() - 1 );
1963}
1964
1965
1966void Part::slotPreviousPage()
1967{
1968 if ( m_document->isOpened() && !(m_document->currentPage() < 1) )
1969 m_document->setViewportPage( m_document->currentPage() - 1 );
1970}
1971
1972
1973void Part::slotNextPage()
1974{
1975 if ( m_document->isOpened() && m_document->currentPage() < (m_document->pages() - 1) )
1976 m_document->setViewportPage( m_document->currentPage() + 1 );
1977}
1978
1979
1980void Part::slotGotoFirst()
1981{
1982 if ( m_document->isOpened() ) {
1983 m_document->setViewportPage( 0 );
1984 m_beginningOfDocument->setEnabled( false );
1985 }
1986}
1987
1988
1989void Part::slotGotoLast()
1990{
1991 if ( m_document->isOpened() )
1992 {
1993 DocumentViewport endPage(m_document->pages() -1 );
1994 endPage.rePos.enabled = true;
1995 endPage.rePos.normalizedX = 0;
1996 endPage.rePos.normalizedY = 1;
1997 endPage.rePos.pos = Okular::DocumentViewport::TopLeft;
1998 m_document->setViewport(endPage);
1999 m_endOfDocument->setEnabled(false);
2000 }
2001}
2002
2003
2004void Part::slotHistoryBack()
2005{
2006 m_document->setPrevViewport();
2007}
2008
2009
2010void Part::slotHistoryNext()
2011{
2012 m_document->setNextViewport();
2013}
2014
2015
2016void Part::slotAddBookmark()
2017{
2018 DocumentViewport vp = m_document->viewport();
2019 if ( m_document->bookmarkManager()->isBookmarked( vp ) )
2020 {
2021 m_document->bookmarkManager()->removeBookmark( vp );
2022 }
2023 else
2024 {
2025 m_document->bookmarkManager()->addBookmark( vp );
2026 }
2027}
2028
2029void Part::slotRenameBookmark( const DocumentViewport &viewport )
2030{
2031 Q_ASSERT(m_document->bookmarkManager()->isBookmarked( viewport ));
2032 if ( m_document->bookmarkManager()->isBookmarked( viewport ) )
2033 {
2034 KBookmark bookmark = m_document->bookmarkManager()->bookmark( viewport );
2035 const QString newName = KInputDialog::getText( i18n( "Rename Bookmark" ), i18n( "Enter the new name of the bookmark:" ), bookmark.fullText(), 0, widget());
2036 if (!newName.isEmpty())
2037 {
2038 m_document->bookmarkManager()->renameBookmark(&bookmark, newName);
2039 }
2040 }
2041}
2042
2043void Part::slotRenameBookmarkFromMenu()
2044{
2045 QAction *action = dynamic_cast<QAction *>(sender());
2046 Q_ASSERT( action );
2047 if ( action )
2048 {
2049 DocumentViewport vp( action->data().toString() );
2050 slotRenameBookmark( vp );
2051 }
2052}
2053
2054void Part::slotRenameCurrentViewportBookmark()
2055{
2056 slotRenameBookmark( m_document->viewport() );
2057}
2058
2059void Part::slotAboutToShowContextMenu(KMenu * /*menu*/, QAction *action, QMenu *contextMenu)
2060{
2061 const QList<QAction*> actions = contextMenu->findChildren<QAction*>("OkularPrivateRenameBookmarkActions");
2062 foreach(QAction *a, actions)
2063 {
2064 contextMenu->removeAction(a);
2065 delete a;
2066 }
2067
2068 KBookmarkAction *ba = dynamic_cast<KBookmarkAction*>(action);
2069 if (ba != NULL)
2070 {
2071 QAction *separatorAction = contextMenu->addSeparator();
2072 separatorAction->setObjectName("OkularPrivateRenameBookmarkActions");
2073 QAction *renameAction = contextMenu->addAction( KIcon( "edit-rename" ), i18n( "Rename this Bookmark" ), this, SLOT(slotRenameBookmarkFromMenu()) );
2074 renameAction->setData(ba->property("htmlRef").toString());
2075 renameAction->setObjectName("OkularPrivateRenameBookmarkActions");
2076 }
2077}
2078
2079void Part::slotPreviousBookmark()
2080{
2081 const KBookmark bookmark = m_document->bookmarkManager()->previousBookmark( m_document->viewport() );
2082
2083 if ( !bookmark.isNull() )
2084 {
2085 DocumentViewport vp( bookmark.url().htmlRef() );
2086 m_document->setViewport( vp );
2087 }
2088}
2089
2090
2091void Part::slotNextBookmark()
2092{
2093 const KBookmark bookmark = m_document->bookmarkManager()->nextBookmark( m_document->viewport() );
2094
2095 if ( !bookmark.isNull() )
2096 {
2097 DocumentViewport vp( bookmark.url().htmlRef() );
2098 m_document->setViewport( vp );
2099 }
2100}
2101
2102
2103void Part::slotFind()
2104{
2105 // when in presentation mode, there's already a search bar, taking care of
2106 // the 'find' requests
2107 if ( (PresentationWidget*)m_presentationWidget != 0 )
2108 {
2109 m_presentationWidget->slotFind();
2110 }
2111 else
2112 {
2113 slotShowFindBar();
2114 }
2115}
2116
2117
2118void Part::slotFindNext()
2119{
2120 if (m_findBar->isHidden())
2121 slotShowFindBar();
2122 else
2123 m_findBar->findNext();
2124}
2125
2126
2127void Part::slotFindPrev()
2128{
2129 if (m_findBar->isHidden())
2130 slotShowFindBar();
2131 else
2132 m_findBar->findPrev();
2133}
2134
2135bool Part::saveFile()
2136{
2137 kDebug() << "Okular part doesn't support saving the file in the location from which it was opened";
2138 return false;
2139}
2140
2141void Part::slotSaveFileAs()
2142{
2143 if ( m_embedMode == PrintPreviewMode )
2144 return;
2145
2146 /* Show a warning before saving if the generator can't save annotations,
2147 * unless we are going to save a .okular archive. */
2148 if ( !isDocumentArchive && !m_document->canSaveChanges( Document::SaveAnnotationsCapability ) )
2149 {
2150 /* Search local annotations */
2151 bool containsLocalAnnotations = false;
2152 const int pagecount = m_document->pages();
2153
2154 for ( int pageno = 0; pageno < pagecount; ++pageno )
2155 {
2156 const Okular::Page *page = m_document->page( pageno );
2157 foreach ( const Okular::Annotation *ann, page->annotations() )
2158 {
2159 if ( !(ann->flags() & Okular::Annotation::External) )
2160 {
2161 containsLocalAnnotations = true;
2162 break;
2163 }
2164 }
2165 if ( containsLocalAnnotations )
2166 break;
2167 }
2168
2169 /* Don't show it if there are no local annotations */
2170 if ( containsLocalAnnotations )
2171 {
2172 int res = KMessageBox::warningContinueCancel( widget(), i18n("Your annotations will not be exported.\nYou can export the annotated document using File -> Export As -> Document Archive") );
2173 if ( res != KMessageBox::Continue )
2174 return; // Canceled
2175 }
2176 }
2177
2178 KUrl saveUrl = KFileDialog::getSaveUrl( url(),
2179 QString(), widget(), QString(),
2180 KFileDialog::ConfirmOverwrite );
2181 if ( !saveUrl.isValid() || saveUrl.isEmpty() )
2182 return;
2183
2184 saveAs( saveUrl );
2185}
2186
2187bool Part::saveAs( const KUrl & saveUrl )
2188{
2189 KTemporaryFile tf;
2190 QString fileName;
2191 if ( !tf.open() )
2192 {
2193 KMessageBox::information( widget(), i18n("Could not open the temporary file for saving." ) );
2194 return false;
2195 }
2196 fileName = tf.fileName();
2197 tf.close();
2198
2199 QString errorText;
2200 bool saved;
2201
2202 if ( isDocumentArchive )
2203 saved = m_document->saveDocumentArchive( fileName );
2204 else
2205 saved = m_document->saveChanges( fileName, &errorText );
2206
2207 if ( !saved )
2208 {
2209 if (errorText.isEmpty())
2210 {
2211 KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", fileName ) );
2212 }
2213 else
2214 {
2215 KMessageBox::information( widget(), i18n("File could not be saved in '%1'. %2", fileName, errorText ) );
2216 }
2217 return false;
2218 }
2219
2220 KIO::Job *copyJob = KIO::file_copy( fileName, saveUrl, -1, KIO::Overwrite );
2221 if ( !KIO::NetAccess::synchronousRun( copyJob, widget() ) )
2222 {
2223 KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", saveUrl.prettyUrl() ) );
2224 return false;
2225 }
2226
2227 setModified( false );
2228 return true;
2229}
2230
2231
2232void Part::slotSaveCopyAs()
2233{
2234 if ( m_embedMode == PrintPreviewMode )
2235 return;
2236
2237 KUrl saveUrl = KFileDialog::getSaveUrl( KUrl("kfiledialog:///okular/" + url().fileName()),
2238 QString(), widget(), QString(),
2239 KFileDialog::ConfirmOverwrite );
2240 if ( saveUrl.isValid() && !saveUrl.isEmpty() )
2241 {
2242 // make use of the already downloaded (in case of remote URLs) file,
2243 // no point in downloading that again
2244 KUrl srcUrl = KUrl::fromPath( localFilePath() );
2245 KTemporaryFile * tempFile = 0;
2246 // duh, our local file disappeared...
2247 if ( !QFile::exists( localFilePath() ) )
2248 {
2249 if ( url().isLocalFile() )
2250 {
2251#ifdef OKULAR_KEEP_FILE_OPEN
2252 // local file: try to get it back from the open handle on it
2253 if ( ( tempFile = m_keeper->copyToTemporary() ) )
2254 srcUrl = KUrl::fromPath( tempFile->fileName() );
2255#else
2256 const QString msg = i18n( "Okular cannot copy %1 to the specified location.\n\nThe document does not exist anymore.", localFilePath() );
2257 KMessageBox::sorry( widget(), msg );
2258 return;
2259#endif
2260 }
2261 else
2262 {
2263 // we still have the original remote URL of the document,
2264 // so copy the document from there
2265 srcUrl = url();
2266 }
2267 }
2268
2269 KIO::Job *copyJob = KIO::file_copy( srcUrl, saveUrl, -1, KIO::Overwrite );
2270 if ( !KIO::NetAccess::synchronousRun( copyJob, widget() ) )
2271 KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", saveUrl.prettyUrl() ) );
2272
2273 delete tempFile;
2274 }
2275}
2276
2277
2278void Part::slotGetNewStuff()
2279{
2280#if 0
2281 KNS::Engine engine(widget());
2282 engine.init( "okular.knsrc" );
2283 // show the modal dialog over pageview and execute it
2284 KNS::Entry::List entries = engine.downloadDialogModal( m_pageView );
2285 Q_UNUSED( entries )
2286#endif
2287}
2288
2289
2290void Part::slotPreferences()
2291{
2292 // Create dialog
2293 PreferencesDialog * dialog = new PreferencesDialog( m_pageView, Okular::Settings::self(), m_embedMode );
2294 dialog->setAttribute( Qt::WA_DeleteOnClose );
2295
2296 // Show it
2297 dialog->show();
2298}
2299
2300
2301void Part::slotAnnotationPreferences()
2302{
2303 // Create dialog
2304 PreferencesDialog * dialog = new PreferencesDialog( m_pageView, Okular::Settings::self(), m_embedMode );
2305 dialog->setAttribute( Qt::WA_DeleteOnClose );
2306
2307 // Show it
2308 dialog->switchToAnnotationsPage();
2309 dialog->show();
2310}
2311
2312
2313void Part::slotNewConfig()
2314{
2315 // Apply settings here. A good policy is to check whether the setting has
2316 // changed before applying changes.
2317
2318 // Watch File
2319 setWatchFileModeEnabled(Okular::Settings::watchFile());
2320
2321 // Main View (pageView)
2322 m_pageView->reparseConfig();
2323
2324 // update document settings
2325 m_document->reparseConfig();
2326
2327 // update TOC settings
2328 if ( m_sidebar->isItemEnabled(0) )
2329 m_toc->reparseConfig();
2330
2331 // update ThumbnailList contents
2332 if ( Okular::Settings::showLeftPanel() && !m_thumbnailList->isHidden() )
2333 m_thumbnailList->updateWidgets();
2334
2335 // update Reviews settings
2336 if ( m_sidebar->isItemEnabled(2) )
2337 m_reviewsWidget->reparseConfig();
2338
2339 setWindowTitleFromDocument ();
2340}
2341
2342
2343void Part::slotPrintPreview()
2344{
2345 if (m_document->pages() == 0) return;
2346
2347 QPrinter printer;
2348
2349 // Native printing supports KPrintPreview, Postscript needs to use FilePrinterPreview
2350 if ( m_document->printingSupport() == Okular::Document::NativePrinting )
2351 {
2352 KPrintPreview previewdlg( &printer, widget() );
2353 setupPrint( printer );
2354 doPrint( printer );
2355 previewdlg.exec();
2356 }
2357 else
2358 {
2359 // Generate a temp filename for Print to File, then release the file so generator can write to it
2360 KTemporaryFile tf;
2361 tf.setAutoRemove( true );
2362 tf.setSuffix( ".ps" );
2363 tf.open();
2364 printer.setOutputFileName( tf.fileName() );
2365 tf.close();
2366 setupPrint( printer );
2367 doPrint( printer );
2368 if ( QFile::exists( printer.outputFileName() ) )
2369 {
2370 Okular::FilePrinterPreview previewdlg( printer.outputFileName(), widget() );
2371 previewdlg.exec();
2372 }
2373 }
2374}
2375
2376
2377void Part::slotShowMenu(const Okular::Page *page, const QPoint &point)
2378{
2379 if ( m_embedMode == PrintPreviewMode )
2380 return;
2381
2382 bool reallyShow = false;
2383 const bool currentPage = page && page->number() == m_document->viewport().pageNumber;
2384
2385 if (!m_actionsSearched)
2386 {
2387 // the quest for options_show_menubar
2388 KActionCollection *ac;
2389 QAction *act;
2390
2391 if (factory())
2392 {
2393 const QList<KXMLGUIClient*> clients(factory()->clients());
2394 for(int i = 0 ; (!m_showMenuBarAction || !m_showFullScreenAction) && i < clients.size(); ++i)
2395 {
2396 ac = clients.at(i)->actionCollection();
2397 // show_menubar
2398 act = ac->action("options_show_menubar");
2399 if (act && qobject_cast<KToggleAction*>(act))
2400 m_showMenuBarAction = qobject_cast<KToggleAction*>(act);
2401 // fullscreen
2402 act = ac->action("fullscreen");
2403 if (act && qobject_cast<KToggleFullScreenAction*>(act))
2404 m_showFullScreenAction = qobject_cast<KToggleFullScreenAction*>(act);
2405 }
2406 }
2407 m_actionsSearched = true;
2408 }
2409
2410 KMenu *popup = new KMenu( widget() );
2411 QAction *addBookmark = 0;
2412 QAction *removeBookmark = 0;
2413 QAction *fitPageWidth = 0;
2414 if (page)
2415 {
2416 popup->addTitle( i18n( "Page %1", page->number() + 1 ) );
2417 if ( ( !currentPage && m_document->bookmarkManager()->isBookmarked( page->number() ) ) ||
2418 ( currentPage && m_document->bookmarkManager()->isBookmarked( m_document->viewport() ) ) )
2419 removeBookmark = popup->addAction( KIcon("edit-delete-bookmark"), i18n("Remove Bookmark") );
2420 else
2421 addBookmark = popup->addAction( KIcon("bookmark-new"), i18n("Add Bookmark") );
2422 if ( m_pageView->canFitPageWidth() )
2423 fitPageWidth = popup->addAction( KIcon("zoom-fit-best"), i18n("Fit Width") );
2424 popup->addAction( m_prevBookmark );
2425 popup->addAction( m_nextBookmark );
2426 reallyShow = true;
2427 }
2428 /*
2429 //Albert says: I have not ported this as i don't see it does anything
2430 if ( d->mouseOnRect ) // and rect->objectType() == ObjectRect::Image ...
2431 {
2432 m_popup->insertItem( SmallIcon("document-save"), i18n("Save Image..."), 4 );
2433 m_popup->setItemEnabled( 4, false );
2434 }*/
2435
2436 if ((m_showMenuBarAction && !m_showMenuBarAction->isChecked()) || (m_showFullScreenAction && m_showFullScreenAction->isChecked()))
2437 {
2438 popup->addTitle( i18n( "Tools" ) );
2439 if (m_showMenuBarAction && !m_showMenuBarAction->isChecked()) popup->addAction(m_showMenuBarAction);
2440 if (m_showFullScreenAction && m_showFullScreenAction->isChecked()) popup->addAction(m_showFullScreenAction);
2441 reallyShow = true;
2442
2443 }
2444
2445 if (reallyShow)
2446 {
2447 QAction *res = popup->exec(point);
2448 if (res)
2449 {
2450 if (res == addBookmark)
2451 {
2452 if (currentPage)
2453 m_document->bookmarkManager()->addBookmark( m_document->viewport() );
2454 else
2455 m_document->bookmarkManager()->addBookmark( page->number() );
2456 }
2457 else if (res == removeBookmark)
2458 {
2459 if (currentPage)
2460 m_document->bookmarkManager()->removeBookmark( m_document->viewport() );
2461 else
2462 m_document->bookmarkManager()->removeBookmark( page->number() );
2463 }
2464 else if (res == fitPageWidth)
2465 {
2466 m_pageView->fitPageWidth( page->number() );
2467 }
2468 }
2469 }
2470 delete popup;
2471}
2472
2473
2474void Part::slotShowProperties()
2475{
2476 PropertiesDialog *d = new PropertiesDialog(widget(), m_document);
2477 d->exec();
2478 delete d;
2479}
2480
2481
2482void Part::slotShowEmbeddedFiles()
2483{
2484 EmbeddedFilesDialog *d = new EmbeddedFilesDialog(widget(), m_document);
2485 d->exec();
2486 delete d;
2487}
2488
2489
2490void Part::slotShowPresentation()
2491{
2492 if ( !m_presentationWidget )
2493 {
2494 m_presentationWidget = new PresentationWidget( widget(), m_document, actionCollection() );
2495 }
2496}
2497
2498
2499void Part::slotHidePresentation()
2500{
2501 if ( m_presentationWidget )
2502 delete (PresentationWidget*) m_presentationWidget;
2503}
2504
2505
2506void Part::slotTogglePresentation()
2507{
2508 if ( m_document->isOpened() )
2509 {
2510 if ( !m_presentationWidget )
2511 m_presentationWidget = new PresentationWidget( widget(), m_document, actionCollection() );
2512 else delete (PresentationWidget*) m_presentationWidget;
2513 }
2514}
2515
2516
2517void Part::reload()
2518{
2519 if ( m_document->isOpened() )
2520 {
2521 slotReload();
2522 }
2523}
2524
2525void Part::enableStartWithPrint()
2526{
2527 m_cliPrint = true;
2528}
2529
2530void Part::slotAboutBackend()
2531{
2532 const KComponentData *data = m_document->componentData();
2533 if ( !data )
2534 return;
2535
2536 KAboutData aboutData( *data->aboutData() );
2537
2538 if ( aboutData.programIconName().isEmpty() || aboutData.programIconName() == aboutData.appName() )
2539 {
2540 if ( const Okular::DocumentInfo *documentInfo = m_document->documentInfo() )
2541 {
2542 const QString mimeTypeName = documentInfo->get("mimeType");
2543 if ( !mimeTypeName.isEmpty() )
2544 {
2545 if ( KMimeType::Ptr type = KMimeType::mimeType( mimeTypeName ) )
2546 aboutData.setProgramIconName( type->iconName() );
2547 }
2548 }
2549 }
2550
2551 KAboutApplicationDialog dlg( &aboutData, widget() );
2552 dlg.exec();
2553}
2554
2555
2556void Part::slotExportAs(QAction * act)
2557{
2558 QList<QAction*> acts = m_exportAs->menu() ? m_exportAs->menu()->actions() : QList<QAction*>();
2559 int id = acts.indexOf( act );
2560 if ( ( id < 0 ) || ( id >= acts.count() ) )
2561 return;
2562
2563 QString filter;
2564 switch ( id )
2565 {
2566 case 0:
2567 filter = "text/plain";
2568 break;
2569 case 1:
2570 filter = "application/vnd.kde.okular-archive";
2571 break;
2572 default:
2573 filter = m_exportFormats.at( id - 2 ).mimeType()->name();
2574 break;
2575 }
2576 QString fileName = KFileDialog::getSaveFileName( url().isLocalFile() ? url().directory() : QString(),
2577 filter, widget(), QString(),
2578 KFileDialog::ConfirmOverwrite );
2579 if ( !fileName.isEmpty() )
2580 {
2581 bool saved = false;
2582 switch ( id )
2583 {
2584 case 0:
2585 saved = m_document->exportToText( fileName );
2586 break;
2587 case 1:
2588 saved = m_document->saveDocumentArchive( fileName );
2589 break;
2590 default:
2591 saved = m_document->exportTo( fileName, m_exportFormats.at( id - 2 ) );
2592 break;
2593 }
2594 if ( !saved )
2595 KMessageBox::information( widget(), i18n("File could not be saved in '%1'. Try to save it to another location.", fileName ) );
2596 }
2597}
2598
2599
2600void Part::slotReload()
2601{
2602 // stop the dirty handler timer, otherwise we may conflict with the
2603 // auto-refresh system
2604 m_dirtyHandler->stop();
2605
2606 slotDoFileDirty();
2607}
2608
2609
2610void Part::slotPrint()
2611{
2612 if (m_document->pages() == 0) return;
2613
2614#ifdef Q_WS_WIN
2615 QPrinter printer(QPrinter::HighResolution);
2616#else
2617 QPrinter printer;
2618#endif
2619 QPrintDialog *printDialog = 0;
2620 QWidget *printConfigWidget = 0;
2621
2622 // Must do certain QPrinter setup before creating QPrintDialog
2623 setupPrint( printer );
2624
2625 // Create the Print Dialog with extra config widgets if required
2626 if ( m_document->canConfigurePrinter() )
2627 {
2628 printConfigWidget = m_document->printConfigurationWidget();
2629 }
2630 if ( printConfigWidget )
2631 {
2632 printDialog = KdePrint::createPrintDialog( &printer, QList<QWidget*>() << printConfigWidget, widget() );
2633 }
2634 else
2635 {
2636 printDialog = KdePrint::createPrintDialog( &printer, widget() );
2637 }
2638
2639 if ( printDialog )
2640 {
2641
2642 // Set the available Print Range
2643 printDialog->setMinMax( 1, m_document->pages() );
2644 printDialog->setFromTo( 1, m_document->pages() );
2645
2646 // If the user has bookmarked pages for printing, then enable Selection
2647 if ( !m_document->bookmarkedPageRange().isEmpty() )
2648 {
2649 printDialog->addEnabledOption( QAbstractPrintDialog::PrintSelection );
2650 }
2651
2652 // If the Document type doesn't support print to both PS & PDF then disable the Print Dialog option
2653 if ( printDialog->isOptionEnabled( QAbstractPrintDialog::PrintToFile ) &&
2654 !m_document->supportsPrintToFile() )
2655 {
2656 printDialog->setEnabledOptions( printDialog->enabledOptions() ^ QAbstractPrintDialog::PrintToFile );
2657 }
2658
2659#if QT_VERSION >= KDE_MAKE_VERSION(4,7,0)
2660 // Enable the Current Page option in the dialog.
2661 if ( m_document->pages() > 1 && currentPage() > 0 )
2662 {
2663 printDialog->setOption( QAbstractPrintDialog::PrintCurrentPage );
2664 }
2665#endif
2666
2667 if ( printDialog->exec() )
2668 doPrint( printer );
2669 delete printDialog;
2670 }
2671}
2672
2673
2674void Part::setupPrint( QPrinter &printer )
2675{
2676 printer.setOrientation(m_document->orientation());
2677
2678 // title
2679 QString title = m_document->metaData( "DocumentTitle" ).toString();
2680 if ( title.isEmpty() )
2681 {
2682 title = m_document->currentDocument().fileName();
2683 }
2684 if ( !title.isEmpty() )
2685 {
2686 printer.setDocName( title );
2687 }
2688}
2689
2690
2691void Part::doPrint(QPrinter &printer)
2692{
2693 if (!m_document->isAllowed(Okular::AllowPrint))
2694 {
2695 KMessageBox::error(widget(), i18n("Printing this document is not allowed."));
2696 return;
2697 }
2698
2699 if (!m_document->print(printer))
2700 {
2701 const QString error = m_document->printError();
2702 if (error.isEmpty())
2703 {
2704 KMessageBox::error(widget(), i18n("Could not print the document. Unknown error. Please report to bugs.kde.org"));
2705 }
2706 else
2707 {
2708 KMessageBox::error(widget(), i18n("Could not print the document. Detailed error is \"%1\". Please report to bugs.kde.org", error));
2709 }
2710 }
2711}
2712
2713
2714void Part::restoreDocument(const KConfigGroup &group)
2715{
2716 KUrl url ( group.readPathEntry( "URL", QString() ) );
2717 if ( url.isValid() )
2718 {
2719 QString viewport = group.readEntry( "Viewport" );
2720 if (!viewport.isEmpty()) m_document->setNextDocumentViewport( Okular::DocumentViewport( viewport ) );
2721 openUrl( url );
2722 }
2723}
2724
2725
2726void Part::saveDocumentRestoreInfo(KConfigGroup &group)
2727{
2728 group.writePathEntry( "URL", url().url() );
2729 group.writeEntry( "Viewport", m_document->viewport().toString() );
2730}
2731
2732
2733void Part::psTransformEnded(int exit, QProcess::ExitStatus status)
2734{
2735 Q_UNUSED( exit )
2736 if ( status != QProcess::NormalExit )
2737 return;
2738
2739 QProcess *senderobj = sender() ? qobject_cast< QProcess * >( sender() ) : 0;
2740 if ( senderobj )
2741 {
2742 senderobj->close();
2743 senderobj->deleteLater();
2744 }
2745
2746 setLocalFilePath( m_temporaryLocalFile );
2747 openUrl( m_temporaryLocalFile );
2748 m_temporaryLocalFile.clear();
2749}
2750
2751
2752void Part::displayInfoMessage( const QString &message, KMessageWidget::MessageType messageType, int duration )
2753{
2754 if ( !Okular::Settings::showOSD() )
2755 {
2756 if (messageType == KMessageWidget::Error)
2757 {
2758 KMessageBox::error( widget(), message );
2759 }
2760 return;
2761 }
2762
2763 // hide messageWindow if string is empty
2764 if ( message.isEmpty() )
2765 m_infoMessage->animatedHide();
2766
2767 // display message (duration is length dependant)
2768 if ( duration < 0 )
2769 {
2770 duration = 500 + 100 * message.length();
2771 }
2772 m_infoTimer->start( duration );
2773 m_infoMessage->setText( message );
2774 m_infoMessage->setMessageType( messageType );
2775 m_infoMessage->setVisible( true );
2776}
2777
2778
2779void Part::errorMessage( const QString &message, int duration )
2780{
2781 displayInfoMessage( message, KMessageWidget::Error, duration );
2782}
2783
2784void Part::warningMessage( const QString &message, int duration )
2785{
2786 displayInfoMessage( message, KMessageWidget::Warning, duration );
2787}
2788
2789void Part::noticeMessage( const QString &message, int duration )
2790{
2791 // less important message -> simpleer display widget in the PageView
2792 m_pageView->displayMessage( message, QString(), PageViewMessage::Info, duration );
2793}
2794
2795
2796void Part::unsetDummyMode()
2797{
2798 if ( m_embedMode == PrintPreviewMode )
2799 return;
2800
2801 m_sidebar->setItemEnabled( 2, true );
2802 m_sidebar->setItemEnabled( 3, true );
2803 m_sidebar->setSidebarVisibility( Okular::Settings::showLeftPanel() );
2804
2805 // add back and next in history
2806 m_historyBack = KStandardAction::documentBack( this, SLOT(slotHistoryBack()), actionCollection() );
2807 m_historyBack->setWhatsThis( i18n( "Go to the place you were before" ) );
2808 connect(m_pageView, SIGNAL(mouseBackButtonClick()), m_historyBack, SLOT(trigger()));
2809
2810 m_historyNext = KStandardAction::documentForward( this, SLOT(slotHistoryNext()), actionCollection());
2811 m_historyNext->setWhatsThis( i18n( "Go to the place you were after" ) );
2812 connect(m_pageView, SIGNAL(mouseForwardButtonClick()), m_historyNext, SLOT(trigger()));
2813
2814 m_pageView->setupActions( actionCollection() );
2815
2816 // attach the actions of the children widgets too
2817 m_formsMessage->addAction( m_pageView->toggleFormsAction() );
2818 m_formsMessage->setVisible( m_pageView->toggleFormsAction() != 0 );
2819
2820 // ensure history actions are in the correct state
2821 updateViewActions();
2822}
2823
2824
2825bool Part::handleCompressed( QString &destpath, const QString &path, const QString &compressedMimetype )
2826{
2827 m_tempfile = 0;
2828
2829 // we are working with a compressed file, decompressing
2830 // temporary file for decompressing
2831 KTemporaryFile *newtempfile = new KTemporaryFile();
2832 newtempfile->setAutoRemove(true);
2833
2834 if ( !newtempfile->open() )
2835 {
2836 KMessageBox::error( widget(),
2837 i18n("<qt><strong>File Error!</strong> Could not create temporary file "
2838 "<nobr><strong>%1</strong></nobr>.</qt>",
2839 strerror(newtempfile->error())));
2840 delete newtempfile;
2841 return false;
2842 }
2843
2844 // decompression filer
2845 QIODevice* filterDev = KFilterDev::deviceForFile( path, compressedMimetype );
2846 if (!filterDev)
2847 {
2848 delete newtempfile;
2849 return false;
2850 }
2851
2852 if ( !filterDev->open(QIODevice::ReadOnly) )
2853 {
2854 KMessageBox::detailedError( widget(),
2855 i18n("<qt><strong>File Error!</strong> Could not open the file "
2856 "<nobr><strong>%1</strong></nobr> for uncompression. "
2857 "The file will not be loaded.</qt>", path),
2858 i18n("<qt>This error typically occurs if you do "
2859 "not have enough permissions to read the file. "
2860 "You can check ownership and permissions if you "
2861 "right-click on the file in the Dolphin "
2862 "file manager and then choose the 'Properties' tab.</qt>"));
2863
2864 delete filterDev;
2865 delete newtempfile;
2866 return false;
2867 }
2868
2869 char buf[65536];
2870 int read = 0, wrtn = 0;
2871
2872 while ((read = filterDev->read(buf, sizeof(buf))) > 0)
2873 {
2874 wrtn = newtempfile->write(buf, read);
2875 if ( read != wrtn )
2876 break;
2877 }
2878 delete filterDev;
2879 if ((read != 0) || (newtempfile->size() == 0))
2880 {
2881 KMessageBox::detailedError(widget(),
2882 i18n("<qt><strong>File Error!</strong> Could not uncompress "
2883 "the file <nobr><strong>%1</strong></nobr>. "
2884 "The file will not be loaded.</qt>", path ),
2885 i18n("<qt>This error typically occurs if the file is corrupt. "
2886 "If you want to be sure, try to decompress the file manually "
2887 "using command-line tools.</qt>"));
2888 delete newtempfile;
2889 return false;
2890 }
2891 m_tempfile = newtempfile;
2892 destpath = m_tempfile->fileName();
2893 return true;
2894}
2895
2896void Part::rebuildBookmarkMenu( bool unplugActions )
2897{
2898 if ( unplugActions )
2899 {
2900 unplugActionList( "bookmarks_currentdocument" );
2901 qDeleteAll( m_bookmarkActions );
2902 m_bookmarkActions.clear();
2903 }
2904 KUrl u = m_document->currentDocument();
2905 if ( u.isValid() )
2906 {
2907 m_bookmarkActions = m_document->bookmarkManager()->actionsForUrl( u );
2908 }
2909 bool havebookmarks = true;
2910 if ( m_bookmarkActions.isEmpty() )
2911 {
2912 havebookmarks = false;
2913 QAction * a = new KAction( 0 );
2914 a->setText( i18n( "No Bookmarks" ) );
2915 a->setEnabled( false );
2916 m_bookmarkActions.append( a );
2917 }
2918 plugActionList( "bookmarks_currentdocument", m_bookmarkActions );
2919
2920 if (factory())
2921 {
2922 const QList<KXMLGUIClient*> clients(factory()->clients());
2923 bool containerFound = false;
2924 for (int i = 0; !containerFound && i < clients.size(); ++i)
2925 {
2926 QWidget *container = factory()->container("bookmarks", clients[i]);
2927 if (container && container->actions().contains(m_bookmarkActions.first()))
2928 {
2929 Q_ASSERT(dynamic_cast<KMenu*>(container));
2930 disconnect(container, 0, this, 0);
2931 connect(container, SIGNAL(aboutToShowContextMenu(KMenu*,QAction*,QMenu*)), this, SLOT(slotAboutToShowContextMenu(KMenu*,QAction*,QMenu*)));
2932 containerFound = true;
2933 }
2934 }
2935 }
2936
2937 m_prevBookmark->setEnabled( havebookmarks );
2938 m_nextBookmark->setEnabled( havebookmarks );
2939}
2940
2941void Part::updateAboutBackendAction()
2942{
2943 const KComponentData *data = m_document->componentData();
2944 if ( data )
2945 {
2946 m_aboutBackend->setEnabled( true );
2947 }
2948 else
2949 {
2950 m_aboutBackend->setEnabled( false );
2951 }
2952}
2953
2954void Part::resetStartArguments()
2955{
2956 m_cliPrint = false;
2957}
2958
2959void Part::setReadWrite(bool readwrite)
2960{
2961 m_document->setAnnotationEditingEnabled( readwrite );
2962 ReadWritePart::setReadWrite( readwrite );
2963}
2964
2965} // namespace Okular
2966
2967#include "part.moc"
2968
2969/* kate: replace-tabs on; indent-width 4; */
2970