Page MenuHomePhorge

No OneTemporary

diff --git a/kdeui/kernel/kapplication.cpp b/kdeui/kernel/kapplication.cpp
index 19064935cb..b289c182dd 100644
--- a/kdeui/kernel/kapplication.cpp
+++ b/kdeui/kernel/kapplication.cpp
@@ -1,1159 +1,1160 @@
/* This file is part of the KDE libraries
Copyright (C) 1997 Matthias Kalle Dalheimer (kalle@kde.org)
Copyright (C) 1998, 1999, 2000 KDE Team
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 FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kapplication.h"
// TODO: KDE5 +#include "kdeversion.h"
#include <config.h>
#include <QtCore/QDir>
#include <QtCore/QFile>
#include <QSessionManager>
#include <QStyleFactory>
#include <QtCore/QTimer>
#include <QWidget>
#include <QtCore/QList>
#include <QtDBus/QtDBus>
#include <QtCore/QMetaType>
#include "kauthorized.h"
#include "kaboutdata.h"
#include "kcheckaccelerators.h"
#include "kcrash.h"
#include "kconfig.h"
#include "kcmdlineargs.h"
#include "kclipboard.h"
#include "kglobalsettings.h"
#include "kdebug.h"
#include "kglobal.h"
#include "kicon.h"
#include "klocale.h"
#include "ksessionmanager.h"
#include "kstandarddirs.h"
#include "kstandardshortcut.h"
#include "ktoolinvocation.h"
#include "kgesturemap.h"
#include "kurl.h"
#include "kmessage.h"
#include "kmessageboxmessagehandler.h"
#if defined Q_WS_X11
#include <qx11info_x11.h>
#include <kstartupinfo.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <sys/wait.h>
#ifndef Q_WS_WIN
#include "kwindowsystem.h"
#endif
#include <fcntl.h>
#include <stdlib.h> // srand(), rand()
#include <unistd.h>
#if defined Q_WS_X11
//#ifndef Q_WS_QWS //FIXME(embedded): NetWM should talk to QWS...
#include <netwm.h>
#endif
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#ifdef Q_WS_X11
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/SM/SMlib.h>
#include <fixx11h.h>
#include <QX11Info>
#endif
#ifdef Q_WS_MACX
// ick
#undef Status
#include <Carbon/Carbon.h>
#include <QImage>
#include <ksystemtrayicon.h>
#include <kkernel_mac.h>
#endif
#ifdef Q_OS_UNIX
#include <signal.h>
#endif
#include <qstandardpaths.h>
#include <QActionEvent>
#include <kcomponentdata.h>
KApplication* KApplication::KApp = 0L;
bool KApplication::loadedByKdeinit = false;
#ifdef Q_WS_X11
static Atom atom_DesktopWindow;
static Atom atom_NetSupported;
static Atom kde_xdnd_drop;
static QByteArray* startup_id_tmp;
#endif
template class QList<KSessionManager*>;
#ifdef Q_WS_X11
extern "C" {
static int kde_xio_errhandler( Display * dpy )
{
return kapp->xioErrhandler( dpy );
}
static int kde_x_errhandler( Display *dpy, XErrorEvent *err )
{
return kapp->xErrhandler( dpy, err );
}
}
#endif
#ifdef Q_WS_WIN
void KApplication_init_windows();
#endif
/*
Private data to make keeping binary compatibility easier
*/
class KApplicationPrivate
{
public:
KApplicationPrivate(KApplication* q, const QByteArray &cName)
: q(q)
, componentData(cName)
, startup_id("0")
, app_started_timer(0)
, session_save(false)
#ifdef Q_WS_X11
, oldIceIOErrorHandler(0)
, oldXErrorHandler(0)
, oldXIOErrorHandler(0)
#endif
, pSessionConfig( 0 )
, bSessionManagement( true )
{
}
KApplicationPrivate(KApplication* q, const KComponentData &cData)
: q(q)
, componentData(cData)
, startup_id("0")
, app_started_timer(0)
, session_save(false)
#ifdef Q_WS_X11
, oldIceIOErrorHandler(0)
, oldXErrorHandler(0)
, oldXIOErrorHandler(0)
#endif
, pSessionConfig( 0 )
, bSessionManagement( true )
{
}
KApplicationPrivate(KApplication *q)
: q(q)
, componentData(KCmdLineArgs::aboutData())
, startup_id( "0" )
, app_started_timer( 0 )
, session_save( false )
#ifdef Q_WS_X11
, oldIceIOErrorHandler( 0 )
, oldXErrorHandler( 0 )
, oldXIOErrorHandler( 0 )
#endif
, pSessionConfig( 0 )
, bSessionManagement( true )
{
}
~KApplicationPrivate()
{
}
#ifndef KDE3_SUPPORT
KConfig *config() { return KGlobal::config().data(); }
#endif
void _k_x11FilterDestroyed();
void _k_checkAppStartedSlot();
void _k_slot_KToolInvocation_hook(QStringList&, QByteArray&);
QString sessionConfigName() const;
void init(bool GUIenabled=true);
void parseCommandLine( ); // Handle KDE arguments (Using KCmdLineArgs)
static void preqapplicationhack();
static void preread_app_startup_id();
void read_app_startup_id();
KApplication *q;
KComponentData componentData;
QByteArray startup_id;
QTimer* app_started_timer;
bool session_save;
#ifdef Q_WS_X11
IceIOErrorHandler oldIceIOErrorHandler;
int (*oldXErrorHandler)(Display*,XErrorEvent*);
int (*oldXIOErrorHandler)(Display*);
#endif
QString sessionKey;
QString pSessionConfigFile;
KConfig* pSessionConfig; //instance specific application config object
bool bSessionManagement;
};
static QList< QWeakPointer< QWidget > > *x11Filter = 0;
/**
* Installs a handler for the SIGPIPE signal. It is thrown when you write to
* a pipe or socket that has been closed.
* The handler is installed automatically in the constructor, but you may
* need it if your application or component does not have a KApplication
* instance.
*/
static void installSigpipeHandler()
{
#ifdef Q_OS_UNIX
struct sigaction act;
act.sa_handler = SIG_IGN;
sigemptyset( &act.sa_mask );
act.sa_flags = 0;
sigaction( SIGPIPE, &act, 0 );
#endif
}
void KApplication::installX11EventFilter( QWidget* filter )
{
if ( !filter )
return;
if (!x11Filter)
x11Filter = new QList< QWeakPointer< QWidget > >;
connect ( filter, SIGNAL( destroyed() ), this, SLOT( _k_x11FilterDestroyed() ) );
x11Filter->append( filter );
}
void KApplicationPrivate::_k_x11FilterDestroyed()
{
q->removeX11EventFilter( static_cast< const QWidget* >(q->sender()));
}
void KApplication::removeX11EventFilter( const QWidget* filter )
{
if ( !x11Filter || !filter )
return;
// removeAll doesn't work, creating QWeakPointer to something that's about to be deleted aborts
// x11Filter->removeAll( const_cast< QWidget* >( filter ));
for( QMutableListIterator< QWeakPointer< QWidget > > it( *x11Filter );
it.hasNext();
) {
QWidget* w = it.next().data();
if( w == filter || w == NULL )
it.remove();
}
if ( x11Filter->isEmpty() ) {
delete x11Filter;
x11Filter = 0;
}
}
bool KApplication::notify(QObject *receiver, QEvent *event)
{
QEvent::Type t = event->type();
if( t == QEvent::Show && receiver->isWidgetType())
{
QWidget* w = static_cast< QWidget* >( receiver );
#if defined Q_WS_X11
if( w->isTopLevel() && !startupId().isEmpty()) // TODO better done using window group leader?
KStartupInfo::setWindowStartupId( w->winId(), startupId());
#endif
if( w->isTopLevel() && !( w->windowFlags() & Qt::X11BypassWindowManagerHint ) && w->windowType() != Qt::Popup && !event->spontaneous())
{
if( d->app_started_timer == NULL )
{
d->app_started_timer = new QTimer( this );
connect( d->app_started_timer, SIGNAL( timeout()), SLOT( _k_checkAppStartedSlot()));
}
if( !d->app_started_timer->isActive()) {
d->app_started_timer->setSingleShot( true );
d->app_started_timer->start( 0 );
}
}
}
return QApplication::notify(receiver, event);
}
void KApplicationPrivate::_k_checkAppStartedSlot()
{
#if defined Q_WS_X11
KStartupInfo::handleAutoAppStartedSending();
#endif
}
/*
Auxiliary function to calculate a a session config name used for the
instance specific config object.
Syntax: "session/<appname>_<sessionId>"
*/
QString KApplicationPrivate::sessionConfigName() const
{
#ifdef QT_NO_SESSIONMANAGER
#error QT_NO_SESSIONMANAGER was set, this will not compile. Reconfigure Qt with Session management support.
#endif
QString sessKey = q->sessionKey();
if ( sessKey.isEmpty() && !sessionKey.isEmpty() )
sessKey = sessionKey;
return QString(QLatin1String("session/%1_%2_%3")).arg(q->applicationName()).arg(q->sessionId()).arg(sessKey);
}
#ifdef Q_WS_X11
static SmcConn mySmcConnection = 0;
#else
// FIXME(E): Implement for Qt Embedded
// Possibly "steal" XFree86's libSM?
#endif
KApplication::KApplication(bool GUIenabled)
: QApplication((KApplicationPrivate::preqapplicationhack(),KCmdLineArgs::qtArgc()), KCmdLineArgs::qtArgv(), GUIenabled),
d(new KApplicationPrivate(this))
{
d->read_app_startup_id();
setApplicationName(d->componentData.componentName());
setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
installSigpipeHandler();
d->init(GUIenabled);
}
#ifdef Q_WS_X11
KApplication::KApplication(Display *dpy, Qt::HANDLE visual, Qt::HANDLE colormap)
: QApplication((KApplicationPrivate::preqapplicationhack(),dpy), KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), visual, colormap),
d(new KApplicationPrivate(this))
{
d->read_app_startup_id();
setApplicationName(d->componentData.componentName());
setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
installSigpipeHandler();
d->init();
}
KApplication::KApplication(Display *dpy, Qt::HANDLE visual, Qt::HANDLE colormap, const KComponentData &cData)
: QApplication((KApplicationPrivate::preqapplicationhack(),dpy), KCmdLineArgs::qtArgc(), KCmdLineArgs::qtArgv(), visual, colormap),
d (new KApplicationPrivate(this, cData))
{
d->read_app_startup_id();
setApplicationName(d->componentData.componentName());
setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
installSigpipeHandler();
d->init();
}
#endif
KApplication::KApplication(bool GUIenabled, const KComponentData &cData)
: QApplication((KApplicationPrivate::preqapplicationhack(),KCmdLineArgs::qtArgc()), KCmdLineArgs::qtArgv(), GUIenabled),
d (new KApplicationPrivate(this, cData))
{
d->read_app_startup_id();
setApplicationName(d->componentData.componentName());
setOrganizationDomain(d->componentData.aboutData()->organizationDomain());
installSigpipeHandler();
d->init(GUIenabled);
}
#ifdef Q_WS_X11
KApplication::KApplication(Display *display, int& argc, char** argv, const QByteArray& rAppName,
bool GUIenabled)
: QApplication((KApplicationPrivate::preqapplicationhack(),display)),
d(new KApplicationPrivate(this, rAppName))
{
Q_UNUSED(GUIenabled);
d->read_app_startup_id();
setApplicationName(QLatin1String(rAppName));
installSigpipeHandler();
KCmdLineArgs::initIgnore(argc, argv, rAppName.data());
d->init();
}
#endif
// this function is called in KApplication ctors while evaluating arguments to QApplication ctor,
// i.e. before QApplication ctor is called
void KApplicationPrivate::preqapplicationhack()
{
preread_app_startup_id();
KGlobal::config(); // initialize qt plugin path (see KComponentDataPrivate::lazyInit)
}
int KApplication::xioErrhandler( Display* dpy )
{
if(kapp)
{
#ifdef Q_WS_X11
d->oldXIOErrorHandler( dpy );
#else
Q_UNUSED(dpy);
#endif
}
exit( 1 );
return 0;
}
int KApplication::xErrhandler( Display* dpy, void* err_ )
{ // no idea how to make forward decl. for XErrorEvent
#ifdef Q_WS_X11
XErrorEvent* err = static_cast< XErrorEvent* >( err_ );
if(kapp)
{
// add KDE specific stuff here
d->oldXErrorHandler( dpy, err );
}
const QByteArray fatalXError = qgetenv("KDE_FATAL_X_ERROR");
if (!fatalXError.isEmpty()) {
abort();
}
#endif
return 0;
}
void KApplication::iceIOErrorHandler( _IceConn *conn )
{
emit aboutToQuit();
#ifdef Q_WS_X11
if ( d->oldIceIOErrorHandler != NULL )
(*d->oldIceIOErrorHandler)( conn );
#endif
exit( 1 );
}
void KApplicationPrivate::init(bool GUIenabled)
{
if ((getuid() != geteuid()) ||
(getgid() != getegid()))
{
fprintf(stderr, "The KDE libraries are not designed to run with suid privileges.\n");
::exit(127);
}
#ifdef Q_WS_MAC
mac_initialize_dbus();
#endif
KApplication::KApp = q;
// make sure the clipboard is created before setting the window icon (bug 209263)
if(GUIenabled)
(void) QApplication::clipboard();
extern KDECORE_EXPORT bool kde_kdebug_enable_dbus_interface;
kde_kdebug_enable_dbus_interface = true;
parseCommandLine();
if(GUIenabled)
(void) KClipboardSynchronizer::self();
QApplication::setDesktopSettingsAware( false );
#ifdef Q_WS_X11
// create all required atoms in _one_ roundtrip to the X server
if ( q->type() == KApplication::GuiClient ) {
const int max = 20;
Atom* atoms[max];
char* names[max];
Atom atoms_return[max];
int n = 0;
atoms[n] = &atom_DesktopWindow;
names[n++] = (char *) "KDE_DESKTOP_WINDOW";
atoms[n] = &atom_NetSupported;
names[n++] = (char *) "_NET_SUPPORTED";
atoms[n] = &kde_xdnd_drop;
names[n++] = (char *) "XdndDrop";
XInternAtoms( QX11Info::display(), names, n, false, atoms_return );
for (int i = 0; i < n; i++ )
*atoms[i] = atoms_return[i];
}
#endif
// sanity checking, to make sure we've connected
extern void qDBusBindToApplication();
qDBusBindToApplication();
QDBusConnectionInterface *bus = 0;
if (!QDBusConnection::sessionBus().isConnected() || !(bus = QDBusConnection::sessionBus().interface())) {
kFatal(240) << "Session bus not found" << endl <<
"To circumvent this problem try the following command (with Linux and bash)" << endl <<
"export $(dbus-launch)";
::exit(125);
}
extern bool s_kuniqueapplication_startCalled;
if ( bus && !s_kuniqueapplication_startCalled ) // don't register again if KUniqueApplication did so already
{
QStringList parts = q->organizationDomain().split(QLatin1Char('.'), QString::SkipEmptyParts);
QString reversedDomain;
if (parts.isEmpty())
reversedDomain = QLatin1String("local.");
else
foreach (const QString& s, parts)
{
reversedDomain.prepend(QLatin1Char('.'));
reversedDomain.prepend(s);
}
const QString pidSuffix = QString::number( getpid() ).prepend( QLatin1String("-") );
const QString serviceName = reversedDomain + q->applicationName() + pidSuffix;
if ( bus->registerService(serviceName) == QDBusConnectionInterface::ServiceNotRegistered ) {
kError(240) << "Couldn't register name '" << serviceName << "' with DBUS - another process owns it already!" << endl;
::exit(126);
}
}
QDBusConnection::sessionBus().registerObject(QLatin1String("/MainApplication"), q,
QDBusConnection::ExportScriptableSlots |
QDBusConnection::ExportScriptableProperties |
QDBusConnection::ExportAdaptors);
// Trigger creation of locale.
(void) KGlobal::locale();
KSharedConfig::Ptr config = componentData.config();
QByteArray readOnly = qgetenv("KDE_HOME_READONLY");
if (readOnly.isEmpty() && q->applicationName() != QLatin1String("kdialog"))
{
if (KAuthorized::authorize(QLatin1String("warn_unwritable_config")))
config->isConfigWritable(true);
}
if (q->type() == KApplication::GuiClient)
{
#ifdef Q_WS_X11
// this is important since we fork() to launch the help (Matthias)
fcntl(ConnectionNumber(QX11Info::display()), F_SETFD, FD_CLOEXEC);
// set up the fancy (=robust and error ignoring ) KDE xio error handlers (Matthias)
oldXErrorHandler = XSetErrorHandler( kde_x_errhandler );
oldXIOErrorHandler = XSetIOErrorHandler( kde_xio_errhandler );
#endif
// Trigger initial settings
KGlobalSettings::self()->activate();
KMessage::setMessageHandler( new KMessageBoxMessageHandler(0) );
KCheckAccelerators::initiateIfNeeded(q);
KGestureMap::self()->installEventFilterOnMe( q );
q->connect(KToolInvocation::self(), SIGNAL(kapplication_hook(QStringList&, QByteArray&)),
q, SLOT(_k_slot_KToolInvocation_hook(QStringList&,QByteArray&)));
}
#ifdef Q_WS_MAC
if (q->type() == KApplication::GuiClient) {
// This is a QSystemTrayIcon instead of K* because we can't be sure q is a QWidget
QSystemTrayIcon *trayIcon; //krazy:exclude=qclasses
if (QSystemTrayIcon::isSystemTrayAvailable()) //krazy:exclude=qclasses
{
trayIcon = new QSystemTrayIcon(q); //krazy:exclude=qclasses
trayIcon->setIcon(q->windowIcon());
/* it's counter-intuitive, but once you do setIcon it's already set the
dock icon... ->show actually shows an icon in the menu bar too :P */
// trayIcon->show();
}
}
#endif
qRegisterMetaType<KUrl>();
+ qRegisterMetaType<QList<KUrl> >();
qRegisterMetaType<QList<QUrl> >();
#ifdef Q_WS_WIN
KApplication_init_windows();
#endif
}
KApplication* KApplication::kApplication()
{
return KApp;
}
KConfig* KApplication::sessionConfig()
{
if (!d->pSessionConfig) // create an instance specific config object
d->pSessionConfig = new KConfig( d->sessionConfigName(), KConfig::SimpleConfig );
return d->pSessionConfig;
}
void KApplication::reparseConfiguration()
{
KGlobal::config()->reparseConfiguration();
}
void KApplication::quit()
{
QApplication::quit();
}
void KApplication::disableSessionManagement() {
d->bSessionManagement = false;
}
void KApplication::enableSessionManagement() {
d->bSessionManagement = true;
#ifdef Q_WS_X11
// Session management support in Qt/KDE is awfully broken.
// If konqueror disables session management right after its startup,
// and enables it later (preloading stuff), it won't be properly
// saved on session shutdown.
// I'm not actually sure why it doesn't work, but saveState()
// doesn't seem to be called on session shutdown, possibly
// because disabling session management after konqueror startup
// disabled it somehow. Forcing saveState() here for this application
// seems to fix it.
if( mySmcConnection ) {
SmcRequestSaveYourself( mySmcConnection, SmSaveLocal, False,
SmInteractStyleAny,
False, False );
// flush the request
IceFlush(SmcGetIceConnection(mySmcConnection));
}
#endif
}
void KApplication::commitData( QSessionManager& sm )
{
d->session_save = true;
bool canceled = false;
foreach (KSessionManager *it, KSessionManager::sessionClients()) {
if ( ( canceled = !it->commitData( sm ) ) )
break;
}
if ( canceled )
sm.cancel();
if ( sm.allowsInteraction() ) {
QWidgetList donelist, todolist;
QWidget* w;
commitDataRestart:
todolist = QApplication::topLevelWidgets();
for ( int i = 0; i < todolist.size(); ++i ) {
w = todolist.at( i );
if( !w )
break;
if ( donelist.contains( w ) )
continue;
if ( !w->isHidden() && !w->inherits( "KMainWindow" ) ) {
QCloseEvent e;
sendEvent( w, &e );
if ( !e.isAccepted() )
break; //canceled
donelist.append( w );
//grab the new list that was just modified by our closeevent
goto commitDataRestart;
}
}
}
if ( !d->bSessionManagement )
sm.setRestartHint( QSessionManager::RestartNever );
else
sm.setRestartHint( QSessionManager::RestartIfRunning );
d->session_save = false;
}
#ifdef Q_WS_X11
static void checkRestartVersion( QSessionManager& sm )
{
Display* dpy = QX11Info::display();
Atom type;
int format;
unsigned long nitems, after;
unsigned char* data;
if( dpy != NULL && XGetWindowProperty( dpy, RootWindow( dpy, 0 ), XInternAtom( dpy, "KDE_SESSION_VERSION", False ),
0, 1, False, AnyPropertyType, &type, &format, &nitems, &after, &data ) == Success ) {
if( type == XA_CARDINAL && format == 32 ) {
int version = *( long* ) data;
if( version == KDE_VERSION_MAJOR ) { // we run in our native session
XFree( data );
return; // no need to wrap
}
}
XFree( data );
}
if( getenv( "KDE_SESSION_VERSION" ) != NULL && atoi( getenv( "KDE_SESSION_VERSION" )) == KDE_VERSION_MAJOR )
return; // we run in our native session, no need to wrap
#define NUM_TO_STRING2( num ) #num
#define NUM_TO_STRING( num ) NUM_TO_STRING2( num )
QString wrapper = QStandardPaths::findExecutable( "kde" NUM_TO_STRING( KDE_VERSION_MAJOR ) ); // "kde4", etc.
#undef NUM_TO_STRING
#undef NUM_TO_STRING2
if( !wrapper.isEmpty()) {
QStringList restartCommand = sm.restartCommand();
restartCommand.prepend( wrapper );
sm.setRestartCommand( restartCommand );
}
}
#endif // Q_WS_X11
void KApplication::saveState( QSessionManager& sm )
{
d->session_save = true;
#ifdef Q_WS_X11
static bool firstTime = true;
mySmcConnection = (SmcConn) sm.handle();
if ( !d->bSessionManagement ) {
sm.setRestartHint( QSessionManager::RestartNever );
d->session_save = false;
return;
}
else
sm.setRestartHint( QSessionManager::RestartIfRunning );
if ( firstTime ) {
firstTime = false;
d->session_save = false;
return; // no need to save the state.
}
// remove former session config if still existing, we want a new
// and fresh one. Note that we do not delete the config file here,
// this is done by the session manager when it executes the
// discard commands. In fact it would be harmful to remove the
// file here, as the session might be stored under a different
// name, meaning the user still might need it eventually.
delete d->pSessionConfig;
d->pSessionConfig = 0;
// tell the session manager about our new lifecycle
QStringList restartCommand = sm.restartCommand();
QByteArray multiHead = qgetenv("KDE_MULTIHEAD");
if (multiHead.toLower() == "true") {
// if multihead is enabled, we save our -display argument so that
// we are restored onto the correct head... one problem with this
// is that the display is hard coded, which means we cannot restore
// to a different display (ie. if we are in a university lab and try,
// try to restore a multihead session, our apps could be started on
// someone else's display instead of our own)
QByteArray displayname = qgetenv("DISPLAY");
if (! displayname.isNull()) {
// only store the command if we actually have a DISPLAY
// environment variable
restartCommand.append(QLatin1String("-display"));
restartCommand.append(QLatin1String(displayname));
}
sm.setRestartCommand( restartCommand );
}
#ifdef Q_WS_X11
checkRestartVersion( sm );
#endif
// finally: do session management
emit saveYourself(); // for compatibility
bool canceled = false;
foreach(KSessionManager* it, KSessionManager::sessionClients()) {
if(canceled) break;
canceled = !it->saveState( sm );
}
// if we created a new session config object, register a proper discard command
if ( d->pSessionConfig ) {
d->pSessionConfig->sync();
QStringList discard;
discard << QLatin1String("rm") << QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + '/' + d->sessionConfigName();
sm.setDiscardCommand( discard );
} else {
sm.setDiscardCommand( QStringList( QLatin1String("") ) );
}
if ( canceled )
sm.cancel();
#endif
d->session_save = false;
}
bool KApplication::sessionSaving() const
{
return d->session_save;
}
void KApplicationPrivate::parseCommandLine( )
{
KCmdLineArgs *args = KCmdLineArgs::parsedArgs("kde");
if (args && args->isSet("style"))
{
extern QString kde_overrideStyle; // see KGlobalSettings. Should we have a static setter?
QString reqStyle(args->getOption("style").toLower());
if (QStyleFactory::keys().contains(reqStyle, Qt::CaseInsensitive))
kde_overrideStyle = reqStyle;
else
qWarning() << i18n("The style '%1' was not found", reqStyle);
}
if ( q->type() != KApplication::Tty ) {
if (args && args->isSet("icon"))
{
q->setWindowIcon(KIcon(args->getOption("icon")));
}
else {
q->setWindowIcon(KIcon(componentData.aboutData()->programIconName()));
}
}
if (!args)
return;
if (args->isSet("config"))
{
QString config = args->getOption("config");
componentData.setConfigName(config);
}
bool nocrashhandler = (!qgetenv("KDE_DEBUG").isEmpty());
if (!nocrashhandler && args->isSet("crashhandler"))
{
// enable drkonqi
KCrash::setDrKonqiEnabled(true);
}
// Always set the app name, can be usefuls for apps that call setEmergencySaveFunction or enable AutoRestart
KCrash::setApplicationName(args->appName());
if (!QCoreApplication::applicationDirPath().isEmpty()) {
KCrash::setApplicationPath(QCoreApplication::applicationDirPath());
}
#ifdef Q_WS_X11
if ( args->isSet( "waitforwm" ) ) {
Atom type;
(void) q->desktop(); // trigger desktop creation, we need PropertyNotify events for the root window
int format;
unsigned long length, after;
unsigned char *data;
while ( XGetWindowProperty( QX11Info::display(), QX11Info::appRootWindow(), atom_NetSupported,
0, 1, false, AnyPropertyType, &type, &format,
&length, &after, &data ) != Success || !length ) {
if ( data )
XFree( data );
XEvent event;
XWindowEvent( QX11Info::display(), QX11Info::appRootWindow(), PropertyChangeMask, &event );
}
if ( data )
XFree( data );
}
#endif
#ifndef Q_WS_WIN
if (args->isSet("smkey"))
{
sessionKey = args->getOption("smkey");
}
#endif
}
extern void kDebugCleanup();
KApplication::~KApplication()
{
#ifdef Q_WS_X11
if ( d->oldXErrorHandler != NULL )
XSetErrorHandler( d->oldXErrorHandler );
if ( d->oldXIOErrorHandler != NULL )
XSetIOErrorHandler( d->oldXIOErrorHandler );
if ( d->oldIceIOErrorHandler != NULL )
IceSetIOErrorHandler( d->oldIceIOErrorHandler );
#endif
delete d;
KApp = 0;
#ifdef Q_WS_X11
mySmcConnection = 0;
#endif
}
#ifdef Q_WS_X11
class KAppX11HackWidget: public QWidget
{
public:
bool publicx11Event( XEvent * e) { return x11Event( e ); }
};
#endif
#ifdef Q_WS_X11
bool KApplication::x11EventFilter( XEvent *_event )
{
if (x11Filter) {
foreach (const QWeakPointer< QWidget >& wp, *x11Filter) {
if( QWidget* w = wp.data())
if ( static_cast<KAppX11HackWidget*>( w )->publicx11Event(_event))
return true;
}
}
return false;
}
#endif // Q_WS_X11
void KApplication::updateUserTimestamp( int time )
{
#if defined Q_WS_X11
if( time == 0 )
{ // get current X timestamp
Window w = XCreateSimpleWindow( QX11Info::display(), QX11Info::appRootWindow(), 0, 0, 1, 1, 0, 0, 0 );
XSelectInput( QX11Info::display(), w, PropertyChangeMask );
unsigned char data[ 1 ];
XChangeProperty( QX11Info::display(), w, XA_ATOM, XA_ATOM, 8, PropModeAppend, data, 1 );
XEvent ev;
XWindowEvent( QX11Info::display(), w, PropertyChangeMask, &ev );
time = ev.xproperty.time;
XDestroyWindow( QX11Info::display(), w );
}
if( QX11Info::appUserTime() == 0
|| NET::timestampCompare( time, QX11Info::appUserTime()) > 0 ) // time > appUserTime
QX11Info::setAppUserTime(time);
if( QX11Info::appTime() == 0
|| NET::timestampCompare( time, QX11Info::appTime()) > 0 ) // time > appTime
QX11Info::setAppTime(time);
#endif
}
unsigned long KApplication::userTimestamp() const
{
#if defined Q_WS_X11
return QX11Info::appUserTime();
#else
return 0;
#endif
}
void KApplication::updateRemoteUserTimestamp( const QString& service, int time )
{
#if defined Q_WS_X11
Q_ASSERT(service.contains('.'));
if( time == 0 )
time = QX11Info::appUserTime();
QDBusInterface(service, QLatin1String("/MainApplication"),
QString(QLatin1String("org.kde.KApplication")))
.call(QLatin1String("updateUserTimestamp"), time);
#endif
}
#ifndef KDE_NO_DEPRECATED
QString KApplication::tempSaveName( const QString& pFilename )
{
QString aFilename;
if( QDir::isRelativePath(pFilename) )
{
kWarning(240) << "Relative filename passed to KApplication::tempSaveName";
aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath();
}
else
aFilename = pFilename;
QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") );
if( !aAutosaveDir.exists() )
{
if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) )
{
// Last chance: use temp dir
aAutosaveDir.setPath( KGlobal::dirs()->saveLocation("tmp") );
}
}
aFilename.replace( '/', QLatin1String("\\!") )
.prepend( QLatin1Char('#') )
.append( QLatin1Char('#') )
.prepend( QLatin1Char('/') ).prepend( aAutosaveDir.absolutePath() );
return aFilename;
}
#endif
QString KApplication::checkRecoverFile( const QString& pFilename,
bool& bRecover )
{
QString aFilename;
if( QDir::isRelativePath(pFilename) )
{
kWarning(240) << "Relative filename passed to KApplication::tempSaveName";
aFilename = QFileInfo( QDir( QLatin1String(".") ), pFilename ).absoluteFilePath();
}
else
aFilename = pFilename;
QDir aAutosaveDir( QDir::homePath() + QLatin1String("/autosave/") );
if( !aAutosaveDir.exists() )
{
if( !aAutosaveDir.mkdir( aAutosaveDir.absolutePath() ) )
{
// Last chance: use temp dir
aAutosaveDir.setPath( QDir::tempPath() );
}
}
aFilename.replace( QLatin1String("/"), QLatin1String("\\!") )
.prepend( QLatin1Char('#') )
.append( QLatin1Char('#') )
.prepend( QLatin1Char('/') )
.prepend( aAutosaveDir.absolutePath() );
if( QFile( aFilename ).exists() )
{
bRecover = true;
return aFilename;
}
else
{
bRecover = false;
return pFilename;
}
}
void KApplication::setTopWidget( QWidget *topWidget )
{
if( !topWidget )
return;
// set the specified caption
if ( !topWidget->inherits("KMainWindow") ) { // KMainWindow does this already for us
topWidget->setWindowTitle(KGlobal::caption());
}
#ifdef Q_WS_X11
// set the app startup notification window property
KStartupInfo::setWindowStartupId(topWidget->winId(), startupId());
#endif
}
QByteArray KApplication::startupId() const
{
return d->startup_id;
}
void KApplication::setStartupId( const QByteArray& startup_id )
{
if( startup_id == d->startup_id )
return;
#if defined Q_WS_X11
KStartupInfo::handleAutoAppStartedSending(); // finish old startup notification if needed
#endif
if( startup_id.isEmpty())
d->startup_id = "0";
else
{
d->startup_id = startup_id;
#if defined Q_WS_X11
KStartupInfoId id;
id.initId( startup_id );
long timestamp = id.timestamp();
if( timestamp != 0 )
updateUserTimestamp( timestamp );
#endif
}
}
void KApplication::clearStartupId()
{
d->startup_id = "0";
}
// Qt reads and unsets the value and doesn't provide any way to reach the value,
// so steal it from it beforehand. If Qt gets API for taking (reading and unsetting)
// the startup id from it, this can be dumped.
void KApplicationPrivate::preread_app_startup_id()
{
#if defined Q_WS_X11
KStartupInfoId id = KStartupInfo::currentStartupIdEnv();
KStartupInfo::resetStartupEnv();
startup_id_tmp = new QByteArray( id.id());
#endif
}
// read the startup notification env variable, save it and unset it in order
// not to propagate it to processes started from this app
void KApplicationPrivate::read_app_startup_id()
{
#if defined Q_WS_X11
startup_id = *startup_id_tmp;
delete startup_id_tmp;
startup_id_tmp = NULL;
#endif
}
// Hook called by KToolInvocation
void KApplicationPrivate::_k_slot_KToolInvocation_hook(QStringList& envs,QByteArray& startup_id)
{
#ifdef Q_WS_X11
if (QX11Info::display()) {
QByteArray dpystring(XDisplayString(QX11Info::display()));
envs << QLatin1String("DISPLAY=") + dpystring;
} else {
const QByteArray dpystring( qgetenv( "DISPLAY" ));
if(!dpystring.isEmpty())
envs << QLatin1String("DISPLAY=") + dpystring;
}
if(startup_id.isEmpty())
startup_id = KStartupInfo::createNewStartupId();
#else
Q_UNUSED(envs);
Q_UNUSED(startup_id);
#endif
}
void KApplication::setSynchronizeClipboard(bool synchronize)
{
KClipboardSynchronizer::self()->setSynchronizing(synchronize);
KClipboardSynchronizer::self()->setReverseSynchronizing(synchronize);
}
#include "moc_kapplication.cpp"
diff --git a/staging/kcoreaddons/src/io/kurl.h b/staging/kcoreaddons/src/io/kurl.h
index 4743c00fd8..da5fbfb043 100644
--- a/staging/kcoreaddons/src/io/kurl.h
+++ b/staging/kcoreaddons/src/io/kurl.h
@@ -1,1193 +1,1196 @@
// -*- c-basic-offset: 2 -*-
/* This file is part of the KDE libraries
* Copyright (C) 1999 Torben Weis <weis@kde.org>
* Copyright (C) 2005-2006 David Faure <faure@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 FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef kurl_h
#define kurl_h
#include <kcoreaddons_export.h>
#include <QtCore/QVariant>
#include <QtCore/QUrl>
#include <QtCore/QMap>
class QStringList;
class QMimeData;
class KUrlPrivate;
// maybe we should encapsulate QUrl instead of inheriting from it.
// this would even allow us to inherit from KUri instead.
// and this way hacks like setPath() would be less ugly, and we could avoid
// half KDE code using setScheme() and the other half using setProtocol(), etc.
// (DF)
/**
* \class KUrl kurl.h <KUrl>
*
* Represents and parses a URL.
*
* A prototypical URL looks like:
* \code
* protocol://user:password\@hostname:port/path/to/file.ext#reference
* \endcode
*
* KUrl handles escaping of URLs. This means that the specification
* of a full URL will differ from the corresponding string that would specify a
* local file or directory in file-operations like fopen. This is because an URL
* doesn't allow certain characters and escapes them. (e.g. '#'->"%23", space->"%20")
* (In a URL the hash-character '#' is used to specify a "reference", i.e. the position
* within a document).
*
* The constructor KUrl(const QString&) expects a string properly escaped,
* or at least non-ambiguous.
* If you have the absolute path you should use KUrl::fromPath(const QString&).
* \code
* KUrl kurl = KUrl::fromPath("/bar/#foo#");
* QString url = kurl.url(); // -> "file:///bar/%23foo%23"
* \endcode
*
* If you have the URL of a local file or directory and need the absolute path,
* you would use toLocalFile().
* \code
* KUrl url( "file:///bar/%23foo%23" );
* ...
* if ( url.isLocalFile() )
* QString path = url.toLocalFile(); // -> "/bar/#foo#"
* \endcode
*
* This must also be considered when you have separated directory and file
* strings and need to put them together.
* While you can simply concatenate normal path strings, you must take care if
* the directory-part is already an escaped URL.
* (This might be needed if the user specifies a relative path, and your
* program supplies the rest from elsewhere.)
*
* Wrong:
* \code
* QString dirUrl = "file:///bar/";
* QString fileName = "#foo#";
* QString invalidURL = dirUrl + fileName; // -> "file:///bar/#foo#" won't behave like you would expect.
* \endcode
* Instead you should use addPath():
* Right:
* \code
* KUrl url( "file:///bar/" );
* QString fileName = "#foo#";
* url.addPath( fileName );
* QString validURL = url.url(); // -> "file:///bar/%23foo%23"
* \endcode
*
* Also consider that some URLs contain the password, but this shouldn't be
* visible. Your program should use prettyUrl() every time it displays a
* URL, whether in the GUI or in debug output or...
*
* \code
* KUrl url( "ftp://name:password@ftp.faraway.org/bar/%23foo%23");
* QString visibleURL = url.prettyUrl(); // -> "ftp://name@ftp.faraway.org/bar/%23foo%23"
* \endcode
* Note that prettyUrl() doesn't change the character escapes (like "%23").
* Otherwise the URL would be invalid and the user wouldn't be able to use it in another
* context.
*
*/
class KCOREADDONS_EXPORT KUrl : public QUrl // krazy:exclude=dpointer,qclasses (krazy can't deal with embedded classes)
{
public:
typedef QMap<QString, QString> MetaDataMap;
enum MimeDataFlags { DefaultMimeDataFlags = 0, NoTextExport = 1 };
/**
* Options to be used in adjustPath
*/
enum AdjustPathOption
{
/**
* strips a trailing '/', except when the path is already just "/".
*/
RemoveTrailingSlash,
/**
* Do not change the path.
*/
LeaveTrailingSlash,
/**
* adds a trailing '/' if there is none yet
*/
AddTrailingSlash
};
/**
* \class List kurl.h <KUrl>
*
* KUrl::List is a QList that contains KUrls with a few
* convenience methods.
* @see KUrl
* @see QList
*/
class KCOREADDONS_EXPORT List : public QList<KUrl> //krazy:exclude=dpointer (just some convenience methods)
{
public:
/**
* Creates an empty List.
*/
List() { }
/**
* Creates a list that contains the given URL as only
* item.
* @param url the url to add.
*/
List(const KUrl &url);
/**
* Creates a list that contains the URLs from the given
* list of strings.
* @param list the list containing the URLs as strings
*/
List(const QStringList &list);
/**
* Creates a list that contains the URLs from the given QList<KUrl>.
* @param list the list containing the URLs
*/
List(const QList<KUrl> &list);
/**
* Creates a list that contains the URLs from the given QList<KUrl>.
* @param list the list containing the URLs
* @since 4.7
*/
List(const QList<QUrl> &list);
/**
* Converts the URLs of this list to a list of strings.
* @return the list of strings
*/
QStringList toStringList() const;
/**
* Converts the URLs of this list to a list of strings.
*
* @param trailing use to add or remove a trailing slash to/from the path.
*
* @return the list of strings
*
* @since 4.6
*/
QStringList toStringList(KUrl::AdjustPathOption trailing) const;
/**
* Converts this KUrl::List to a QVariant, this allows to use KUrl::List
* in QVariant() constructor
*/
operator QVariant() const;
/**
* Converts this KUrl::List into a list of QUrl instances.
* @since 4.7
*/
operator QList<QUrl>() const;
/**
* Adds URLs data into the given QMimeData.
*
* By default, populateMimeData also exports the URLs as plain text, for e.g. dropping
* onto a text editor.
* But in some cases this might not be wanted, e.g. if adding other mime data
* which provides better plain text data.
*
* WARNING: do not call this method multiple times on the same mimedata object,
* you can add urls only once. But you can add other things, e.g. images, XML...
*
* @param mimeData the QMimeData instance used to drag or copy this URL
* @param metaData KIO metadata shipped in the mime data, which is used for instance to
* set a correct HTTP referrer (some websites require it for downloading e.g. an image)
* @param flags set NoTextExport to prevent setting plain/text data into @p mimeData
*
* @deprecated use QMimeData::setUrls, followed by KUrlMimeData::setMetaData if you have metadata.
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED void populateMimeData( QMimeData* mimeData,
const KUrl::MetaDataMap& metaData = MetaDataMap(),
MimeDataFlags flags = DefaultMimeDataFlags ) const;
#endif
/**
* Adds URLs into the given QMimeData.
*
* This should add both the KDE-style URLs (eg: desktop:/foo) and
* the "most local" version of the URLs (eg:
* file:///home/jbloggs/Desktop/foo) to the mimedata.
*
* This method should be called on the KDE-style URLs.
*
* @code
* QMimeData* mimeData = new QMimeData();
*
* KUrl::List kdeUrls;
* kdeUrls << "desktop:/foo";
* kdeUrls << "desktop:/bar";
*
* KUrl::List normalUrls;
* normalUrls << "file:///home/jbloggs/Desktop/foo";
* normalUrls << "file:///home/jbloggs/Desktop/bar";
*
* kdeUrls.populateMimeData(normalUrls, mimeData);
* @endcode
*
* @param mostLocalUrls the "most local" urls
* @param mimeData the mime data object to populate
* @param metaData KIO metadata shipped in the mime data, which is
* used for instance to set a correct HTTP referrer
* (some websites require it for downloading e.g. an
* image)
* @param flags set NoTextExport to prevent setting plain/text
* data into @p mimeData.
* @since 4.2
* @deprecated use KUrlMimeData::setUrls, followed by KUrlMimeData::setMetaData if you have metadata.
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED void populateMimeData(const KUrl::List& mostLocalUrls,
QMimeData* mimeData,
const KUrl::MetaDataMap& metaData = MetaDataMap(),
MimeDataFlags flags = DefaultMimeDataFlags) const;
#endif
/**
* Return true if @p mimeData contains URI data
* @deprecated use QMimeData::hasUrls
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED static bool canDecode( const QMimeData *mimeData );
#endif
/**
* Return the list of mimeTypes that can be decoded by fromMimeData
* @deprecated use KUrlMimeData::mimeDataTypes
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED static QStringList mimeDataTypes();
#endif
/**
* Flags to be used in fromMimeData.
* @since 4.2.3
* @deprecated use KUrlMimeData
*/
enum DecodeOptions {
/**
* When the mimedata contains both KDE-style URLs (eg: desktop:/foo) and
* the "most local" version of the URLs (eg: file:///home/dfaure/Desktop/foo),
* decode it as local urls. Useful in paste/drop operations that end up calling KIO,
* so that urls from other users work as well.
*/
PreferLocalUrls,
/**
* When the mimedata contains both KDE-style URLs (eg: desktop:/foo) and
* the "most local" version of the URLs (eg: file:///home/dfaure/Desktop/foo),
* decode it as the KDE-style URL. Useful in DnD code e.g. when moving icons,
* and the kde-style url is used as identifier for the icons.
*/
PreferKdeUrls
};
/**
* Extract a list of KUrls from the contents of @p mimeData.
* Decoding will fail if @p mimeData does not contain any URLs, or if at
* least one extracted URL is not valid.
* @param mimeData the mime data to extract from; cannot be 0
* @param decodeOptions options for decoding
* @param metaData optional pointer to a map holding the metadata
* @return the list of urls
* @since 4.2.3
* @deprecated use KUrlMimeData::urlsFromMimeData
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED static KUrl::List fromMimeData( const QMimeData *mimeData,
DecodeOptions decodeOptions = PreferKdeUrls,
KUrl::MetaDataMap* metaData = 0 );
#endif
};
/**
* Constructs an empty URL.
*/
KUrl();
/**
* Destructs the KUrl object.
*/
~KUrl();
/**
* Usual constructor, to construct from a string.
* @param urlOrPath An encoded URL or a path.
*/
KUrl( const QString& urlOrPath );
/**
* Constructor taking a char * @p urlOrPath, which is an _encoded_ representation
* of the URL, exactly like the usual constructor. This is useful when
* the URL, in its encoded form, is strictly ascii.
* @param urlOrPath An encoded URL, or a path.
*/
explicit KUrl( const char * urlOrPath );
/**
* Constructor taking a QByteArray @p urlOrPath, which is an _encoded_ representation
* of the URL, exactly like the usual constructor. This is useful when
* the URL, in its encoded form, is strictly ascii.
* @param urlOrPath An encoded URL, or a path.
*/
explicit KUrl( const QByteArray& urlOrPath );
/**
* Copy constructor.
* @param u the KUrl to copy
*/
KUrl( const KUrl& u );
/**
* Converts from a QUrl.
* @param u the QUrl
*/
KUrl( const QUrl &u ); //krazy:exclude=qclasses
/**
* Constructor allowing relative URLs.
*
* @param _baseurl The base url.
* @param _rel_url A relative or absolute URL.
* If this is an absolute URL then @p _baseurl will be ignored.
* If this is a relative URL it will be combined with @p _baseurl.
* Note that _rel_url should be encoded too, in any case.
* So do NOT pass a path here (use setPath or addPath instead).
*/
KUrl( const KUrl& _baseurl, const QString& _rel_url );
/**
* Returns the protocol for the URL (i.e., file, http, etc.), lowercased.
* @see QUrl::scheme
*/
QString protocol() const;
/**
* Sets the protocol for the URL (i.e., file, http, etc.)
* @param proto the new protocol of the URL (without colon)
*/
void setProtocol( const QString& proto );
/**
* Returns the decoded user name (login, user id, ...) included in the URL.
* @return the user name or QString() if there is no user name
*/
QString user() const;
/**
* Sets the user name (login, user id, ...) included in the URL.
*
* Special characters in the user name will appear encoded in the URL.
* @param user the name of the user or QString() to remove the user
*/
void setUser( const QString& user );
/**
* Test to see if this URL has a user name included in it.
* @return true if the URL has an non-empty user name
*/
bool hasUser() const;
/**
* Returns the decoded password (corresponding to user()) included in the URL.
* @return the password or QString() if it does not exist
**/
QString pass() const;
/**
* Sets the password (corresponding to user()) included in the URL.
*
* Special characters in the password will appear encoded in the URL.
* Note that a password can only appear in a URL string if you also set
* a user.
* @param pass the password to set or QString() to remove the password
* @see setUser
* @see hasUser
**/
void setPass( const QString& pass );
/**
* Test to see if this URL has a password included in it.
* @return true if there is a non-empty password set
**/
bool hasPass() const;
/**
* Test to see if this URL has a hostname included in it.
* @return true if the URL has a host
**/
bool hasHost() const;
/**
* @param trailing use to add or remove a trailing slash to/from the path. see adjustPath
* @return The current decoded path. This does not include the query. Can
* be QString() if no path is set.
*/
QString path( AdjustPathOption trailing = LeaveTrailingSlash ) const;
/**
* @param trailing use to add or remove a trailing slash to/from the local path. see adjustPath
* @return The current local path. Can
* be QString() if no path is set.
*/
QString toLocalFile( AdjustPathOption trailing = LeaveTrailingSlash ) const;
/// \reimp so that KUrl u; u.setPath(path); implies "file" protocol.
void setPath( const QString& path );
/**
* Test to see if this URL has a path is included in it.
* @return true if there is a path
**/
bool hasPath() const;
/**
* Options to be used in cleanPath
*/
enum CleanPathOption
{
/**
* if set, occurrences of consecutive directory separators
* (e.g. /foo//bar) are cleaned up as well. (set by default)
*/
SimplifyDirSeparators = 0x00,
/**
* The opposite of SimplifyDirSeparators.
*/
KeepDirSeparators = 0x01
};
Q_DECLARE_FLAGS(CleanPathOptions,CleanPathOption)
/**
* Resolves "." and ".." components in path.
* Some servers seem not to like the removal of extra '/'
* even though it is against the specification in RFC 2396.
*
* @param options use KeepDirSeparators if you don't want to remove consecutive
* occurrences of directory separator
*/
void cleanPath(const CleanPathOption& options = SimplifyDirSeparators);
/**
* Add or remove a trailing slash to/from the path.
*
* If the URL has no path, then no '/' is added
* anyway. And on the other side: If the path is "/", then this
* character won't be stripped. Reason: "ftp://weis\@host" means something
* completely different than "ftp://weis\@host/". So adding or stripping
* the '/' would really alter the URL, while "ftp://host/path" and
* "ftp://host/path/" mean the same directory.
*
* @param trailing RemoveTrailingSlash strips any trailing '/' and
* AddTrailingSlash adds a trailing '/' if there is none yet
*/
void adjustPath(AdjustPathOption trailing);
/**
* This is useful for HTTP. It looks first for '?' and decodes then.
* The encoded path is the concatenation of the current path and the query.
* @param _txt the new path and query.
*/
void setEncodedPathAndQuery( const QString& _txt );
#if 0
/**
* Sets the (already encoded) path
* @param _txt the new path
* @see QTextCodec::mibEnum()
*/
void setEncodedPath(const QString& _txt );
#endif
/**
* Option to be used in encodedPathAndQuery
**/
enum EncodedPathAndQueryOption
{
/**
* Permit empty path (default)
*/
PermitEmptyPath=0x00,
/**
* If set to true then an empty path is substituted by "/"
* (this is the opposite of PermitEmptyPath)
*/
AvoidEmptyPath=0x01
};
Q_DECLARE_FLAGS( EncodedPathAndQueryOptions, EncodedPathAndQueryOption)
/**
* Returns the encoded path and the query.
*
* @param trailing add or remove a trailing '/', see adjustPath
* @param options a set of flags from EncodedPathAndQueryOption
* @return The concatenation of the encoded path , '?' and the encoded query.
*
*/
QString encodedPathAndQuery( AdjustPathOption trailing = LeaveTrailingSlash, const EncodedPathAndQueryOptions &options = PermitEmptyPath ) const;
/**
* @param query This is considered to be encoded. This has a good reason:
* The query may contain the 0 character.
*
* The query should start with a '?'. If it doesn't '?' is prepended.
*/
void setQuery( const QString& query );
/**
* Returns the query of the URL.
* The query may contain the 0 character.
* If a query is present it always starts with a '?'.
* A single '?' means an empty query.
* An empty string means no query.
* @return The encoded query, or QString() if there is none.
*/
QString query() const;
/**
* Returns the reference (or "fragment") of the URL.
* The reference is @em never decoded automatically.
* @return the undecoded reference, or QString() if there is none
*/
QString ref() const;
/**
* Sets the reference/fragment part (everything after '#').
* If you have an encoded fragment already (as a QByteArray), you can call setFragment directly.
* @param fragment the unencoded reference (or QString() to remove it).
*/
void setRef( const QString& fragment );
/**
* Checks whether the URL has a reference/fragment part.
* @return true if the URL has a reference part. In a URL like
* http://www.kde.org/kdebase.tar#tar:/README it would
* return true, too.
*/
bool hasRef() const;
/**
* Returns the HTML reference (the part of the URL after "#").
* @return The HTML-style reference.
* @see split
* @see hasSubUrl
* @see encodedHtmlRef
*/
QString htmlRef() const;
/**
* Returns the HTML reference (the part of the URL after "#") in
* encoded form.
* @return The HTML-style reference in its original form.
*/
QString encodedHtmlRef() const;
/**
* Sets the HTML-style reference.
*
* @param _ref The new reference. This is considered to be @em not encoded in
* contrast to setRef(). Use QString() to remove it.
* @see htmlRef()
*/
void setHTMLRef( const QString& _ref );
/**
* Checks whether there is a HTML reference.
* @return true if the URL has an HTML-style reference.
* @see htmlRef()
*/
bool hasHTMLRef() const;
/**
* Checks whether the file is local.
* @return true if the file is a plain local file (i.e. uses the file protocol
* and no hostname, or the local hostname).
* When isLocalFile returns true, you can use toLocalFile to read the file contents.
* Otherwise you need to use KIO (e.g. KIO::get).
*/
bool isLocalFile() const;
/**
* Adds encoding information to url by adding a "charset" parameter. If there
* is already a charset parameter, it will be replaced.
* @param encoding the encoding to add or QString() to remove the
* encoding.
*/
void setFileEncoding(const QString &encoding);
/**
* Returns encoding information from url, the content of the "charset"
* parameter.
* @return An encoding suitable for QTextCodec::codecForName()
* or QString() if not encoding was specified.
*/
QString fileEncoding() const;
/**
* Checks whether the URL has any sub URLs. See split()
* for examples for sub URLs.
* @return true if the file has at least one sub URL.
* @see split
*/
bool hasSubUrl() const;
/**
* Adds to the current path.
* Assumes that the current path is a directory. @p txt is appended to the
* current path. The function adds '/' if needed while concatenating.
* This means it does not matter whether the current path has a trailing
* '/' or not. If there is none, it becomes appended. If @p txt
* has a leading '/' then this one is stripped.
*
* @param txt The text to add. It is considered to be decoded.
*/
void addPath( const QString& txt );
/**
* Options for queryItems. Currently, only one option is
* defined:
*
* @param CaseInsensitiveKeys normalize query keys to lowercase.
**/
enum QueryItemsOption { CaseInsensitiveKeys = 1 };
Q_DECLARE_FLAGS(QueryItemsOptions,QueryItemsOption)
/**
* Returns the list of query items as a map mapping keys to values.
*
* This does the same as QUrl::queryItems(), except that it
* decodes "+" into " " in the value, supports CaseInsensitiveKeys,
* and returns a different data type.
*
* @param options any of QueryItemsOption <em>or</em>ed together.
*
* @return the map of query items or the empty map if the url has no
* query items.
*/
QMap< QString, QString > queryItems( const QueryItemsOptions& options = 0 ) const;
// #### TODO port the above queryItems to look more like QUrl's
//using QUrl::queryItems; // temporary
/**
* Returns the value of a certain query item.
*
* This does the same as QUrl::queryItemValue(), except that it
* decodes "+" into " " in the value.
*
* @param item Item whose value we want
*
* @return the value of the given query item name or QString() if the
* specified item does not exist.
*/
QString queryItem(const QString &item) const;
/**
* Add an additional query item.
* To replace an existing query item, the item should first be
* removed with removeQueryItem()
*
* @param _item Name of item to add
* @param _value Value of item to add
*/
void addQueryItem( const QString& _item, const QString& _value );
/**
* Sets the filename of the path.
* In comparison to addPath() this function does not assume that the current
* path is a directory. This is only assumed if the current path ends with '/'.
*
* Any reference is reset.
*
* @param _txt The filename to be set. It is considered to be decoded. If the
* current path ends with '/' then @p _txt int just appended, otherwise
* all text behind the last '/' in the current path is erased and
* @p _txt is appended then. It does not matter whether @p _txt starts
* with '/' or not.
*/
void setFileName( const QString&_txt );
/**
* option to be used in fileName and directory
*/
enum DirectoryOption
{
/**
* This tells whether a trailing '/' should be ignored.
*
* If the flag is not set, for both <tt>file:///hallo/torben/</tt> and <tt>file:///hallo/torben</tt>
* the fileName is "torben" and the path is "hallo"
*
* If the flag is set, then everything behind the last '/'is considered to be the filename.
* So "hallo/torben" will be the path and the filename will be empty.
*/
ObeyTrailingSlash = 0x02,
/**
* tells whether the returned result should end with '/' or not.
* If the flag is set, '/' is added to the end of the path
*
* If the path is empty or just "/" then this flag has no effect.
*
* This option should only be used in directory(), it has no effect in fileName()
*/
AppendTrailingSlash = 0x04,
/**
* Opposite of ObeyTrailingSlash (default)
* fileName("file:/foo/") and fileName("file:/foo") is "foo" in both cases.
*/
IgnoreTrailingSlash = 0x01
};
Q_DECLARE_FLAGS(DirectoryOptions,DirectoryOption)
/**
* Returns the filename of the path.
* @param options a set of DirectoryOption flags. (StripTrailingSlashFromResult has no effect)
* @return The filename of the current path. The returned string is decoded. Null
* if there is no file (and thus no path).
*/
QString fileName( const DirectoryOptions& options = IgnoreTrailingSlash ) const;
/**
* Returns the directory of the path.
* @param options a set of DirectoryOption flags
* @return The directory part of the current path. Everything between the last and the second last '/'
* is returned. For example <tt>file:///hallo/torben/</tt> would return "/hallo/torben/" while
* <tt>file:///hallo/torben</tt> would return "hallo/". The returned string is decoded.
* QString() is returned when there is no path.
*/
QString directory( const DirectoryOptions& options = IgnoreTrailingSlash ) const;
/**
* Set the directory to @p dir, leaving the filename empty.
*/
void setDirectory(const QString &dir);
/**
* Changes the directory by descending into the given directory.
* It is assumed the current URL represents a directory.
* If @p dir starts with a "/" the
* current URL will be "protocol://host/dir" otherwise @p _dir will
* be appended to the path. @p _dir can be ".."
* This function won't strip protocols. That means that when you are in
* file:///dir/dir2/my.tgz#tar:/ and you do cd("..") you will
* still be in file:///dir/dir2/my.tgz#tar:/
*
* @param _dir the directory to change to
* @return true if successful
*/
bool cd( const QString& _dir );
/**
* Returns the URL as string, with all escape sequences intact,
* encoded in a given charset.
* This is used in particular for encoding URLs in UTF-8 before using them
* in a drag and drop operation.
* Please note that the string returned by url() will include
* the password of the URL. If you want to show the URL to the
* user, use prettyUrl().
*
* @param trailing use to add or remove a trailing slash to/from the path. See adjustPath
* @return The complete URL, with all escape sequences intact, encoded
* in a given charset.
* @see prettyUrl()
*/
QString url( AdjustPathOption trailing = LeaveTrailingSlash ) const;
/**
* Returns the URL as string in human-friendly format.
* Example:
* \code
* http://localhost:8080/test.cgi?test=hello world&name=fred
* \endcode
* @param trailing use to add or remove a trailing slash to/from the path. see adjustPath.
*
* @return A human readable URL, with no non-necessary encodings/escaped
* characters. Password will not be shown.
* @see url()
*/
QString prettyUrl( AdjustPathOption trailing = LeaveTrailingSlash ) const;
/**
* Return the URL as a string, which will be either the URL (as prettyUrl
* would return) or, when the URL is a local file without query or ref,
* the path.
* Use this method, to display URLs to the user.
* You can give the result of pathOrUrl back to the KUrl constructor, it accepts
* both paths and urls.
*
* @return the new KUrl
*/
QString pathOrUrl() const;
/**
* Overload with @p trailing parameter
* @param trailing use to add or remove a trailing slash to/from the path. see adjustPath.
* @since 4.2
*/
QString pathOrUrl(AdjustPathOption trailing) const; // KDE5: merge with above. Rename to toUrlOrLocalFile?
/**
* Returns the URL as a string, using the standard conventions for mime data
* (drag-n-drop or copy-n-paste).
* Internally used by KUrl::List::fromMimeData, which is probably what you want to use instead.
* @deprecated use QMimeData::setUrls directly, or KUrlMimeData::setUrls
*/
QString toMimeDataString() const;
/**
* This function is useful to implement the "Up" button in a file manager for example.
* cd() never strips a sub-protocol. That means that if you are in
* file:///home/x.tgz#gzip:/#tar:/ and hit the up button you expect to see
* file:///home. The algorithm tries to go up on the right-most URL. If that is not
* possible it strips the right most URL. It continues stripping URLs.
* @return a URL that is a level higher
*/
KUrl upUrl( ) const;
KUrl& operator=( const KUrl& _u );
// Define those, since the constructors are explicit
KUrl& operator=( const char * _url ) { *this = KUrl(_url); return *this; }
KUrl& operator=( const QByteArray& _url ) { *this = KUrl(_url); return *this; }
KUrl& operator=( const QString& _url ) { *this = KUrl(_url); return *this; }
bool operator==( const KUrl& _u ) const;
bool operator==( const QString& _u ) const;
bool operator!=( const KUrl& _u ) const { return !( *this == _u ); }
bool operator!=( const QString& _u ) const { return !( *this == _u ); }
/**
* Converts this KUrl to a QVariant, this allows to use KUrl
* in QVariant() constructor
*/
operator QVariant() const;
/**
* The same as equals(), just with a less obvious name.
* Compares this url with @p u.
* @param u the URL to compare this one with.
* @param ignore_trailing set to true to ignore trailing '/' characters.
* @return True if both urls are the same. If at least one of the urls is invalid,
* false is returned.
* @see operator==. This function should be used if you want to
* ignore trailing '/' characters.
* @deprecated Use equals() instead.
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED bool cmp( const KUrl &u, bool ignore_trailing = false ) const;
#endif
/**
* Flags to be used in URL comparison functions like equals, or urlcmp
*/
enum EqualsOption
{
/**
* ignore trailing '/' characters. The paths "dir" and "dir/" are treated the same.
* Note however, that by default, the paths "" and "/" are not the same
* (For instance ftp://user@host redirects to ftp://user@host/home/user (on a linux server),
* while ftp://user@host/ is the root dir).
* This is also why path(RemoveTrailingSlash) for "/" returns "/" and not "".
*
* When dealing with web pages however, you should also set AllowEmptyPath so that
* no path and "/" are considered equal.
*/
CompareWithoutTrailingSlash = 0x01,
/**
* disables comparison of HTML-style references.
*/
CompareWithoutFragment = 0x02,
/**
* Treat a URL with no path as equal to a URL with a path of "/",
* when CompareWithoutTrailingSlash is set.
* Example:
* KUrl::urlcmp("http://www.kde.org", "http://www.kde.org/", KUrl::CompareWithoutTrailingSlash | KUrl::AllowEmptyPath)
* returns true.
* This option is ignored if CompareWithoutTrailingSlash isn't set.
* @since 4.5
*/
AllowEmptyPath = 0x04
};
Q_DECLARE_FLAGS(EqualsOptions,EqualsOption)
/**
* Compares this url with @p u.
* @param u the URL to compare this one with.
* @param options a set of EqualsOption flags
* @return True if both urls are the same. If at least one of the urls is invalid,
* false is returned.
* @see operator==. This function should be used if you want to
* set additional options, like ignoring trailing '/' characters.
*/
bool equals( const KUrl &u, const EqualsOptions& options=0 ) const;
/**
* Checks whether the given URL is parent of this URL.
* For instance, ftp://host/dir/ is a parent of ftp://host/dir/subdir/subsubdir/.
* @return true if this url is a parent of @p u (or the same URL as @p u)
*
*/
bool isParentOf( const KUrl& u ) const;
// (this overload of the QUrl method allows to use the implicit KUrl constructors)
// but also the equality test
/**
* Splits nested URLs like file:///home/weis/kde.tgz#gzip:/#tar:/kdebase
* A URL like http://www.kde.org#tar:/kde/README.hml#ref1 will be split in
* http://www.kde.org and tar:/kde/README.html#ref1.
* That means in turn that "#ref1" is an HTML-style reference and not a new sub URL.
* Since HTML-style references mark
* a certain position in a document this reference is appended to every URL.
* The idea behind this is that browsers, for example, only look at the first URL while
* the rest is not of interest to them.
*
*
* @param _url The URL that has to be split.
* @return An empty list on error or the list of split URLs.
* @see hasSubUrl
*/
static List split( const QString& _url );
/**
* Splits nested URLs like file:///home/weis/kde.tgz#gzip:/#tar:/kdebase
* A URL like http://www.kde.org#tar:/kde/README.hml#ref1 will be split in
* http://www.kde.org and tar:/kde/README.html#ref1.
* That means in turn that "#ref1" is an HTML-style reference and not a new sub URL.
* Since HTML-style references mark
* a certain position in a document this reference is appended to every URL.
* The idea behind this is that browsers, for example, only look at the first URL while
* the rest is not of interest to them.
*
* @return An empty list on error or the list of split URLs.
*
* @param _url The URL that has to be split.
* @see hasSubUrl
*/
static List split( const KUrl& _url );
/**
* Reverses split(). Only the first URL may have a reference. This reference
* is considered to be HTML-like and is appended at the end of the resulting
* joined URL.
* @param _list the list to join
* @return the joined URL
*/
static KUrl join( const List& _list );
/**
* Creates a KUrl object from a QString representing an absolute path.
* KUrl url( somePath ) is almost the same, but this method is more explicit,
* avoids the path-or-url detection in the KUrl constructor, and parses
* "abc:def" as a filename, not as URL.
*
* @param text the path
* @return the new KUrl
*/
static KUrl fromPath( const QString& text );
/**
* \deprecated
* Since KDE4 you can pass both urls and paths to the KUrl constructors.
* Use KUrl(text) instead.
*/
#ifndef KDE_NO_DEPRECATED
static KCOREADDONS_DEPRECATED KUrl fromPathOrUrl( const QString& text );
#endif
/**
* Adds URL data into the given QMimeData.
*
* By default, populateMimeData also exports the URL as plain text, for e.g. dropping
* onto a text editor.
* But in some cases this might not be wanted, e.g. if adding other mime data
* which provides better plain text data.
*
* WARNING: do not call this method multiple times, use KUrl::List::populateMimeData instead.
*
* @param mimeData the QMimeData instance used to drag or copy this URL
* @param metaData KIO metadata shipped in the mime data, which is used for instance to
* set a correct HTTP referrer (some websites require it for downloading e.g. an image)
* @param flags set NoTextExport to prevent setting plain/text data into @p mimeData
*
* @deprecated use QMimeData::setUrls(QList<QUrl>() << url),
* followed by KUrlMimeData::setMetaData if you have metadata.
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED void populateMimeData( QMimeData* mimeData,
const MetaDataMap& metaData = MetaDataMap(),
MimeDataFlags flags = DefaultMimeDataFlags ) const;
#endif
/**
* Convert unicoded string to local encoding and use %-style
* encoding for all common delimiters / non-ascii characters.
* @param str String to encode (can be QString()).
* @return the encoded string
*
* @deprecated use QUrl::toPercentEncoding instead, but note that it
* returns a QByteArray and not a QString. Which makes sense since
* everything is 7 bit (ascii) after being percent-encoded.
*/
#ifndef KDE_NO_DEPRECATED
static KCOREADDONS_DEPRECATED QString encode_string(const QString &str) {
return QString::fromLatin1( QUrl::toPercentEncoding( str ).constData() ); //krazy:exclude=qclasses
}
#endif
/**
* Convert unicoded string to local encoding and use %-style
* encoding for all common delimiters / non-ascii characters
* as well as the slash '/'.
* @param str String to encode
*
* @deprecated use QUrl::toPercentEncoding(str,"/") instead, but note that it
* returns a QByteArray and not a QString. Which makes sense since
* everything is 7 bit (ascii) after being percent-encoded.
*
*/
#ifndef KDE_NO_DEPRECATED
static KCOREADDONS_DEPRECATED QString encode_string_no_slash(const QString &str) {
return QString::fromLatin1( QUrl::toPercentEncoding( str, "/" ).constData() ); //krazy:exclude=qclasses
}
#endif
/**
* Decode %-style encoding and convert from local encoding to unicode.
* Reverse of encode_string()
* @param str String to decode (can be QString()).
*
* @deprecated use QUrl::fromPercentEncoding(encodedURL) instead, but
* note that it takes a QByteArray and not a QString. Which makes sense since
* everything is 7 bit (ascii) when being percent-encoded.
*
*/
#ifndef KDE_NO_DEPRECATED
static KCOREADDONS_DEPRECATED QString decode_string(const QString &str) {
return QUrl::fromPercentEncoding( str.toLatin1() ); //krazy:exclude=qclasses
}
#endif
/**
* Convenience function.
*
* Returns whether '_url' is likely to be a "relative" URL instead of
* an "absolute" URL.
*
* This is mostly meant for KUrl(url, relativeUrl).
*
* If you are looking for the notion of "relative path" (foo) vs "absolute path" (/foo),
* use QUrl::isRelative() instead.
* Indeed, isRelativeUrl() returns true for the string "/foo" since it doesn't contain a protocol,
* while KUrl("/foo").isRelative() is false since the KUrl constructor turns it into file:///foo.
* The two methods basically test the same thing, but this one takes a string (which is faster)
* while the class method requires a QUrl/KUrl which gives a more expected result, given
* the "magic" in the KUrl constructor.
*
* @param _url URL to examine
* @return true when the URL is likely to be "relative", false otherwise.
*/
static bool isRelativeUrl(const QString &_url);
/**
* Convenience function
*
* Returns a "relative URL" based on @p base_url that points to @p url.
*
* If no "relative URL" can be created, e.g. because the protocol
* and/or hostname differ between @p base_url and @p url an absolute
* URL is returned.
* Note that if @p base_url represents a directory, it should contain
* a trailing slash.
* @param base_url the URL to derive from
* @param url new URL
* @see adjustPath()
*/
static QString relativeUrl(const KUrl &base_url, const KUrl &url);
/**
* Convenience function
*
* Returns a relative path based on @p base_dir that points to @p path.
* @param base_dir the base directory to derive from
* @param path the new target directory
* @param isParent A pointer to a boolean which, if provided, will be set to reflect
* whether @p path has @p base_dir is a parent dir.
*/
static QString relativePath(const QString &base_dir, const QString &path, bool *isParent=0);
private:
void _setQuery( const QString& query );
void _setEncodedUrl(const QByteArray& url);
QString toString() const; // forbidden, use url(), prettyUrl(), or pathOrUrl() instead.
operator QString() const; // forbidden, use url(), prettyUrl(), or pathOrUrl() instead.
private:
KUrlPrivate* const d; // Don't ever use this, it would break clear() (which is in QUrl)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(KUrl::EncodedPathAndQueryOptions)
Q_DECLARE_OPERATORS_FOR_FLAGS(KUrl::CleanPathOptions)
Q_DECLARE_OPERATORS_FOR_FLAGS(KUrl::QueryItemsOptions)
Q_DECLARE_OPERATORS_FOR_FLAGS(KUrl::EqualsOptions)
Q_DECLARE_OPERATORS_FOR_FLAGS(KUrl::DirectoryOptions)
Q_DECLARE_METATYPE(KUrl)
Q_DECLARE_METATYPE(KUrl::List)
Q_DECLARE_METATYPE(QList<KUrl>)
+#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
+Q_DECLARE_METATYPE(QList<QUrl>)
+#endif
/**
* \relates KUrl
* Compares URLs. They are parsed, split and compared.
* Two malformed URLs with the same string representation
* are nevertheless considered to be unequal.
* That means no malformed URL equals anything else.
* @deprecated use KUrl(_url1).equals(KUrl(_url2)) instead.
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED_EXPORT bool urlcmp( const QString& _url1, const QString& _url2 ); // KDE5: remove, KUrl::equals is better API
#endif
/**
* \relates KUrl
* Compares URLs. They are parsed, split and compared.
* Two malformed URLs with the same string representation
* are nevertheless considered to be unequal.
* That means no malformed URL equals anything else.
*
* @param _url1 A reference URL
* @param _url2 A URL that will be compared with the reference URL
* @param options a set of KUrl::EqualsOption flags
* @deprecated use KUrl(_url1).equals(KUrl(_url2), options) instead.
*/
#ifndef KDE_NO_DEPRECATED
KCOREADDONS_DEPRECATED_EXPORT bool urlcmp( const QString& _url1, const QString& _url2, const KUrl::EqualsOptions& options ); // KDE5: remove, KUrl::equals is better API
#endif
KCOREADDONS_EXPORT uint qHash(const KUrl& kurl);
#endif

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 1, 9:23 AM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
10075791
Default Alt Text
(77 KB)

Event Timeline