Commit 2a64d687 authored by Luboš Luňák's avatar Luboš Luňák
Browse files

Konqueror preloading (keeping last KonqMainWindow instead of destroying it,

and keeping konqy running. to be precise).
Turned off by default for now, and no GUI for the option yet (I need to
recall how to do that ;)  ) :
konquerorrc:
[Reusing]
MaxPreloadCount=<num>

First person with a computer slow enough to measure the speed difference
(telling me it) wins a free upgrade to the kcontrol module having the GUI option.

svn path=/trunk/kdebase/konqueror/; revision=193029
parent 5f894c73
......@@ -23,6 +23,7 @@
#include "KonqMainWindowIface.h"
#include "konq_mainwindow.h"
#include "konq_viewmgr.h"
#include "konq_view.h"
#include <konq_settings.h>
#include <kapplication.h>
#include <dcopclient.h>
......@@ -207,3 +208,67 @@ void KonquerorIface::comboCleared( QCString objId )
KonqMainWindow::comboAction( KonqMainWindow::ComboClear,
QString::null, objId );
}
bool KonquerorIface::processCanBeReused()
{
if( KonqMainWindow::isPreloaded())
return false; // will be handled by preloading related code instead
QPtrList<KonqMainWindow>* windows = KonqMainWindow::mainWindowList();
if( windows == NULL )
return true;
KConfig* cfg = kapp->config();
KConfigGroupSaver saver( cfg, "Reusing" );
QStringList allowed_parts;
allowed_parts << QString::fromLatin1( "konq_iconview.desktop" )
<< QString::fromLatin1( "konq_multicolumnview.desktop" )
<< QString::fromLatin1( "konq_sidebartng.desktop" )
<< QString::fromLatin1( "konq_infolistview.desktop" )
<< QString::fromLatin1( "konq_treeview.desktop" )
<< QString::fromLatin1( "konq_detailedlistview.desktop" );
bool all_parts_allowed = false;
if( cfg->hasKey( "SafeParts" ))
allowed_parts = cfg->readListEntry( "SafeParts" );
else
{ // backwards comp.
KConfig cfg( "kfmclientrc", true );
cfg.setGroup( "Settings" );
QString value = cfg.readEntry( "StartNewKonqueror", QString::fromLatin1( "Web Only " ));
if( value == QString::fromLatin1("Always") ||
value == QString::fromLatin1("true") ||
value == QString::fromLatin1("TRUE") ||
value == QString::fromLatin1("1") )
; // no parts are allowed
else if( value == QString::fromLatin1( "Local Only" ))
allowed_parts << QString::fromLatin1( "KHTMLPart" );
else if( value == QString::fromLatin1( "Web only" ))
; // use the defaults
else
all_parts_allowed = true;
}
if( allowed_parts.count() == 1 && allowed_parts.first() == QString::fromLatin1( "ALL" ))
all_parts_allowed = true;
if( all_parts_allowed )
return true;
for( QPtrListIterator<KonqMainWindow> it1( *windows );
it1 != NULL;
++it1 )
{
kdDebug(1202) << "processCanBeReused: count=" << (*it1)->viewCount() << endl;
const KonqMainWindow::MapViews& views = (*it1)->viewMap();
for( KonqMainWindow::MapViews::ConstIterator it2 = views.begin();
it2 != views.end();
++it2 )
{
kdDebug(1202) << "processCanBeReused: part=" << (*it2)->service()->desktopEntryPath() << ", URL=" << (*it2)->url().prettyURL() << endl;
if( !allowed_parts.contains( (*it2)->service()->desktopEntryPath()))
return false;
}
}
return true;
}
void KonquerorIface::terminatePreloaded()
{
if( KonqMainWindow::isPreloaded())
kapp->exit();
}
......@@ -162,7 +162,17 @@ k_dcop:
*/
ASYNC comboCleared( QCString );
/**
* Used by kfmclient when the 'minimize memory usage' setting is set
* to find out if this konqueror can be used.
*/
bool processCanBeReused();
/**
* Called from konqy_preloader to terminate this Konqueror instance,
* if it's in the preloaded mode, and there are too many preloaded Konqy's
*/
ASYNC terminatePreloaded();
};
#endif
......@@ -3,7 +3,7 @@ AM_CPPFLAGS = -D_LARGEFILE64_SOURCE
INCLUDES = -I$(top_srcdir)/libkonq -I$(top_srcdir)/kcontrol/input $(all_includes)
#SUBDIRS = . kfmexec client iconview dirtree listview keditbookmarks shellcmdplugin about pics sidebar
SUBDIRS = . kfmexec client iconview listview keditbookmarks shellcmdplugin about pics sidebar
SUBDIRS = . kfmexec client iconview listview keditbookmarks shellcmdplugin about pics sidebar preloader
lib_LTLIBRARIES = konqueror.la
......
......@@ -139,28 +139,61 @@ int main( int argc, char **argv )
}
/** Whether to start a new konqueror or reuse an existing process */
static bool startNewKonqueror( const KURL & url )
static bool startNewKonqueror()
{
KConfig config( QString::fromLatin1("kfmclientrc") );
config.setGroup( QString::fromLatin1("Settings") );
// Current default: reuse an existing process for local urls, create a new one for remote ones
QString val = config.readEntry( QString::fromLatin1("StartNewKonqueror"), QString::fromLatin1("Web only") );
if ( (val == QString::fromLatin1("Web only") && !url.isLocalFile()) ||
(val == QString::fromLatin1("Local only") && url.isLocalFile()) ||
( val == QString::fromLatin1("Always") ||
val == QString::fromLatin1("true") ||
val == QString::fromLatin1("TRUE") ||
val == QString::fromLatin1("1")))
return true;
KConfig cfg( QString::fromLatin1( "konquerorrc" ), true );
cfg.setGroup( "Reusing" );
QStringList allowed_parts;
if( cfg.hasKey( "SafeParts" ))
{
if( cfg.readListEntry( "SafeParts" ).isEmpty())
return true; // always start new konqy
return false; // will check safe parts using DCOP
}
else
return false; // means (val == "Never") or one of the two above combinations return false
{ // backwards comp.
KConfig cfg( QString::fromLatin1( "kfmclientrc" ), true );
cfg.setGroup( "Settings" );
QString value = cfg.readEntry( "StartNewKonqueror", QString::fromLatin1( "Web Only " ));
if( value == QString::fromLatin1("Always") ||
value == QString::fromLatin1("true") ||
value == QString::fromLatin1("TRUE") ||
value == QString::fromLatin1("1") )
return true; // always start new konqy
return false; // will check safe parts using DCOP
}
}
// when reusing a preloaded konqy, make sure your always use a DCOP call which opens a profile !
static QCString getPreloadedKonqy()
{
KConfig cfg( QString::fromLatin1( "konquerorrc" ), true );
cfg.setGroup( "Reusing" );
if( cfg.readNumEntry( "MaxPreloadCount", 0 ) == 0 )
return "";
DCOPRef ref( "kded", "konqy_preloader" );
return ref.call( "getPreloadedKonqy" );
}
static QCString konqyToReuse()
{ // prefer(?) preloaded ones
QCString ret = getPreloadedKonqy();
if( !ret.isEmpty())
return ret;
if( startNewKonqueror())
return "";
QCString appObj;
QByteArray data;
if( !kapp->dcopClient()->findObject( "konqueror*", "KonquerorIface",
"processCanBeReused()", data, ret, appObj ) )
return "";
return ret;
}
bool clientApp::createNewWindow(const KURL & url, const QString & mimetype)
{
kdDebug() << "clientApp::createNewWindow " << url.url() << " mimetype=" << mimetype << endl;
QByteArray data;
QCString appId, appObj;
// check if user wants to use external browser
KConfig config( QString::fromLatin1("kfmclientrc"));
......@@ -174,12 +207,11 @@ bool clientApp::createNewWindow(const KURL & url, const QString & mimetype)
return true;
}
if ( !startNewKonqueror(url) &&
dcopClient()->findObject( "konqueror*", "KonquerorIface", "", data,
appId, appObj ) )
QCString appId = konqyToReuse();
if( !appId.isEmpty())
{
kdDebug() << "clientApp::createNewWindow using existing konqueror" << endl;
KonquerorIface_stub konqy( appId, appObj );
KonquerorIface_stub konqy( appId, "KonquerorIface" );
konqy.createNewWindowASN( url.url(), mimetype, kapp->startupId());
KStartupInfoId id;
id.initId( kapp->startupId());
......@@ -219,11 +251,8 @@ bool clientApp::openProfile( const QString & filename, const QString & url, cons
m_profileName = filename;
m_url = url;
m_mimetype = mimetype;
QByteArray data;
QCString appId, appObj;
if ( startNewKonqueror(KURL(url)) ||
!dcopClient()->findObject( "konqueror*", "KonquerorIface", "", data,
appId, appObj ) )
QCString appId = konqyToReuse();
if( appId.isEmpty())
{
QString error;
if ( KApplication::startServiceByDesktopPath( QString::fromLatin1("konqueror.desktop"),
......@@ -232,8 +261,8 @@ bool clientApp::openProfile( const QString & filename, const QString & url, cons
kdError() << "Couldn't start konqueror from konqueror.desktop: " << error << endl;
return false;
}
// startServiceByDesktopPath waits for the app to register with DCOP
// so when we arrive here, konq is up and running already, and appId contains the identification
// startServiceByDesktopPath waits for the app to register with DCOP
// so when we arrive here, konq is up and running already, and appId contains the identification
}
QString profile = locate( "data", QString::fromLatin1("konqueror/profiles/") + m_profileName );
......
......@@ -21,6 +21,7 @@
#include "konq_misc.h"
#include "konq_factory.h"
#include "konq_mainwindow.h"
#include "konq_view.h"
#include "KonquerorIface.h"
#include <ktempfile.h>
......@@ -37,6 +38,7 @@
static KCmdLineOptions options[] =
{
{ "silent", I18N_NOOP("Start without a default window."), 0 },
{ "preload", I18N_NOOP("Preload for later use."), 0 },
{ "profile <profile>", I18N_NOOP("Profile to open."), 0 },
{ "mimetype <mimetype>", I18N_NOOP("Mimetype to use for this URL, (e.g. text/html or inode/directory)."), 0 },
{ "+[URL]", I18N_NOOP("Location to open."), 0 },
......@@ -95,7 +97,23 @@ int main( int argc, char **argv )
{
if (args->count() == 0)
{
if (!args->isSet("silent"))
if (args->isSet("preload"))
{
KConfigGroupSaver group( app.config(), "Reusing" );
if( app.config()->readNumEntry( "MaxPreloadCount", 0 ) > 0 )
{
DCOPRef ref( "kded", "konqy_preloader" );
if( !ref.call( "registerPreloadedKonqy", app.dcopClient()->appId()))
return 0; // too many preloaded or failed
KonqMainWindow::setPreloadedFlag( true );
kdDebug(1202) << "Konqy preloaded :" << app.dcopClient()->appId() << endl;
}
else
{
return 0; // no preloading
}
}
else if (!args->isSet("silent"))
{
KonqMainWindow *mainWindow = new KonqMainWindow;
mainWindow->show();
......@@ -127,20 +145,18 @@ int main( int argc, char **argv )
}
args->clear();
app.ref(); // for preloading
app.exec();
//// Temporary code, waiting for Qt to do this properly
// Delete all toplevel widgets that have WDestructiveClose, so that we don't have
// Delete all KonqMainWindows, so that we don't have
// any parts loaded when KLibLoader::cleanUp is called.
QWidgetList *list = QApplication::topLevelWidgets();
QWidgetListIt it(*list);
QWidget * w;
while( (w=it.current()) != 0 ) {
++it;
if ( w->testWFlags( Qt::WDestructiveClose ) )
delete w;
}
// Their deletion was postponed in their event()
// (and Qt doesn't delete WDestructiveClose widgets on exit anyway :( )
while( KonqMainWindow::mainWindowList() != NULL )
{ // the list will be deleted by last KonqMainWindow
delete KonqMainWindow::mainWindowList()->first();
}
KonqMainWindow::s_crashlog_file->remove();
......
......@@ -98,10 +98,14 @@ QPtrList<KonqMainWindow> *KonqMainWindow::s_lstViews = 0;
KConfig * KonqMainWindow::s_comboConfig = 0;
KCompletion * KonqMainWindow::s_pCompletion = 0;
QFile * KonqMainWindow::s_crashlog_file = 0;
bool KonqMainWindow::s_preloaded = false;
KonqMainWindow* KonqMainWindow::s_preloadedWindow = 0;
KonqMainWindow::KonqMainWindow( const KURL &initialURL, bool openInitialURL, const char *name )
: KParts::MainWindow( name, WDestructiveClose | WStyle_ContextHelp )
{
setPreloadedFlag( false );
if ( !s_lstViews )
s_lstViews = new QPtrList<KonqMainWindow>;
......@@ -265,6 +269,13 @@ KonqMainWindow::~KonqMainWindow()
kdDebug(1202) << "KonqMainWindow::~KonqMainWindow " << this << " done" << endl;
}
// used by preloading - this KonqMainWindow will be reused, reset everything
// that won't be reset by loading a profile
void KonqMainWindow::resetWindow()
{
ignoreInitialGeometry();
}
QWidget * KonqMainWindow::createContainer( QWidget *parent, int index, const QDomElement &element, int &id )
{
static QString nameBookmarkBar = QString::fromLatin1( "bookmarkToolBar" );
......@@ -3956,7 +3967,7 @@ void KonqMainWindow::closeEvent( QCloseEvent *e )
if ( !config->hasKey( "MultipleTabConfirm" ) )
{
if ( KMessageBox::warningYesNo( 0L, i18n("You have multiple tabs open in this window, are you sure you wish to close it?"), i18n("Confirmation"),
if ( KMessageBox::warningYesNo( this, i18n("You have multiple tabs open in this window, are you sure you wish to close it?"), i18n("Confirmation"),
KStdGuiItem::yes(), KStdGuiItem::no(), "MultipleTabConfirm" ) == KMessageBox::No )
{
e->ignore();
......@@ -4309,6 +4320,72 @@ bool KonqMainWindow::isMimeTypeAssociatedWithSelf( const QString &mimeType, cons
// KonqFrameContainerBase implementation END
void KonqMainWindow::setPreloadedFlag( bool preloaded )
{
if( s_preloaded == preloaded )
return;
s_preloaded = preloaded;
if( s_preloaded )
{
kapp->disableSessionManagement(); // dont restore preloaded konqy's
return; // was registered before calling this
}
delete s_preloadedWindow; // preloaded state was abandoned without reusing the window
s_preloadedWindow = NULL;
kapp->enableSessionManagement(); // enable SM again
DCOPRef ref( "kded", "konqy_preloader" );
ref.send( "unregisterPreloadedKonqy", kapp->dcopClient()->appId());
}
void KonqMainWindow::setPreloadedWindow( KonqMainWindow* window )
{
s_preloadedWindow = window;
if( window == NULL )
return;
window->viewManager()->clear();
}
// since the preloading code tries to reuse KonqMainWindow,
// the last window shouldn't be really deleted, but only hidden
// deleting WDestructiveClose windows is done using deleteLater(),
// so catch QEvent::DefferedDelete and check if this window should stay
bool KonqMainWindow::event( QEvent* e )
{
if( e->type() == QEvent::DeferredDelete )
{
if( stayPreloaded())
{
setWFlags(WDestructiveClose); // was reset before deleteLater()
return true; // no deleting
}
}
return KParts::MainWindow::event( e );
}
bool KonqMainWindow::stayPreloaded()
{
if( mainWindowList()->count() > 1 )
return false;
// ok, last one
KConfigGroupSaver group( KGlobal::config(), "Reusing" );
if( KGlobal::config()->readNumEntry( "MaxPreloadCount", 0 ) == 0 )
{
kapp->deref(); // for the extra ref() done in main()
return false;
}
DCOPRef ref( "kded", "konqy_preloader" );
if( !ref.call( "registerPreloadedKonqy", kapp->dcopClient()->appId()))
{
kapp->deref();
return false;
}
KonqMainWindow::setPreloadedFlag( true );
kdDebug(1202) << "Konqy kept for preloading :" << kapp->dcopClient()->appId() << endl;
kapp->ref(); // closeEvent() did deref()
KonqMainWindow::setPreloadedWindow( this );
return true;
}
#include "konq_mainwindow.moc"
/* vim: et sw=4 ts=4
......
......@@ -279,6 +279,14 @@ public:
bool isMimeTypeAssociatedWithSelf( const QString &mimeType );
bool isMimeTypeAssociatedWithSelf( const QString &mimeType, const KService::Ptr &offer );
void resetWindow();
static void setPreloadedFlag( bool preloaded );
static bool isPreloaded() { return s_preloaded; }
static void setPreloadedWindow( KonqMainWindow* );
static KonqMainWindow* preloadedWindow() { return s_preloadedWindow; }
bool stayPreloaded();
signals:
void viewAdded( KonqView *view );
void viewRemoved( KonqView *view );
......@@ -434,6 +442,8 @@ protected slots:
void slotFindClosed( KonqDirPart * dirPart );
void slotIconsChanged();
virtual bool event( QEvent* );
protected:
static QString detectNameFilter( QString & url );
......@@ -648,6 +658,9 @@ private:
bool m_urlCompletionStarted;
bool m_bBackRightClick;
static bool s_preloaded;
static KonqMainWindow* s_preloadedWindow;
public:
......
......@@ -18,11 +18,11 @@
*/
#include <qapplication.h>
#include <qwhatsthis.h>
#include <qstyle.h>
#include <qdir.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kmessagebox.h>
#include <kurifilter.h>
......@@ -31,6 +31,7 @@
#include <kwin.h>
#include <kprotocolinfo.h>
#include <kurldrag.h>
#include <kstartupinfo.h>
#include "konq_misc.h"
#include "konq_mainwindow.h"
......@@ -104,7 +105,6 @@ KonqMainWindow * KonqMisc::createNewWindow( const KURL &url, const KParts::URLAr
return createBrowserWindowFromProfile( profile, profileName, url, args, forbidUseHTML );
}
KonqMainWindow * KonqMisc::createBrowserWindowFromProfile( const QString &path, const QString &filename, const KURL &url, const KParts::URLArgs &args, bool forbidUseHTML )
{
kdDebug(1202) << "void KonqMisc::createBrowserWindowFromProfile() " << endl;
......@@ -119,6 +119,18 @@ KonqMainWindow * KonqMisc::createBrowserWindowFromProfile( const QString &path,
if ( forbidUseHTML )
mainWindow->setShowHTML( false );
}
else if( KonqMainWindow::isPreloaded() && KonqMainWindow::preloadedWindow() != NULL )
{
mainWindow = KonqMainWindow::preloadedWindow();
KStartupInfo::setWindowStartupId( mainWindow->winId(), kapp->startupId());
KonqMainWindow::setPreloadedWindow( NULL );
mainWindow->resetWindow();
mainWindow->setShowHTML( !forbidUseHTML );
//FIXME: obey args (like passing post-data (to KRun), etc.)
KonqOpenURLRequest req;
req.args = args;
mainWindow->viewManager()->loadViewProfile( path, filename, url, req, true );
}
else
{
mainWindow = new KonqMainWindow( KURL(), false );
......
......@@ -790,6 +790,7 @@ void KonqViewManager::viewCountChanged()
}
}
void KonqViewManager::clear()
{
kdDebug(1202) << "KonqViewManager::clear" << endl;
......@@ -981,16 +982,18 @@ void KonqViewManager::saveViewProfile( KConfig & cfg, bool saveURLs, bool saveWi
}
void KonqViewManager::loadViewProfile( const QString & path, const QString & filename,
const KURL & forcedURL, const KonqOpenURLRequest &req )
const KURL & forcedURL, const KonqOpenURLRequest &req,
bool resetWindow )
{
KConfig cfg( path, true );
cfg.setDollarExpansion( true );
cfg.setGroup( "Profile" );
loadViewProfile( cfg, filename, forcedURL, req );
loadViewProfile( cfg, filename, forcedURL, req, resetWindow );
}
void KonqViewManager::loadViewProfile( KConfig &cfg, const QString & filename,
const KURL & forcedURL, const KonqOpenURLRequest &req )
const KURL & forcedURL, const KonqOpenURLRequest &req,
bool resetWindow )
{
m_currentProfile = filename;
m_currentProfileText = cfg.readEntry("Name",filename);
......@@ -1061,6 +1064,8 @@ void KonqViewManager::loadViewProfile( KConfig &cfg, const QString & filename,
QSize size = readConfigSize( cfg, m_pMainWindow );
if ( size.isValid() )
m_pMainWindow->resize( size );
else if( resetWindow )
m_pMainWindow->resize( 700, 480 ); // size from KonqMainWindow ctor
}
// Apply menu/toolbar settings saved in profile. Read from a separate group
......@@ -1071,6 +1076,8 @@ void KonqViewManager::loadViewProfile( KConfig &cfg, const QString & filename,
QString savedGroup = cfg.group();
m_pMainWindow->applyMainWindowSettings( &cfg, "Main Window Settings" );
cfg.setGroup( savedGroup );
} else if( resetWindow ) {
m_pMainWindow->applyMainWindowSettings( KGlobal::config(), "KonqMainWindow" );
}
#ifndef NDEBUG
......
......@@ -176,10 +176,13 @@ public:
* It has to be under the profiles dir. Otherwise, set to QString::null
* @param forcedURL if set, the URL to open, whatever the profile says
* @param req attributes related to @p forcedURL
* @param resetWindow if the profile doesn't have attributes like size or toolbar
* settings, they will be reset to the defaults
*/
void loadViewProfile( KConfig &cfg, const QString & filename,
const KURL & forcedURL = KURL(),
const KonqOpenURLRequest &req = KonqOpenURLRequest() );
const KonqOpenURLRequest &req = KonqOpenURLRequest(),
bool resetWindow = false );
/**
* Loads a view layout from a config file. Removes all views before loading.
......@@ -188,11 +191,13 @@ public:
* It has to be under the profiles dir. Otherwise, set to QString::null
* @param forcedURL if set, the URL to open, whatever the profile says
* @param req attributes related to @p forcedURL
* @param resetWindow if the profile doesn't have attributes like size or toolbar
* settings, they will be reset to the defaults
*/
void loadViewProfile( const QString & path, const QString & filename,
const KURL & forcedURL = KURL(),
const KonqOpenURLRequest &req = KonqOpenURLRequest() );
const KonqOpenURLRequest &req = KonqOpenURLRequest(),
bool resetWindow = false );
/**
* Return the filename of the last profile that was loaded
* by the view manager. For "save settings".
......
AM_CPPFLAGS = -DQT_NO_CAST_ASCII
kde_module_LTLIBRARIES = kded_konqy_preloader.la
INCLUDES= -I.. $(all_includes)
kded_konqy_preloader_la_SOURCES = preloader.cc preloader.skel
kded_konqy_preloader_la_LDFLAGS = $(all_libraries) -module -avoid-version
kded_konqy_preloader_la_LIBADD = $(LIB_KSYCOCA)
METASOURCES = AUTO
KonquerorIface_DIR = $(srcdir)/..
servicesdir = $(kde_servicesdir)/kded
services_DATA = konqy_preloader.desktop
[Desktop Entry]
Encoding=UTF-8
Type=Service
Name=KDED Konqueror Preloader module
ServiceTypes=KDEDModule
X-KDE-ModuleType=Library
X-KDE-Library=konqy_preloader
X-KDE-FactoryName=konqy_preloader
X-KDE-Kded-autoload=false
X-KDE-Kded-load-on-demand=true
/* This file is part of the KDE project
Copyright (C) 2002 Lubos Lunak <l.lunak@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or F