Launch documents from external sources in new tabs

FIXED-IN: 4.13.0
BUGS: 331872
BUGS: 332238
REVIEW: 116700
This commit is contained in:
Jonathan Doman 2014-04-09 23:29:13 +02:00 committed by Albert Astals Cid
parent a7f3a1ac37
commit 89fd56c23b
11 changed files with 189 additions and 75 deletions

View file

@ -346,6 +346,7 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
m_sidebar = new Sidebar( parentWidget );
setWidget( m_sidebar );
connect( m_sidebar, SIGNAL(urlsDropped(KUrl::List)), SLOT(handleDroppedUrls(KUrl::List)) );
// build the document
m_document = new Okular::Document(widget());
@ -382,7 +383,6 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
m_searchWidget = new SearchWidget( thumbsBox, m_document );
m_thumbnailList = new ThumbnailList( thumbsBox, m_document );
// ThumbnailController * m_tc = new ThumbnailController( thumbsBox, m_thumbnailList );
connect( m_thumbnailList, SIGNAL(urlDropped(KUrl)), SLOT(openUrlFromDocument(KUrl)) );
connect( m_thumbnailList, SIGNAL(rightClick(const Okular::Page*,QPoint)), this, SLOT(slotShowMenu(const Okular::Page*,QPoint)) );
tbIndex = m_sidebar->addItem( thumbsBox, KIcon( "view-preview" ), i18n("Thumbnails") );
m_sidebar->setCurrentIndex( tbIndex );
@ -433,7 +433,6 @@ m_cliPresentation(false), m_cliPrint(false), m_embedMode(detectEmbedMode(parentW
m_pageView = new PageView( rightContainer, m_document );
QMetaObject::invokeMethod( m_pageView, "setFocus", Qt::QueuedConnection ); //usability setting
// m_splitter->setFocusProxy(m_pageView);
connect( m_pageView, SIGNAL(urlDropped(KUrl)), SLOT(openUrlFromDocument(KUrl)));
connect( m_pageView, SIGNAL(rightClick(const Okular::Page*,QPoint)), this, SLOT(slotShowMenu(const Okular::Page*,QPoint)) );
connect( m_document, SIGNAL(error(QString,int)), m_pageView, SLOT(errorMessage(QString,int)) );
connect( m_document, SIGNAL(warning(QString,int)), m_pageView, SLOT(warningMessage(QString,int)) );
@ -989,6 +988,17 @@ void Part::openUrlFromBookmarks(const KUrl &_url)
openUrl( url );
}
void Part::handleDroppedUrls( const KUrl::List& urls )
{
if( m_embedMode != NativeShellMode || !openNewFilesInTabs() )
{
openUrlFromDocument( urls.first() );
return;
}
emit urlsDropped( urls );
}
void Part::slotJobStarted(KIO::Job *job)
{
if (job)

2
part.h
View file

@ -158,6 +158,7 @@ class OKULAR_PART_EXPORT Part : public KParts::ReadWritePart, public Okular::Doc
void viewerMenuStateChange(bool enabled);
void enableCloseAction(bool enable);
void mimeTypeChanged(KMimeType::Ptr mimeType);
void urlsDropped( const KUrl::List& urls );
protected:
// reimplemented from KParts::ReadWritePart
@ -176,6 +177,7 @@ class OKULAR_PART_EXPORT Part : public KParts::ReadWritePart, public Okular::Doc
// connected to actions
void openUrlFromDocument(const KUrl &url);
void openUrlFromBookmarks(const KUrl &url);
void handleDroppedUrls( const KUrl::List& urls );
void slotGoToPage();
void slotHistoryBack();
void slotHistoryNext();

View file

@ -18,6 +18,7 @@
#include <klocale.h>
#include <QtDBus/qdbusinterface.h>
#include <QTextStream>
#include <kwindowsystem.h>
#include "aboutdata.h"
#include "shellutils.h"
@ -44,6 +45,77 @@ static bool attachUniqueInstance(KCmdLineArgs* args)
return true;
}
// Ask an existing non-unique instance to open new tabs
static bool attachExistingInstance( KCmdLineArgs* args )
{
if( args->count() < 1 )
return false;
const QStringList services = QDBusConnection::sessionBus().interface()->registeredServiceNames().value();
// Don't match the service without trailing "-" (unique instance)
const QString pattern = "org.kde.okular-";
const QString myPid = QString::number( kapp->applicationPid() );
QScopedPointer<QDBusInterface> bestService;
const int desktop = KWindowSystem::currentDesktop();
// Select the first instance that isn't us (metric may change in future)
foreach( const QString& service, services )
{
if( service.startsWith(pattern) && !service.endsWith(myPid) )
{
bestService.reset( new QDBusInterface(service,"/okularshell","org.kde.okular") );
// Find a window that can handle our documents
const QDBusReply<bool> reply = bestService->call( "canOpenDocs", args->count(), desktop );
if( reply.isValid() && reply.value() )
break;
bestService.reset();
}
}
if( !bestService )
return false;
for( int i = 0; i < args->count(); ++i )
{
QString arg = args->arg( i );
// Copy stdin to temporary file which can be opened by the existing
// window. The temp file is automatically deleted after it has been
// opened. Not sure if this behavior is safe on all platforms.
QScopedPointer<QTemporaryFile> tempFile;
if( arg == "-" )
{
tempFile.reset( new QTemporaryFile );
QFile stdinFile;
if( !tempFile->open() || !stdinFile.open(stdin,QIODevice::ReadOnly) )
return false;
const size_t bufSize = 1024*1024;
QScopedPointer<char,QScopedPointerArrayDeleter<char> > buf( new char[bufSize] );
size_t bytes;
do
{
bytes = stdinFile.read( buf.data(), bufSize );
tempFile->write( buf.data(), bytes );
} while( bytes != 0 );
arg = tempFile->fileName();
}
// Returns false if it can't fit another document
const QDBusReply<bool> reply = bestService->call( "openDocument", arg );
if( !reply.isValid() || !reply.value() )
return false;
}
bestService->call( "tryRaise" );
return true;
}
int main(int argc, char** argv)
{
KAboutData about = okularAboutData( "okular", I18N_NOOP( "Okular" ) );
@ -69,19 +141,14 @@ int main(int argc, char** argv)
// no session.. just start up normally
KCmdLineArgs* args = KCmdLineArgs::parsedArgs();
// try to attach the "unique" session: if we succeed, do nothing more and exit
if (attachUniqueInstance(args))
// try to attach to existing session, unique or not
if (attachUniqueInstance(args) || attachExistingInstance(args))
{
args->clear();
return 0;
}
if (args->count() == 0)
{
Shell* widget = new Shell(args);
widget->show();
}
else if (args->isSet( "unique" ) && args->count() > 1)
if (args->isSet( "unique" ) && args->count() > 1)
{
QTextStream stream(stderr);
stream << i18n( "Error: Can't open more than one document with the --unique switch" ) << endl;
@ -89,11 +156,18 @@ int main(int argc, char** argv)
}
else
{
for (int i = 0; i < args->count(); ++i)
Shell* shell = new Shell( args );
shell->show();
for( int i = 0; i < args->count(); )
{
if( shell->openDocument(args->arg(i)) )
++i;
else
{
Shell* widget = new Shell(args, i);
widget->show();
shell = new Shell( args );
shell->show();
}
}
}
}

View file

@ -45,6 +45,7 @@
#include <kwindowsystem.h>
#include <ktabwidget.h>
#include <kxmlguifactory.h>
#include <QDragMoveEvent>
#ifdef KActivities_FOUND
#include <KActivities/ResourceInstance>
@ -104,6 +105,8 @@ void Shell::init()
m_tabWidget->setDocumentMode( true );
connect( m_tabWidget, SIGNAL(currentChanged(int)), SLOT(setActiveTab(int)) );
connect( m_tabWidget, SIGNAL(tabCloseRequested(int)), SLOT(closeTab(int)) );
connect( m_tabWidget, SIGNAL(testCanDecode(const QDragMoveEvent*,bool&)), SLOT(testTabDrop(const QDragMoveEvent*,bool&)) );
connect( m_tabWidget, SIGNAL(receivedDropEvent(QDropEvent*)), SLOT(handleTabDrop(QDropEvent*)) );
setCentralWidget( m_tabWidget );
@ -119,11 +122,6 @@ void Shell::init()
readSettings();
if (m_args && m_args->isSet("unique") && m_args->count() == 1)
{
QDBusConnection::sessionBus().registerService("org.kde.okular");
}
m_unique = false;
if (m_args && m_args->isSet("unique") && m_args->count() <= 1)
{
@ -171,6 +169,43 @@ Shell::~Shell()
m_args->clear();
}
// Open a new document if we have space for it
// This can hang if called on a unique instance and openUrl pops a messageBox
bool Shell::openDocument( const QString& doc )
{
if( m_tabs.size() <= 0 )
return false;
KParts::ReadWritePart* const part = m_tabs[0].part;
// Return false if we can't open new tabs and the only part is occupied
if( !dynamic_cast<Okular::ViewerInterface*>(part)->openNewFilesInTabs()
&& !part->url().isEmpty() )
return false;
openUrl( ShellUtils::urlFromArg(doc,ShellUtils::qfileExistFunc()) );
return true;
}
bool Shell::canOpenDocs( int numDocs, int desktop )
{
if( m_tabs.size() <= 0 || numDocs <= 0 || m_unique )
return false;
KParts::ReadWritePart* const part = m_tabs[0].part;
const bool allowTabs = dynamic_cast<Okular::ViewerInterface*>(part)->openNewFilesInTabs();
if( !allowTabs && (numDocs > 1 || !part->url().isEmpty()) )
return false;
const KWindowInfo winfo( window()->effectiveWinId(), KWindowSystem::WMDesktop );
if( winfo.desktop() != desktop )
return false;
return true;
}
void Shell::openUrl( const KUrl & url )
{
const int activeTab = m_tabWidget->currentIndex();
@ -382,10 +417,7 @@ void Shell::slotQuit()
void Shell::tryRaise()
{
if (m_unique)
{
KWindowSystem::forceActiveWindow( window()->effectiveWinId() );
}
KWindowSystem::forceActiveWindow( window()->effectiveWinId() );
}
// only called when starting the program
@ -521,6 +553,7 @@ void Shell::connectPart( QObject* part )
connect( part, SIGNAL(enablePrintAction(bool)), this, SLOT(setPrintEnabled(bool)));
connect( part, SIGNAL(enableCloseAction(bool)), this, SLOT(setCloseEnabled(bool)));
connect( part, SIGNAL(mimeTypeChanged(KMimeType::Ptr)), this, SLOT(setTabIcon(KMimeType::Ptr)));
connect( part, SIGNAL(urlsDropped(KUrl::List)), this, SLOT(handleDroppedUrls(KUrl::List)) );
}
void Shell::print()
@ -593,6 +626,26 @@ int Shell::findTabIndex( QObject* sender )
return -1;
}
void Shell::handleDroppedUrls( const KUrl::List& urls )
{
foreach( const KUrl& url, urls )
{
openUrl( url );
}
}
void Shell::testTabDrop( const QDragMoveEvent* event, bool& accept )
{
accept = KUrl::List::canDecode( event->mimeData() );
}
void Shell::handleTabDrop( QDropEvent* event )
{
const KUrl::List list = KUrl::List::fromMimeData( event->mimeData() );
if( !list.isEmpty() )
handleDroppedUrls( list );
}
#include "shell.moc"
/* kate: replace-tabs on; indent-width 4; */

View file

@ -63,6 +63,8 @@ public slots:
void slotQuit();
Q_SCRIPTABLE Q_NOREPLY void tryRaise();
Q_SCRIPTABLE bool openDocument( const QString& doc );
Q_SCRIPTABLE bool canOpenDocs( int numDocs, int desktop );
protected:
/**
@ -98,12 +100,15 @@ private slots:
void setPrintEnabled( bool enabled );
void setCloseEnabled( bool enabled );
void setTabIcon( KMimeType::Ptr mimeType );
void handleDroppedUrls( const KUrl::List& urls );
// Tab event handlers
void setActiveTab( int tab );
void closeTab( int tab );
void activateNextTab();
void activatePrevTab();
void testTabDrop( const QDragMoveEvent* event, bool& accept );
void handleTabDrop( QDropEvent* event );
signals:
void restoreDocument(const KConfigGroup &group);

View file

@ -260,7 +260,7 @@ OkularTTS* PageViewPrivate::tts()
* Code weight (in rows) and meaning:
* 160 - constructor and creating actions plus their connected slots (empty stuff)
* 70 - DocumentObserver inherited methodes (important)
* 550 - events: mouse, keyboard, drag/drop
* 550 - events: mouse, keyboard, drag
* 170 - slotRelayoutPages: set contents of the scrollview on continuous/single modes
* 100 - zoom: zooming pages in different ways, keeping update the toolbar actions, etc..
* other misc functions: only slotRequestVisiblePixmaps and pickItemOnPoint noticeable,
@ -362,12 +362,11 @@ PageView::PageView( QWidget *parent, Okular::Document *document )
setObjectName( QLatin1String( "okular::pageView" ) );
// viewport setup: setup focus, accept drops and track mouse
// viewport setup: setup focus, and track mouse
viewport()->setFocusProxy( this );
viewport()->setFocusPolicy( Qt::StrongFocus );
viewport()->setAttribute( Qt::WA_OpaquePaintEvent );
viewport()->setAttribute( Qt::WA_NoSystemBackground );
setAcceptDrops( true );
viewport()->setMouseTracking( true );
viewport()->setAutoFillBackground( false );
// the apparently "magic" value of 20 is the same used internally in QScrollArea
@ -3088,26 +3087,6 @@ void PageView::wheelEvent( QWheelEvent *e )
updateCursor();
}
void PageView::dragEnterEvent( QDragEnterEvent * ev )
{
ev->accept();
}
void PageView::dragMoveEvent( QDragMoveEvent * ev )
{
ev->accept();
}
void PageView::dropEvent( QDropEvent * ev )
{
if ( KUrl::List::canDecode( ev->mimeData() ) )
{
const KUrl::List list = KUrl::List::fromMimeData( ev->mimeData() );
if ( !list.isEmpty() )
emit urlDropped( list.first() );
}
}
bool PageView::viewportEvent( QEvent * e )
{
if ( e->type() == QEvent::ToolTip && Okular::Settings::mouseMode() == Okular::Settings::EnumMouseMode::Browse )

View file

@ -30,7 +30,6 @@
class KAction;
class KActionCollection;
class KMenu;
class KUrl;
namespace Okular {
class Action;
@ -136,7 +135,6 @@ Q_OBJECT
void openAnnotationWindow( Okular::Annotation *annotation, int pageNumber );
signals:
void urlDropped( const KUrl& );
void rightClick( const Okular::Page *, const QPoint & );
void mouseBackButtonClick();
void mouseForwardButtonClick();
@ -151,11 +149,6 @@ Q_OBJECT
void inputMethodEvent( QInputMethodEvent * );
void wheelEvent( QWheelEvent* );
// drag and drop related events
void dragEnterEvent( QDragEnterEvent* );
void dragMoveEvent( QDragMoveEvent* );
void dropEvent( QDropEvent* );
void paintEvent( QPaintEvent *e );
void tabletEvent (QTabletEvent *e );
void mouseMoveEvent( QMouseEvent *e );

View file

@ -449,6 +449,7 @@ Sidebar::Sidebar( QWidget *parent )
mainlay->setSpacing( 0 );
setAutoFillBackground( true );
setAcceptDrops( true );
d->list = new SidebarListWidget( this );
mainlay->addWidget( d->list );
@ -753,4 +754,16 @@ void Sidebar::appearanceChanged()
d->sideDelegate->updateBrushCache();
}
void Sidebar::dragEnterEvent( QDragEnterEvent* event )
{
event->setAccepted( KUrl::List::canDecode(event->mimeData()) );
}
void Sidebar::dropEvent( QDropEvent* event )
{
const KUrl::List list = KUrl::List::fromMimeData( event->mimeData() );
if( !list.isEmpty() )
emit urlsDropped( list );
}
#include "sidebar.moc"

View file

@ -11,6 +11,7 @@
#define _SIDEBAR_H_
#include <qwidget.h>
#include <KUrl>
class QIcon;
class QListWidgetItem;
@ -41,6 +42,13 @@ class Sidebar : public QWidget
void setCollapsed( bool collapsed );
bool isCollapsed() const;
signals:
void urlsDropped( const KUrl::List& urls );
protected:
void dragEnterEvent( QDragEnterEvent* event );
void dropEvent( QDropEvent* event );
private slots:
void itemClicked( QListWidgetItem *item );
void splitterMoved( int pos, int index );

View file

@ -208,8 +208,6 @@ ThumbnailList::ThumbnailList( QWidget *parent, Okular::Document *document )
setAttribute( Qt::WA_StaticContents );
setAcceptDrops( true );
viewport()->setBackgroundRole( QPalette::Base );
setWidget( d );
@ -600,21 +598,6 @@ void ThumbnailListPrivate::viewportResizeEvent( QResizeEvent * e )
// update Thumbnails since width has changed or height has increased
delayedRequestVisiblePixmaps( 500 );
}
void ThumbnailList::dragEnterEvent( QDragEnterEvent * ev )
{
ev->accept();
}
void ThumbnailList::dropEvent( QDropEvent * ev )
{
if ( KUrl::List::canDecode( ev->mimeData() ) )
{
const KUrl::List list = KUrl::List::fromMimeData( ev->mimeData() );
if ( !list.isEmpty() )
emit urlDropped( list.first() );
}
}
//END widget events
//BEGIN internal SLOTS

View file

@ -17,7 +17,6 @@
#include "core/observer.h"
class KUrl;
class ThumbnailListPrivate;
namespace Okular {
@ -63,12 +62,7 @@ Q_OBJECT
// catch the viewport event and filter them if necessary
bool viewportEvent( QEvent * );
// file drop related events (an url may be dropped even here)
void dragEnterEvent( QDragEnterEvent* );
void dropEvent( QDropEvent* );
signals:
void urlDropped( const KUrl& );
void rightClick( const Okular::Page *, const QPoint & );
private: