Merge remote-tracking branch 'origin/master' into frameworks

Conflicts:
	autotests/mainshelltest.cpp
	part.cpp
	part.h
	shell/shell.cpp
This commit is contained in:
Montel Laurent 2015-03-17 08:47:52 +01:00
commit d780d15ea9
12 changed files with 257 additions and 62 deletions

View file

@ -11,8 +11,8 @@
#include <qprintdialog.h>
#include <qwidget.h>
#include <kglobal.h>
#include <qtabwidget.h>
#include <kconfiggroup.h>
#include "../shell/okular_main.h"
#include "../shell/shell.h"
@ -75,10 +75,26 @@ private slots:
void testFileRemembersPagePosition();
void test2FilesError_data();
void test2FilesError();
void testSessionRestore_data();
void testSessionRestore();
private:
};
QList<Shell*> getShells()
{
QList<Shell*> shells;
foreach( KMainWindow* kmw, KMainWindow::memberList() )
{
Shell* shell = qobject_cast<Shell*>( kmw );
if( shell )
{
shells.append( shell );
}
}
return shells;
}
Shell *findShell(Shell *ignore = 0)
{
foreach (QWidget *widget, QApplication::topLevelWidgets())
@ -469,6 +485,108 @@ void MainShellTest::test2FilesError()
QVERIFY(!s);
}
void MainShellTest::testSessionRestore_data()
{
QTest::addColumn<QStringList>("paths");
QTest::addColumn<QString>("options");
QTest::addColumn<bool>("useTabsOpen");
QTest::addColumn<bool>("useTabsRestore");
QStringList oneDocPaths( KDESRCDIR "data/file1.pdf" );
QStringList twoDocPaths( oneDocPaths );
twoDocPaths << KDESRCDIR "data/formSamples.pdf";
const QString options = ShellUtils::serializeOptions(false, false, false, false, QString());
QTest::newRow("1 doc, 1 window, tabs") << oneDocPaths << options << true << true;
QTest::newRow("2 docs, 1 window, tabs") << twoDocPaths << options << true << true;
QTest::newRow("2 docs, 2 windows, tabs") << twoDocPaths << options << false << true;
QTest::newRow("2 docs, 2 windows, no tabs") << twoDocPaths << options << false << false;
QTest::newRow("2 docs, 1 window, no tabs") << twoDocPaths << options << true << false;
}
void MainShellTest::testSessionRestore()
{
QFETCH( QStringList, paths );
QFETCH( QString, options );
QFETCH( bool, useTabsOpen );
QFETCH( bool, useTabsRestore );
Okular::Settings::self()->setShellOpenFileInTabs( useTabsOpen );
Okular::Status status = Okular::main( paths, options );
QCOMPARE( status, Okular::Success );
// Gather some information about the state
// Verify that the correct number of windows/tabs were opened
QList<Shell*> shells = getShells();
QVERIFY( !shells.isEmpty() );
int numDocs = 0;
foreach( Shell* shell, shells )
{
QTest::qWaitForWindowShown( shell );
numDocs += shell->m_tabs.size();
}
QCOMPARE( numDocs, paths.size() );
QCOMPARE( shells.size(), useTabsOpen ? 1 : paths.size() );
QTest::qWait( 100 );
// Simulate session shutdown. The actual shutdown path comes through
// QSessionManager XSMP handlers, then KApplication::commitData/saveState,
// then KMWSessionManager::commitData/saveState. Without simulating an X
// session manager, the best we can do here is to make a temporary Config
// and call KMainWindows save functions directly.
QTemporaryFile configFile;
QVERIFY( configFile.open() );
int numWindows = 0;
{ // Scope for config so that we can reconstruct from file
KConfig config( configFile.fileName(), KConfig::SimpleConfig );
foreach( Shell* shell, shells )
{
shell->savePropertiesInternal( &config, ++numWindows );
// Windows aren't necessarily closed on shutdown, but we'll use
// this as a way to trigger the destructor code, which is normally
// connected to the aboutToQuit signal
shell->close();
}
}
// Wait for shells to delete themselves. QTest::qWait doesn't do deferred
// deletions so we'll set up a full event loop to do that.
QEventLoop eventLoop;
QTimer::singleShot( 100, &eventLoop, SLOT(quit()) );
eventLoop.exec( QEventLoop::AllEvents );
shells = getShells();
QVERIFY( shells.isEmpty() );
Okular::Settings::self()->setShellOpenFileInTabs( useTabsRestore );
// Simulate session restore. We can't call KMainWindow::restore() directly
// because it asks for info from the session manager, which doesn't know
// about our temporary config. But the logic here mostly mirrors restore().
KConfig config( configFile.fileName(), KConfig::SimpleConfig );
for( int i = 1; i <= numWindows; ++i )
{
Shell* shell = new Shell;
shell->readPropertiesInternal( &config, i );
shell->show();
}
// Verify that the restore state is reasonable
shells = getShells();
QVERIFY( !shells.isEmpty() );
numDocs = 0;
foreach( Shell* shell, shells )
{
QTest::qWaitForWindowShown( shell );
numDocs += shell->m_tabs.size();
}
QCOMPARE( numDocs, paths.size() );
QCOMPARE( shells.size(), useTabsRestore ? numWindows : paths.size() );
}
QTEST_MAIN( MainShellTest )
#include "mainshelltest.moc"

View file

@ -60,7 +60,7 @@ public:
* @param documentOrientation the orientation stored in the document itself
* @param fileDeletePolicy if the application or system deletes the file
* @param pageSelectPolicy if the application or system selects the pages to print
* @param pageRange page range to print if SystemSlectsPages and user chooses Selection in Print Dialog
* @param pageRange page range to print if SystemSelectsPages and user chooses Selection in Print Dialog
*
* @returns Returns exit code:
* -9 if lpr not found

View file

@ -949,6 +949,16 @@ Context menu actions like Rename Bookmarks etc.)
</variablelist>
</sect2>
</sect1>
<sect1 id="fit-window-to-page">
<title>Fit window to page</title>
<para>
The Fit window to page feature resizes the window so that it is exactly the same size as the page at the current zoom factor.
If the page doesn't completely fit on the screen, the window is enlarged so that the largest possible part of the page is shown.
</para>
<para>
This feature can be accessed by using the keyboard shortcut <keycombo action="simul">&Ctrl;<keycap>J</keycap></keycombo>
</para>
</sect1>
</chapter>
<chapter id="primary-menu-items">

View file

@ -440,6 +440,7 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
connect( m_document, SIGNAL(warning(QString,int)), this, SLOT(warningMessage(QString,int)) );
connect( m_document, SIGNAL(notice(QString,int)), this, SLOT(noticeMessage(QString,int)) );
connect( m_document, SIGNAL(sourceReferenceActivated(const QString&,int,int,bool*)), this, SLOT(slotHandleActivatedSourceReference(const QString&,int,int,bool*)) );
connect( m_pageView, SIGNAL(fitWindowToPage(QSize,QSize)), this, SIGNAL(fitWindowToPage(QSize,QSize)) );
rightLayout->addWidget( m_pageView );
m_findBar = new FindBar( m_document, rightContainer );
rightLayout->addWidget( m_findBar );
@ -2731,26 +2732,6 @@ void Part::doPrint(QPrinter &printer)
}
}
void Part::restoreDocument(const KConfigGroup &group)
{
QUrl url ( group.readPathEntry( "URL", QString() ) );
if ( url.isValid() )
{
QString viewport = group.readEntry( "Viewport" );
if (!viewport.isEmpty()) m_document->setNextDocumentViewport( Okular::DocumentViewport( viewport ) );
openUrl( url );
}
}
void Part::saveDocumentRestoreInfo(KConfigGroup &group)
{
group.writePathEntry( "URL", url().url() );
group.writeEntry( "Viewport", m_document->viewport().toString() );
}
void Part::psTransformEnded(int exit, QProcess::ExitStatus status)
{
Q_UNUSED( exit )
@ -2813,6 +2794,11 @@ void Part::noticeMessage( const QString &message, int duration )
m_pageView->displayMessage( message, QString(), PageViewMessage::Info, duration );
}
void Part::moveSplitter(int sideWidgetSize)
{
m_sidebar->moveSplitter( sideWidgetSize );
}
void Part::unsetDummyMode()
{

5
part.h
View file

@ -159,6 +159,7 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
void enableCloseAction(bool enable);
void mimeTypeChanged(QMimeType mimeType);
void urlsDropped( const QList<QUrl>& urls );
void fitWindowToPage( const QSize& pageViewPortSize, const QSize& pageSize );
protected:
// reimplemented from KParts::ReadWritePart
@ -222,8 +223,6 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
public slots:
// connected to Shell action (and browserExtension), not local one
void slotPrint();
void restoreDocument(const KConfigGroup &group);
void saveDocumentRestoreInfo(KConfigGroup &group);
void slotFileDirty( const QString& );
void slotDoFileDirty();
void psTransformEnded(int, QProcess::ExitStatus);
@ -233,6 +232,8 @@ class OKULARPART_EXPORT Part : public KParts::ReadWritePart, public Okular::Docu
void warningMessage( const QString &message, int duration = -1 );
void noticeMessage( const QString &message, int duration = -1 );
void moveSplitter( const int sideWidgetSize );
private:
Document::OpenResult doOpenFile(const QMimeType &mime, const QString &fileNameToOpen, bool *isCompressedFile);

View file

@ -57,7 +57,7 @@ int main(int argc, char** argv)
// see if we are starting with session management
if (app.isSessionRestored())
{
RESTORE(Shell);
kRestoreMainWindows<Shell>();
}
else
{

View file

@ -57,6 +57,9 @@
static const char *shouldShowMenuBarComingFromFullScreen = "shouldShowMenuBarComingFromFullScreen";
static const char *shouldShowToolBarComingFromFullScreen = "shouldShowToolBarComingFromFullScreen";
static const char* const SESSION_URL_KEY = "Urls";
static const char* const SESSION_TAB_KEY = "ActiveTab";
Shell::Shell( const QString &serializedOptions )
: KParts::MainWindow(), m_menuBarWasShown(true), m_toolBarWasShown(true)
#ifdef KActivities_FOUND
@ -64,7 +67,7 @@ Shell::Shell( const QString &serializedOptions )
#endif
, m_isValid(true)
{
setObjectName( QLatin1String( "okular::Shell" ) );
setObjectName( QLatin1String( "okular::Shell#" ) );
setContextMenuPolicy( Qt::NoContextMenu );
// set the shell's ui resource file
setXMLFile("shell.rc");
@ -105,6 +108,7 @@ Shell::Shell( const QString &serializedOptions )
// then, setup our actions
setupActions();
connect( QCoreApplication::instance(), SIGNAL(aboutToQuit()), SLOT(deleteLater()) );
// and integrate the part's GUI with the shell's
setupGUI(Keys | ToolBar | Save);
createGUI(firstPart);
@ -155,6 +159,7 @@ Shell::~Shell()
{
it->part->closeUrl( false );
}
m_tabs.clear();
}
if (m_unique)
QDBusConnection::sessionBus().unregisterService("org.kde.okular");
@ -320,19 +325,31 @@ void Shell::setupActions()
void Shell::saveProperties(KConfigGroup &group)
{
// the 'config' object points to the session managed
// config file. anything you write here will be available
// later when this app is restored
emit saveDocumentRestoreInfo(group);
// Gather lists of settings to preserve
QStringList urls;
for( int i = 0; i < m_tabs.size(); ++i )
{
urls.append( m_tabs[i].part->url().url() );
}
group.writePathEntry( SESSION_URL_KEY, urls );
group.writeEntry( SESSION_TAB_KEY, m_tabWidget->currentIndex() );
}
void Shell::readProperties(const KConfigGroup &group)
{
// the 'config' object points to the session managed
// config file. this function is automatically called whenever
// the app is being restored. read in here whatever you wrote
// in 'saveProperties'
emit restoreDocument(group);
// Reopen documents based on saved settings
QStringList urls = group.readPathEntry( SESSION_URL_KEY, QStringList() );
while( !urls.isEmpty() )
{
openUrl( QUrl(urls.takeFirst()) );
}
int desiredTab = group.readEntry<int>( SESSION_TAB_KEY, 0 );
if( desiredTab < m_tabs.size() )
{
setActiveTab( desiredTab );
}
}
QStringList Shell::fileFormats() const
@ -399,11 +416,6 @@ void Shell::fileOpen()
}
}
void Shell::slotQuit()
{
close();
}
void Shell::tryRaise()
{
KWindowSystem::forceActiveWindow( window()->effectiveWinId() );
@ -553,12 +565,12 @@ void Shell::applyOptionsToPart( QObject* part, const QString &serializedOptions
void Shell::connectPart( QObject* part )
{
connect( this, SIGNAL(restoreDocument(KConfigGroup)), part, SLOT(restoreDocument(KConfigGroup)));
connect( this, SIGNAL(saveDocumentRestoreInfo(KConfigGroup&)), part, SLOT(saveDocumentRestoreInfo(KConfigGroup&)));
connect( this, SIGNAL(moveSplitter(int)), part, SLOT(moveSplitter(int)) );
connect( part, SIGNAL(enablePrintAction(bool)), this, SLOT(setPrintEnabled(bool)));
connect( part, SIGNAL(enableCloseAction(bool)), this, SLOT(setCloseEnabled(bool)));
connect( part, SIGNAL(mimeTypeChanged(QMimeType)), this, SLOT(setTabIcon(QMimeType)));
connect( part, SIGNAL(urlsDropped(QList<QUrl>)), this, SLOT(handleDroppedUrls(QList<QUrl>)) );
connect( part, SIGNAL(fitWindowToPage(QSize,QSize)), this, SLOT(slotFitWindowToPage(QSize,QSize)) );
}
void Shell::print()
@ -655,4 +667,13 @@ void Shell::moveTabData( int from, int to )
m_tabs.move( from, to );
}
void Shell::slotFitWindowToPage(const QSize& pageViewSize, const QSize& pageSize )
{
const int xOffset = pageViewSize.width() - pageSize.width();
const int yOffset = pageViewSize.height() - pageSize.height();
showNormal();
resize( width() - xOffset, height() - yOffset);
moveSplitter(pageSize.width());
}
/* kate: replace-tabs on; indent-width 4; */

View file

@ -70,8 +70,6 @@ public:
bool isValid() const;
public slots:
void slotQuit();
Q_SCRIPTABLE Q_NOREPLY void tryRaise();
Q_SCRIPTABLE bool openDocument(const QUrl &url, const QString &serializedOptions = QString() );
Q_SCRIPTABLE bool canOpenDocs( int numDocs, int desktop );
@ -89,6 +87,13 @@ protected:
* with @ref saveProperties
*/
void readProperties(const KConfigGroup&);
/**
* Expose internal functions for session restore testing
*/
void savePropertiesInternal(KConfig* config, int num) {KMainWindow::savePropertiesInternal(config,num);}
void readPropertiesInternal(KConfig* config, int num) {KMainWindow::readPropertiesInternal(config,num);}
void readSettings();
void writeSettings();
void setFullScreen( bool );
@ -119,10 +124,11 @@ private slots:
void testTabDrop( const QDragMoveEvent* event, bool& accept );
void handleTabDrop( QDropEvent* event );
void moveTabData( int from, int to );
void slotFitWindowToPage( const QSize& pageViewSize, const QSize& pageSize );
signals:
void restoreDocument(const KConfigGroup &group);
void saveDocumentRestoreInfo(KConfigGroup &group);
void moveSplitter(int sideWidgetSize);
private:
void setupAccel();

View file

@ -224,6 +224,7 @@ public:
QAction * aSpeakStop;
KActionCollection * actionCollection;
QActionGroup * mouseModeActionGroup;
QAction * aFitWindowToPage;
int setting_viewCols;
@ -507,6 +508,12 @@ void PageView::setupViewerActions( KActionCollection * ac )
ac->addAction("view_auto_fit", d->aZoomAutoFit );
connect( d->aZoomAutoFit, SIGNAL(toggled(bool)), SLOT(slotAutoFitToggled(bool)) );
d->aFitWindowToPage = new QAction(QIcon::fromTheme( "zoom-fit-width" ), i18n("Fit Wi&ndow to Page"), this);
d->aFitWindowToPage->setEnabled( Okular::Settings::viewMode() == (int)Okular::Settings::EnumViewMode::Single );
d->aFitWindowToPage->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_J) );
ac->addAction( "fit_window_to_page", d->aFitWindowToPage );
connect( d->aFitWindowToPage, SIGNAL(triggered()), this, SLOT(slotFitWindowToPage()) );
// View-Layout actions
d->aViewMode = new KActionMenu( QIcon::fromTheme( "view-split-left-right" ), i18n( "&View Mode" ), this );
d->aViewMode->setDelayed( false );
@ -1103,6 +1110,8 @@ void PageView::updateActionState( bool haspages, bool documentChanged, bool hasf
#endif
if (d->aMouseMagnifier)
d->aMouseMagnifier->setEnabled(d->document->supportsTiles());
if ( d->aFitWindowToPage )
d->aFitWindowToPage->setEnabled( haspages && !Okular::Settings::viewContinuous() );
}
bool PageView::areSourceLocationsShownGraphically() const
@ -2151,7 +2160,7 @@ void PageView::mousePressEvent( QMouseEvent * e )
d->leftClickTimer.start( QApplication::doubleClickInterval() + 10 );
}
}
else if ( rightButton )
else if ( rightButton && !d->mouseAnn )
{
PageViewItem * pageItem = pickItemOnPoint( eventPos.x(), eventPos.y() );
if ( pageItem )
@ -2300,6 +2309,18 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
d->leftClickTimer.stop();
const bool leftButton = e->button() == Qt::LeftButton;
const bool rightButton = e->button() == Qt::RightButton;
if ( d->mouseAnn && leftButton )
{
// Just finished to move the annotation
d->mouseAnn->setFlags( d->mouseAnn->flags() & ~Okular::Annotation::BeingMoved );
d->document->translatePageAnnotation(d->mouseAnnPageNum, d->mouseAnn, Okular::NormalizedPoint( 0.0, 0.0 ) );
setCursor( Qt::ArrowCursor );
d->mouseAnn = 0;
}
// don't perform any mouse action when no document is shown..
if ( d->items.isEmpty() )
{
@ -2333,17 +2354,6 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
return;
}
if ( d->mouseAnn )
{
// Just finished to move the annotation
d->mouseAnn->setFlags( d->mouseAnn->flags() & ~Okular::Annotation::BeingMoved );
d->document->translatePageAnnotation(d->mouseAnnPageNum, d->mouseAnn, Okular::NormalizedPoint( 0.0, 0.0 ) );
setCursor( Qt::ArrowCursor );
d->mouseAnn = 0;
}
bool leftButton = e->button() == Qt::LeftButton;
bool rightButton = e->button() == Qt::RightButton;
switch ( d->mouseMode )
{
case Okular::Settings::EnumMouseMode::Browse:{
@ -2439,7 +2449,7 @@ void PageView::mouseReleaseEvent( QMouseEvent * e )
#endif
}
}
else if ( rightButton )
else if ( rightButton && !d->mouseAnn )
{
if ( pageItem && pageItem == pageItemPressPos &&
( (d->mousePressPos - e->globalPos()).manhattanLength() < QApplication::startDragDistance() ) )
@ -4041,8 +4051,15 @@ void PageView::toggleFormWidgets( bool on )
void PageView::resizeContentArea( const QSize & newSize )
{
const QSize vs = viewport()->size();
horizontalScrollBar()->setRange( 0, newSize.width() - vs.width() );
verticalScrollBar()->setRange( 0, newSize.height() - vs.height() );
int hRange = newSize.width() - vs.width();
int vRange = newSize.height() - vs.height();
if ( horizontalScrollBar()->isVisible() && hRange == verticalScrollBar()->width() && verticalScrollBar()->isVisible() && vRange == horizontalScrollBar()->height() && Okular::Settings::showScrollBars() )
{
hRange = 0;
vRange = 0;
}
horizontalScrollBar()->setRange( 0, hRange );
verticalScrollBar()->setRange( 0, vRange );
updatePageStep();
}
@ -4147,6 +4164,9 @@ void PageView::slotRelayoutPages()
const bool centerLastPage = centerFirstPage && pageCount % 2 == 0;
const bool continuousView = Okular::Settings::viewContinuous();
const int nCols = overrideCentering ? 1 : viewColumns();
const bool singlePageViewMode = Okular::Settings::viewMode() == Okular::Settings::EnumViewMode::Single;
d->aFitWindowToPage->setEnabled( !continuousView && singlePageViewMode );
// set all items geometry and resize contents. handle 'continuous' and 'single' modes separately
@ -5095,6 +5115,26 @@ void PageView::slotToggleChangeColors()
viewport()->update();
}
void PageView::slotFitWindowToPage()
{
PageViewItem currentPageItem = NULL;
QSize viewportSize = viewport()->size();
foreach ( const PageViewItem * pageItem, d->items )
{
if ( pageItem->isVisible() )
{
currentPageItem = *pageItem;
break;
}
}
const QSize pageSize = QSize( currentPageItem.uncroppedWidth() + kcolWidthMargin, currentPageItem.uncroppedHeight() + krowHeightMargin );
if ( verticalScrollBar()->isVisible() )
viewportSize.setWidth( viewportSize.width() + verticalScrollBar()->width() );
if ( horizontalScrollBar()->isVisible() )
viewportSize.setHeight( viewportSize.height() + horizontalScrollBar()->height() );
emit fitWindowToPage( viewportSize, pageSize );
}
//END private SLOTS
#include "moc_pageview.cpp"

View file

@ -122,6 +122,7 @@ Q_OBJECT
void mouseBackButtonClick();
void mouseForwardButtonClick();
void escPressed();
void fitWindowToPage( const QSize& pageViewPortSize, const QSize& pageSize );
protected:
void resizeEvent( QResizeEvent* );
@ -249,6 +250,7 @@ Q_OBJECT
void slotProcessMovieAction( const Okular::MovieAction *action );
void slotProcessRenditionAction( const Okular::RenditionAction *action );
void slotToggleChangeColors();
void slotFitWindowToPage();
};
#endif

View file

@ -645,6 +645,15 @@ bool Sidebar::isCollapsed() const
return d->sideContainer->isHidden();
}
void Sidebar::moveSplitter(int sideWidgetSize)
{
QList<int> splitterSizeList = d->splitter->sizes();
const int total = splitterSizeList.at( 0 ) + splitterSizeList.at( 1 );
splitterSizeList.replace( 0, total - sideWidgetSize );
splitterSizeList.replace( 1, sideWidgetSize );
d->splitter->setSizes( splitterSizeList );
}
void Sidebar::itemClicked( QListWidgetItem *item )
{
itemClicked( item, UncollapseIfCollapsed );

View file

@ -41,6 +41,8 @@ class Sidebar : public QWidget
void setCollapsed( bool collapsed );
bool isCollapsed() const;
void moveSplitter( int sideWidgetSize );
signals:
void urlsDropped( const QList<QUrl>& urls );